sidebar.js 30 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087
  1. import { Cookies } from 'meteor/ostrio:cookies';
  2. const cookies = new Cookies();
  3. Sidebar = null;
  4. const defaultView = 'home';
  5. const MCB = '.materialCheckBox';
  6. const CKCLS = 'is-checked';
  7. const viewTitles = {
  8. filter: 'filter-cards',
  9. search: 'search-cards',
  10. multiselection: 'multi-selection',
  11. customFields: 'custom-fields',
  12. archives: 'archives',
  13. };
  14. BlazeComponent.extendComponent({
  15. mixins() {
  16. return [Mixins.InfiniteScrolling, Mixins.PerfectScrollbar];
  17. },
  18. onCreated() {
  19. this._isOpen = new ReactiveVar(false);
  20. this._view = new ReactiveVar(defaultView);
  21. Sidebar = this;
  22. },
  23. onDestroyed() {
  24. Sidebar = null;
  25. },
  26. isOpen() {
  27. return this._isOpen.get();
  28. },
  29. open() {
  30. if (!this._isOpen.get()) {
  31. this._isOpen.set(true);
  32. EscapeActions.executeUpTo('detailsPane');
  33. }
  34. },
  35. hide() {
  36. if (this._isOpen.get()) {
  37. this._isOpen.set(false);
  38. }
  39. },
  40. toggle() {
  41. this._isOpen.set(!this._isOpen.get());
  42. },
  43. calculateNextPeak() {
  44. const sidebarElement = this.find('.js-board-sidebar-content');
  45. if (sidebarElement) {
  46. const altitude = sidebarElement.scrollHeight;
  47. this.callFirstWith(this, 'setNextPeak', altitude);
  48. }
  49. },
  50. reachNextPeak() {
  51. const activitiesComponent = this.childComponents('activities')[0];
  52. activitiesComponent.loadNextPage();
  53. },
  54. isTongueHidden() {
  55. return this.isOpen() && this.getView() !== defaultView;
  56. },
  57. scrollTop() {
  58. this.$('.js-board-sidebar-content').scrollTop(0);
  59. },
  60. getView() {
  61. return this._view.get();
  62. },
  63. setView(view) {
  64. view = _.isString(view) ? view : defaultView;
  65. if (this._view.get() !== view) {
  66. this._view.set(view);
  67. this.scrollTop();
  68. EscapeActions.executeUpTo('detailsPane');
  69. }
  70. this.open();
  71. },
  72. isDefaultView() {
  73. return this.getView() === defaultView;
  74. },
  75. getViewTemplate() {
  76. return `${this.getView()}Sidebar`;
  77. },
  78. getViewTitle() {
  79. return TAPi18n.__(viewTitles[this.getView()]);
  80. },
  81. showTongueTitle() {
  82. if (this.isOpen()) return `${TAPi18n.__('sidebar-close')}`;
  83. else return `${TAPi18n.__('sidebar-open')}`;
  84. },
  85. events() {
  86. return [
  87. {
  88. 'click .js-hide-sidebar': this.hide,
  89. 'click .js-toggle-sidebar': this.toggle,
  90. 'click .js-back-home': this.setView,
  91. 'click .js-toggle-minicard-label-text'() {
  92. currentUser = Meteor.user();
  93. if (currentUser) {
  94. Meteor.call('toggleMinicardLabelText');
  95. } else if (cookies.has('hiddenMinicardLabelText')) {
  96. cookies.remove('hiddenMinicardLabelText');
  97. } else {
  98. cookies.set('hiddenMinicardLabelText', 'true');
  99. }
  100. },
  101. 'click .js-shortcuts'() {
  102. FlowRouter.go('shortcuts');
  103. },
  104. },
  105. ];
  106. },
  107. }).register('sidebar');
  108. Blaze.registerHelper('Sidebar', () => Sidebar);
  109. Template.homeSidebar.helpers({
  110. hiddenMinicardLabelText() {
  111. currentUser = Meteor.user();
  112. if (currentUser) {
  113. return (currentUser.profile || {}).hiddenMinicardLabelText;
  114. } else if (cookies.has('hiddenMinicardLabelText')) {
  115. return true;
  116. } else {
  117. return false;
  118. }
  119. },
  120. });
  121. EscapeActions.register(
  122. 'sidebarView',
  123. () => {
  124. Sidebar.setView(defaultView);
  125. },
  126. () => {
  127. return Sidebar && Sidebar.getView() !== defaultView;
  128. },
  129. );
  130. Template.memberPopup.helpers({
  131. user() {
  132. return Users.findOne(this.userId);
  133. },
  134. memberType() {
  135. const type = Users.findOne(this.userId).isBoardAdmin() ? 'admin' : 'normal';
  136. if (type === 'normal') {
  137. const currentBoard = Boards.findOne(Session.get('currentBoard'));
  138. const commentOnly = currentBoard.hasCommentOnly(this.userId);
  139. const noComments = currentBoard.hasNoComments(this.userId);
  140. const worker = currentBoard.hasWorker(this.userId);
  141. if (commentOnly) {
  142. return TAPi18n.__('comment-only').toLowerCase();
  143. } else if (noComments) {
  144. return TAPi18n.__('no-comments').toLowerCase();
  145. } else if (worker) {
  146. return TAPi18n.__('worker').toLowerCase();
  147. } else {
  148. return TAPi18n.__(type).toLowerCase();
  149. }
  150. } else {
  151. return TAPi18n.__(type).toLowerCase();
  152. }
  153. },
  154. isInvited() {
  155. return Users.findOne(this.userId).isInvitedTo(Session.get('currentBoard'));
  156. },
  157. });
  158. Template.boardMenuPopup.events({
  159. 'click .js-rename-board': Popup.open('boardChangeTitle'),
  160. 'click .js-open-rules-view'() {
  161. Modal.openWide('rulesMain');
  162. Popup.close();
  163. },
  164. 'click .js-custom-fields'() {
  165. Sidebar.setView('customFields');
  166. Popup.close();
  167. },
  168. 'click .js-open-archives'() {
  169. Sidebar.setView('archives');
  170. Popup.close();
  171. },
  172. 'click .js-change-board-color': Popup.open('boardChangeColor'),
  173. 'click .js-change-language': Popup.open('changeLanguage'),
  174. 'click .js-archive-board ': Popup.afterConfirm('archiveBoard', function() {
  175. const currentBoard = Boards.findOne(Session.get('currentBoard'));
  176. currentBoard.archive();
  177. // XXX We should have some kind of notification on top of the page to
  178. // confirm that the board was successfully archived.
  179. FlowRouter.go('home');
  180. }),
  181. 'click .js-delete-board': Popup.afterConfirm('deleteBoard', function() {
  182. const currentBoard = Boards.findOne(Session.get('currentBoard'));
  183. Popup.close();
  184. Boards.remove(currentBoard._id);
  185. FlowRouter.go('home');
  186. }),
  187. 'click .js-outgoing-webhooks': Popup.open('outgoingWebhooks'),
  188. 'click .js-import-board': Popup.open('chooseBoardSource'),
  189. 'click .js-subtask-settings': Popup.open('boardSubtaskSettings'),
  190. 'click .js-card-settings': Popup.open('boardCardSettings'),
  191. });
  192. Template.boardMenuPopup.helpers({
  193. exportUrl() {
  194. const params = {
  195. boardId: Session.get('currentBoard'),
  196. };
  197. const queryParams = {
  198. authToken: Accounts._storedLoginToken(),
  199. };
  200. return FlowRouter.path('/api/boards/:boardId/export', params, queryParams);
  201. },
  202. exportFilename() {
  203. const boardId = Session.get('currentBoard');
  204. return `wekan-export-board-${boardId}.json`;
  205. },
  206. });
  207. Template.memberPopup.events({
  208. 'click .js-filter-member'() {
  209. Filter.members.toggle(this.userId);
  210. Popup.close();
  211. },
  212. 'click .js-change-role': Popup.open('changePermissions'),
  213. 'click .js-remove-member': Popup.afterConfirm('removeMember', function() {
  214. const boardId = Session.get('currentBoard');
  215. const memberId = this.userId;
  216. Cards.find({ boardId, members: memberId }).forEach(card => {
  217. card.unassignMember(memberId);
  218. });
  219. Boards.findOne(boardId).removeMember(memberId);
  220. Popup.close();
  221. }),
  222. 'click .js-leave-member': Popup.afterConfirm('leaveBoard', () => {
  223. const boardId = Session.get('currentBoard');
  224. Meteor.call('quitBoard', boardId, () => {
  225. Popup.close();
  226. FlowRouter.go('home');
  227. });
  228. }),
  229. });
  230. Template.removeMemberPopup.helpers({
  231. user() {
  232. return Users.findOne(this.userId);
  233. },
  234. board() {
  235. return Boards.findOne(Session.get('currentBoard'));
  236. },
  237. });
  238. Template.leaveBoardPopup.helpers({
  239. board() {
  240. return Boards.findOne(Session.get('currentBoard'));
  241. },
  242. });
  243. Template.membersWidget.helpers({
  244. isInvited() {
  245. const user = Meteor.user();
  246. return user && user.isInvitedTo(Session.get('currentBoard'));
  247. },
  248. isWorker() {
  249. const user = Meteor.user();
  250. if (user) {
  251. return Meteor.call(Boards.hasWorker(user.memberId));
  252. } else {
  253. return false;
  254. }
  255. },
  256. });
  257. Template.membersWidget.events({
  258. 'click .js-member': Popup.open('member'),
  259. 'click .js-open-board-menu': Popup.open('boardMenu'),
  260. 'click .js-manage-board-members': Popup.open('addMember'),
  261. 'click .js-import': Popup.open('boardImportBoard'),
  262. submit: this.onSubmit,
  263. 'click .js-import-board': Popup.open('chooseBoardSource'),
  264. 'click .js-open-archived-board'() {
  265. Modal.open('archivedBoards');
  266. },
  267. 'click .sandstorm-powerbox-request-identity'() {
  268. window.sandstormRequestIdentity();
  269. },
  270. 'click .js-member-invite-accept'() {
  271. const boardId = Session.get('currentBoard');
  272. Meteor.user().removeInvite(boardId);
  273. },
  274. 'click .js-member-invite-decline'() {
  275. const boardId = Session.get('currentBoard');
  276. Meteor.call('quitBoard', boardId, (err, ret) => {
  277. if (!err && ret) {
  278. Meteor.user().removeInvite(boardId);
  279. FlowRouter.go('home');
  280. }
  281. });
  282. },
  283. });
  284. BlazeComponent.extendComponent({
  285. boardId() {
  286. return Session.get('currentBoard') || Integrations.Const.GLOBAL_WEBHOOK_ID;
  287. },
  288. integrations() {
  289. const boardId = this.boardId();
  290. return Integrations.find({ boardId: `${boardId}` }).fetch();
  291. },
  292. types() {
  293. return Integrations.Const.WEBHOOK_TYPES;
  294. },
  295. integration(cond) {
  296. const boardId = this.boardId();
  297. const condition = { boardId, ...cond };
  298. for (const k in condition) {
  299. if (!condition[k]) delete condition[k];
  300. }
  301. return Integrations.findOne(condition);
  302. },
  303. onCreated() {
  304. this.disabled = new ReactiveVar(false);
  305. },
  306. events() {
  307. return [
  308. {
  309. 'click a.flex'(evt) {
  310. this.disabled.set(!this.disabled.get());
  311. $(evt.target).toggleClass(CKCLS, this.disabled.get());
  312. },
  313. submit(evt) {
  314. evt.preventDefault();
  315. const url = evt.target.url.value;
  316. const boardId = this.boardId();
  317. let id = null;
  318. let integration = null;
  319. const title = evt.target.title.value;
  320. const token = evt.target.token.value;
  321. const type = evt.target.type.value;
  322. const enabled = !this.disabled.get();
  323. let remove = false;
  324. const values = {
  325. url,
  326. type,
  327. token,
  328. title,
  329. enabled,
  330. };
  331. if (evt.target.id) {
  332. id = evt.target.id.value;
  333. integration = this.integration({ _id: id });
  334. remove = !url;
  335. } else if (url) {
  336. integration = this.integration({ url, token });
  337. }
  338. if (remove) {
  339. Integrations.remove(integration._id);
  340. } else if (integration && integration._id) {
  341. Integrations.update(integration._id, {
  342. $set: values,
  343. });
  344. } else if (url) {
  345. Integrations.insert({
  346. ...values,
  347. userId: Meteor.userId(),
  348. enabled: true,
  349. boardId,
  350. activities: ['all'],
  351. });
  352. }
  353. Popup.close();
  354. },
  355. },
  356. ];
  357. },
  358. }).register('outgoingWebhooksPopup');
  359. BlazeComponent.extendComponent({
  360. template() {
  361. return 'chooseBoardSource';
  362. },
  363. }).register('chooseBoardSourcePopup');
  364. Template.labelsWidget.events({
  365. 'click .js-label': Popup.open('editLabel'),
  366. 'click .js-add-label': Popup.open('createLabel'),
  367. });
  368. // Board members can assign people or labels by drag-dropping elements from the
  369. // sidebar to the cards on the board. In order to re-initialize the jquery-ui
  370. // plugin any time a draggable member or label is modified or removed we use a
  371. // autorun function and register a dependency on the both members and labels
  372. // fields of the current board document.
  373. function draggableMembersLabelsWidgets() {
  374. this.autorun(() => {
  375. const currentBoardId = Tracker.nonreactive(() => {
  376. return Session.get('currentBoard');
  377. });
  378. Boards.findOne(currentBoardId, {
  379. fields: {
  380. members: 1,
  381. labels: 1,
  382. },
  383. });
  384. Tracker.afterFlush(() => {
  385. const $draggables = this.$('.js-member,.js-label');
  386. $draggables.draggable({
  387. appendTo: 'body',
  388. helper: 'clone',
  389. revert: 'invalid',
  390. revertDuration: 150,
  391. snap: false,
  392. snapMode: 'both',
  393. start() {
  394. EscapeActions.executeUpTo('popup-back');
  395. },
  396. });
  397. function userIsMember() {
  398. return Meteor.user() && Meteor.user().isBoardMember();
  399. }
  400. this.autorun(() => {
  401. $draggables.draggable('option', 'disabled', !userIsMember());
  402. });
  403. });
  404. });
  405. }
  406. Template.membersWidget.onRendered(draggableMembersLabelsWidgets);
  407. Template.labelsWidget.onRendered(draggableMembersLabelsWidgets);
  408. BlazeComponent.extendComponent({
  409. backgroundColors() {
  410. return Boards.simpleSchema()._schema.color.allowedValues;
  411. },
  412. isSelected() {
  413. const currentBoard = Boards.findOne(Session.get('currentBoard'));
  414. return currentBoard.color === this.currentData().toString();
  415. },
  416. events() {
  417. return [
  418. {
  419. 'click .js-select-background'(evt) {
  420. const currentBoard = Boards.findOne(Session.get('currentBoard'));
  421. const newColor = this.currentData().toString();
  422. currentBoard.setColor(newColor);
  423. evt.preventDefault();
  424. },
  425. },
  426. ];
  427. },
  428. }).register('boardChangeColorPopup');
  429. BlazeComponent.extendComponent({
  430. onCreated() {
  431. this.currentBoard = Boards.findOne(Session.get('currentBoard'));
  432. },
  433. allowsSubtasks() {
  434. return this.currentBoard.allowsSubtasks;
  435. },
  436. allowsReceivedDate() {
  437. return this.currentBoard.allowsReceivedDate;
  438. },
  439. isBoardSelected() {
  440. return this.currentBoard.subtasksDefaultBoardId === this.currentData()._id;
  441. },
  442. isNullBoardSelected() {
  443. return (
  444. this.currentBoard.subtasksDefaultBoardId === null ||
  445. this.currentBoard.subtasksDefaultBoardId === undefined
  446. );
  447. },
  448. boards() {
  449. return Boards.find(
  450. {
  451. archived: false,
  452. 'members.userId': Meteor.userId(),
  453. },
  454. {
  455. sort: ['title'],
  456. },
  457. );
  458. },
  459. lists() {
  460. return Lists.find(
  461. {
  462. boardId: this.currentBoard._id,
  463. archived: false,
  464. },
  465. {
  466. sort: ['title'],
  467. },
  468. );
  469. },
  470. hasLists() {
  471. return this.lists().count() > 0;
  472. },
  473. isListSelected() {
  474. return this.currentBoard.subtasksDefaultBoardId === this.currentData()._id;
  475. },
  476. presentParentTask() {
  477. let result = this.currentBoard.presentParentTask;
  478. if (result === null || result === undefined) {
  479. result = 'no-parent';
  480. }
  481. return result;
  482. },
  483. events() {
  484. return [
  485. {
  486. 'click .js-field-has-subtasks'(evt) {
  487. evt.preventDefault();
  488. this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks;
  489. this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks);
  490. $(`.js-field-has-subtasks ${MCB}`).toggleClass(
  491. CKCLS,
  492. this.currentBoard.allowsSubtasks,
  493. );
  494. $('.js-field-has-subtasks').toggleClass(
  495. CKCLS,
  496. this.currentBoard.allowsSubtasks,
  497. );
  498. $('.js-field-deposit-board').prop(
  499. 'disabled',
  500. !this.currentBoard.allowsSubtasks,
  501. );
  502. },
  503. 'change .js-field-deposit-board'(evt) {
  504. let value = evt.target.value;
  505. if (value === 'null') {
  506. value = null;
  507. }
  508. this.currentBoard.setSubtasksDefaultBoardId(value);
  509. evt.preventDefault();
  510. },
  511. 'change .js-field-deposit-list'(evt) {
  512. this.currentBoard.setSubtasksDefaultListId(evt.target.value);
  513. evt.preventDefault();
  514. },
  515. 'click .js-field-show-parent-in-minicard'(evt) {
  516. const value =
  517. evt.target.id ||
  518. $(evt.target).parent()[0].id ||
  519. $(evt.target)
  520. .parent()[0]
  521. .parent()[0].id;
  522. const options = [
  523. 'prefix-with-full-path',
  524. 'prefix-with-parent',
  525. 'subtext-with-full-path',
  526. 'subtext-with-parent',
  527. 'no-parent',
  528. ];
  529. options.forEach(function(element) {
  530. if (element !== value) {
  531. $(`#${element} ${MCB}`).toggleClass(CKCLS, false);
  532. $(`#${element}`).toggleClass(CKCLS, false);
  533. }
  534. });
  535. $(`#${value} ${MCB}`).toggleClass(CKCLS, true);
  536. $(`#${value}`).toggleClass(CKCLS, true);
  537. this.currentBoard.setPresentParentTask(value);
  538. evt.preventDefault();
  539. },
  540. },
  541. ];
  542. },
  543. }).register('boardSubtaskSettingsPopup');
  544. BlazeComponent.extendComponent({
  545. onCreated() {
  546. this.currentBoard = Boards.findOne(Session.get('currentBoard'));
  547. },
  548. allowsReceivedDate() {
  549. return this.currentBoard.allowsReceivedDate;
  550. },
  551. allowsStartDate() {
  552. return this.currentBoard.allowsStartDate;
  553. },
  554. allowsDueDate() {
  555. return this.currentBoard.allowsDueDate;
  556. },
  557. allowsEndDate() {
  558. return this.currentBoard.allowsEndDate;
  559. },
  560. allowsSubtasks() {
  561. return this.currentBoard.allowsSubtasks;
  562. },
  563. allowsMembers() {
  564. return this.currentBoard.allowsMembers;
  565. },
  566. allowsAssignee() {
  567. return this.currentBoard.allowsAssignee;
  568. },
  569. allowsAssignedBy() {
  570. return this.currentBoard.allowsAssignedBy;
  571. },
  572. allowsRequestedBy() {
  573. return this.currentBoard.allowsRequestedBy;
  574. },
  575. allowsLabels() {
  576. return this.currentBoard.allowsLabels;
  577. },
  578. allowsChecklists() {
  579. return this.currentBoard.allowsChecklists;
  580. },
  581. allowsAttachments() {
  582. return this.currentBoard.allowsAttachments;
  583. },
  584. allowsComments() {
  585. return this.currentBoard.allowsComments;
  586. },
  587. allowsDescriptionTitle() {
  588. return this.currentBoard.allowsDescriptionTitle;
  589. },
  590. allowsDescriptionText() {
  591. return this.currentBoard.allowsDescriptionText;
  592. },
  593. isBoardSelected() {
  594. return this.currentBoard.dateSettingsDefaultBoardID;
  595. },
  596. isNullBoardSelected() {
  597. return (
  598. this.currentBoard.dateSettingsDefaultBoardId === null ||
  599. this.currentBoard.dateSettingsDefaultBoardId === undefined
  600. );
  601. },
  602. boards() {
  603. return Boards.find(
  604. {
  605. archived: false,
  606. 'members.userId': Meteor.userId(),
  607. },
  608. {
  609. sort: ['title'],
  610. },
  611. );
  612. },
  613. lists() {
  614. return Lists.find(
  615. {
  616. boardId: this.currentBoard._id,
  617. archived: false,
  618. },
  619. {
  620. sort: ['title'],
  621. },
  622. );
  623. },
  624. hasLists() {
  625. return this.lists().count() > 0;
  626. },
  627. isListSelected() {
  628. return (
  629. this.currentBoard.dateSettingsDefaultBoardId === this.currentData()._id
  630. );
  631. },
  632. events() {
  633. return [
  634. {
  635. 'click .js-field-has-receiveddate'(evt) {
  636. evt.preventDefault();
  637. this.currentBoard.allowsReceivedDate = !this.currentBoard
  638. .allowsReceivedDate;
  639. this.currentBoard.setAllowsReceivedDate(
  640. this.currentBoard.allowsReceivedDate,
  641. );
  642. $(`.js-field-has-receiveddate ${MCB}`).toggleClass(
  643. CKCLS,
  644. this.currentBoard.allowsReceivedDate,
  645. );
  646. $('.js-field-has-receiveddate').toggleClass(
  647. CKCLS,
  648. this.currentBoard.allowsReceivedDate,
  649. );
  650. },
  651. 'click .js-field-has-startdate'(evt) {
  652. evt.preventDefault();
  653. this.currentBoard.allowsStartDate = !this.currentBoard
  654. .allowsStartDate;
  655. this.currentBoard.setAllowsStartDate(
  656. this.currentBoard.allowsStartDate,
  657. );
  658. $(`.js-field-has-startdate ${MCB}`).toggleClass(
  659. CKCLS,
  660. this.currentBoard.allowsStartDate,
  661. );
  662. $('.js-field-has-startdate').toggleClass(
  663. CKCLS,
  664. this.currentBoard.allowsStartDate,
  665. );
  666. },
  667. 'click .js-field-has-enddate'(evt) {
  668. evt.preventDefault();
  669. this.currentBoard.allowsEndDate = !this.currentBoard.allowsEndDate;
  670. this.currentBoard.setAllowsEndDate(this.currentBoard.allowsEndDate);
  671. $(`.js-field-has-enddate ${MCB}`).toggleClass(
  672. CKCLS,
  673. this.currentBoard.allowsEndDate,
  674. );
  675. $('.js-field-has-enddate').toggleClass(
  676. CKCLS,
  677. this.currentBoard.allowsEndDate,
  678. );
  679. },
  680. 'click .js-field-has-duedate'(evt) {
  681. evt.preventDefault();
  682. this.currentBoard.allowsDueDate = !this.currentBoard.allowsDueDate;
  683. this.currentBoard.setAllowsDueDate(this.currentBoard.allowsDueDate);
  684. $(`.js-field-has-duedate ${MCB}`).toggleClass(
  685. CKCLS,
  686. this.currentBoard.allowsDueDate,
  687. );
  688. $('.js-field-has-duedate').toggleClass(
  689. CKCLS,
  690. this.currentBoard.allowsDueDate,
  691. );
  692. },
  693. 'click .js-field-has-subtasks'(evt) {
  694. evt.preventDefault();
  695. this.currentBoard.allowsSubtasks = !this.currentBoard.allowsSubtasks;
  696. this.currentBoard.setAllowsSubtasks(this.currentBoard.allowsSubtasks);
  697. $(`.js-field-has-subtasks ${MCB}`).toggleClass(
  698. CKCLS,
  699. this.currentBoard.allowsSubtasks,
  700. );
  701. $('.js-field-has-subtasks').toggleClass(
  702. CKCLS,
  703. this.currentBoard.allowsSubtasks,
  704. );
  705. },
  706. 'click .js-field-has-members'(evt) {
  707. evt.preventDefault();
  708. this.currentBoard.allowsMembers = !this.currentBoard.allowsMembers;
  709. this.currentBoard.setAllowsMembers(this.currentBoard.allowsMembers);
  710. $(`.js-field-has-members ${MCB}`).toggleClass(
  711. CKCLS,
  712. this.currentBoard.allowsMembers,
  713. );
  714. $('.js-field-has-members').toggleClass(
  715. CKCLS,
  716. this.currentBoard.allowsMembers,
  717. );
  718. },
  719. 'click .js-field-has-assignee'(evt) {
  720. evt.preventDefault();
  721. this.currentBoard.allowsAssignee = !this.currentBoard.allowsAssignee;
  722. this.currentBoard.setAllowsAssignee(this.currentBoard.allowsAssignee);
  723. $(`.js-field-has-assignee ${MCB}`).toggleClass(
  724. CKCLS,
  725. this.currentBoard.allowsAssignee,
  726. );
  727. $('.js-field-has-assignee').toggleClass(
  728. CKCLS,
  729. this.currentBoard.allowsAssignee,
  730. );
  731. },
  732. 'click .js-field-has-assigned-by'(evt) {
  733. evt.preventDefault();
  734. this.currentBoard.allowsAssignedBy = !this.currentBoard
  735. .allowsAssignedBy;
  736. this.currentBoard.setAllowsAssignedBy(
  737. this.currentBoard.allowsAssignedBy,
  738. );
  739. $(`.js-field-has-assigned-by ${MCB}`).toggleClass(
  740. CKCLS,
  741. this.currentBoard.allowsAssignedBy,
  742. );
  743. $('.js-field-has-assigned-by').toggleClass(
  744. CKCLS,
  745. this.currentBoard.allowsAssignedBy,
  746. );
  747. },
  748. 'click .js-field-has-requested-by'(evt) {
  749. evt.preventDefault();
  750. this.currentBoard.allowsRequestedBy = !this.currentBoard
  751. .allowsRequestedBy;
  752. this.currentBoard.setAllowsRequestedBy(
  753. this.currentBoard.allowsRequestedBy,
  754. );
  755. $(`.js-field-has-requested-by ${MCB}`).toggleClass(
  756. CKCLS,
  757. this.currentBoard.allowsRequestedBy,
  758. );
  759. $('.js-field-has-requested-by').toggleClass(
  760. CKCLS,
  761. this.currentBoard.allowsRequestedBy,
  762. );
  763. },
  764. 'click .js-field-has-labels'(evt) {
  765. evt.preventDefault();
  766. this.currentBoard.allowsLabels = !this.currentBoard.allowsLabels;
  767. this.currentBoard.setAllowsLabels(this.currentBoard.allowsLabels);
  768. $(`.js-field-has-labels ${MCB}`).toggleClass(
  769. CKCLS,
  770. this.currentBoard.allowsAssignee,
  771. );
  772. $('.js-field-has-labels').toggleClass(
  773. CKCLS,
  774. this.currentBoard.allowsLabels,
  775. );
  776. },
  777. 'click .js-field-has-description-title'(evt) {
  778. evt.preventDefault();
  779. this.currentBoard.allowsDescriptionTitle = !this.currentBoard
  780. .allowsDescriptionTitle;
  781. this.currentBoard.setAllowsDescriptionTitle(
  782. this.currentBoard.allowsDescriptionTitle,
  783. );
  784. $(`.js-field-has-description-title ${MCB}`).toggleClass(
  785. CKCLS,
  786. this.currentBoard.allowsDescriptionTitle,
  787. );
  788. $('.js-field-has-description-title').toggleClass(
  789. CKCLS,
  790. this.currentBoard.allowsDescriptionTitle,
  791. );
  792. },
  793. 'click .js-field-has-description-text'(evt) {
  794. evt.preventDefault();
  795. this.currentBoard.allowsDescriptionText = !this.currentBoard
  796. .allowsDescriptionText;
  797. this.currentBoard.setAllowsDescriptionText(
  798. this.currentBoard.allowsDescriptionText,
  799. );
  800. $(`.js-field-has-description-text ${MCB}`).toggleClass(
  801. CKCLS,
  802. this.currentBoard.allowsDescriptionText,
  803. );
  804. $('.js-field-has-description-text').toggleClass(
  805. CKCLS,
  806. this.currentBoard.allowsDescriptionText,
  807. );
  808. },
  809. 'click .js-field-has-checklists'(evt) {
  810. evt.preventDefault();
  811. this.currentBoard.allowsChecklists = !this.currentBoard
  812. .allowsChecklists;
  813. this.currentBoard.setAllowsChecklists(
  814. this.currentBoard.allowsChecklists,
  815. );
  816. $(`.js-field-has-checklists ${MCB}`).toggleClass(
  817. CKCLS,
  818. this.currentBoard.allowsChecklists,
  819. );
  820. $('.js-field-has-checklists').toggleClass(
  821. CKCLS,
  822. this.currentBoard.allowsChecklists,
  823. );
  824. },
  825. 'click .js-field-has-attachments'(evt) {
  826. evt.preventDefault();
  827. this.currentBoard.allowsAttachments = !this.currentBoard
  828. .allowsAttachments;
  829. this.currentBoard.setAllowsAttachments(
  830. this.currentBoard.allowsAttachments,
  831. );
  832. $(`.js-field-has-attachments ${MCB}`).toggleClass(
  833. CKCLS,
  834. this.currentBoard.allowsAttachments,
  835. );
  836. $('.js-field-has-attachments').toggleClass(
  837. CKCLS,
  838. this.currentBoard.allowsAttachments,
  839. );
  840. },
  841. 'click .js-field-has-comments'(evt) {
  842. evt.preventDefault();
  843. this.currentBoard.allowsComments = !this.currentBoard.allowsComments;
  844. this.currentBoard.setAllowsComments(this.currentBoard.allowsComments);
  845. $(`.js-field-has-comments ${MCB}`).toggleClass(
  846. CKCLS,
  847. this.currentBoard.allowsComments,
  848. );
  849. $('.js-field-has-comments').toggleClass(
  850. CKCLS,
  851. this.currentBoard.allowsComments,
  852. );
  853. },
  854. 'click .js-field-has-activities'(evt) {
  855. evt.preventDefault();
  856. this.currentBoard.allowsActivities = !this.currentBoard
  857. .allowsActivities;
  858. this.currentBoard.setAllowsActivities(
  859. this.currentBoard.allowsActivities,
  860. );
  861. $(`.js-field-has-activities ${MCB}`).toggleClass(
  862. CKCLS,
  863. this.currentBoard.allowsActivities,
  864. );
  865. $('.js-field-has-activities').toggleClass(
  866. CKCLS,
  867. this.currentBoard.allowsActivities,
  868. );
  869. },
  870. },
  871. ];
  872. },
  873. }).register('boardCardSettingsPopup');
  874. BlazeComponent.extendComponent({
  875. onCreated() {
  876. this.error = new ReactiveVar('');
  877. this.loading = new ReactiveVar(false);
  878. },
  879. onRendered() {
  880. this.find('.js-search-member input').focus();
  881. this.setLoading(false);
  882. },
  883. isBoardMember() {
  884. const userId = this.currentData()._id;
  885. const user = Users.findOne(userId);
  886. return user && user.isBoardMember();
  887. },
  888. isValidEmail(email) {
  889. return SimpleSchema.RegEx.Email.test(email);
  890. },
  891. setError(error) {
  892. this.error.set(error);
  893. },
  894. setLoading(w) {
  895. this.loading.set(w);
  896. },
  897. isLoading() {
  898. return this.loading.get();
  899. },
  900. inviteUser(idNameEmail) {
  901. const boardId = Session.get('currentBoard');
  902. this.setLoading(true);
  903. const self = this;
  904. Meteor.call('inviteUserToBoard', idNameEmail, boardId, (err, ret) => {
  905. self.setLoading(false);
  906. if (err) self.setError(err.error);
  907. else if (ret.email) self.setError('email-sent');
  908. else Popup.close();
  909. });
  910. },
  911. events() {
  912. return [
  913. {
  914. 'keyup input'() {
  915. this.setError('');
  916. },
  917. 'click .js-select-member'() {
  918. const userId = this.currentData()._id;
  919. const currentBoard = Boards.findOne(Session.get('currentBoard'));
  920. if (!currentBoard.hasMember(userId)) {
  921. this.inviteUser(userId);
  922. }
  923. },
  924. 'click .js-email-invite'() {
  925. const idNameEmail = $('.js-search-member input').val();
  926. if (idNameEmail.indexOf('@') < 0 || this.isValidEmail(idNameEmail)) {
  927. this.inviteUser(idNameEmail);
  928. } else this.setError('email-invalid');
  929. },
  930. },
  931. ];
  932. },
  933. }).register('addMemberPopup');
  934. Template.changePermissionsPopup.events({
  935. 'click .js-set-admin, click .js-set-normal, click .js-set-no-comments, click .js-set-comment-only, click .js-set-worker'(
  936. event,
  937. ) {
  938. const currentBoard = Boards.findOne(Session.get('currentBoard'));
  939. const memberId = this.userId;
  940. const isAdmin = $(event.currentTarget).hasClass('js-set-admin');
  941. const isCommentOnly = $(event.currentTarget).hasClass(
  942. 'js-set-comment-only',
  943. );
  944. const isNoComments = $(event.currentTarget).hasClass('js-set-no-comments');
  945. const isWorker = $(event.currentTarget).hasClass('js-set-worker');
  946. currentBoard.setMemberPermission(
  947. memberId,
  948. isAdmin,
  949. isNoComments,
  950. isCommentOnly,
  951. isWorker,
  952. );
  953. Popup.back(1);
  954. },
  955. });
  956. Template.changePermissionsPopup.helpers({
  957. isAdmin() {
  958. const currentBoard = Boards.findOne(Session.get('currentBoard'));
  959. return currentBoard.hasAdmin(this.userId);
  960. },
  961. isNormal() {
  962. const currentBoard = Boards.findOne(Session.get('currentBoard'));
  963. return (
  964. !currentBoard.hasAdmin(this.userId) &&
  965. !currentBoard.hasNoComments(this.userId) &&
  966. !currentBoard.hasCommentOnly(this.userId) &&
  967. !currentBoard.hasWorker(this.userId)
  968. );
  969. },
  970. isNoComments() {
  971. const currentBoard = Boards.findOne(Session.get('currentBoard'));
  972. return (
  973. !currentBoard.hasAdmin(this.userId) &&
  974. currentBoard.hasNoComments(this.userId)
  975. );
  976. },
  977. isCommentOnly() {
  978. const currentBoard = Boards.findOne(Session.get('currentBoard'));
  979. return (
  980. !currentBoard.hasAdmin(this.userId) &&
  981. currentBoard.hasCommentOnly(this.userId)
  982. );
  983. },
  984. isWorker() {
  985. const currentBoard = Boards.findOne(Session.get('currentBoard'));
  986. return (
  987. !currentBoard.hasAdmin(this.userId) && currentBoard.hasWorker(this.userId)
  988. );
  989. },
  990. isLastAdmin() {
  991. const currentBoard = Boards.findOne(Session.get('currentBoard'));
  992. return (
  993. currentBoard.hasAdmin(this.userId) && currentBoard.activeAdmins() === 1
  994. );
  995. },
  996. });