peopleBody.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414
  1. const usersPerPage = 25;
  2. BlazeComponent.extendComponent({
  3. mixins() {
  4. return [Mixins.InfiniteScrolling];
  5. },
  6. onCreated() {
  7. this.error = new ReactiveVar('');
  8. this.loading = new ReactiveVar(false);
  9. this.people = new ReactiveVar(true);
  10. this.findUsersOptions = new ReactiveVar({});
  11. this.number = new ReactiveVar(0);
  12. this.page = new ReactiveVar(1);
  13. this.loadNextPageLocked = false;
  14. this.callFirstWith(null, 'resetNextPeak');
  15. this.autorun(() => {
  16. const limit = this.page.get() * usersPerPage;
  17. this.subscribe('people', this.findUsersOptions.get(), limit, () => {
  18. this.loadNextPageLocked = false;
  19. const nextPeakBefore = this.callFirstWith(null, 'getNextPeak');
  20. this.calculateNextPeak();
  21. const nextPeakAfter = this.callFirstWith(null, 'getNextPeak');
  22. if (nextPeakBefore === nextPeakAfter) {
  23. this.callFirstWith(null, 'resetNextPeak');
  24. }
  25. });
  26. });
  27. },
  28. events() {
  29. return [
  30. {
  31. 'click #searchButton'() {
  32. this.filterPeople();
  33. },
  34. 'keydown #searchInput'(event) {
  35. if (event.keyCode === 13 && !event.shiftKey) {
  36. this.filterPeople();
  37. }
  38. },
  39. 'click #newUserButton'() {
  40. Popup.open('newUser');
  41. },
  42. },
  43. ];
  44. },
  45. filterPeople() {
  46. const value = $('#searchInput')
  47. .first()
  48. .val();
  49. if (value === '') {
  50. this.findUsersOptions.set({});
  51. } else {
  52. const regex = new RegExp(value, 'i');
  53. this.findUsersOptions.set({
  54. $or: [
  55. { username: regex },
  56. { 'profile.fullname': regex },
  57. { 'emails.address': regex },
  58. ],
  59. });
  60. }
  61. },
  62. loadNextPage() {
  63. if (this.loadNextPageLocked === false) {
  64. this.page.set(this.page.get() + 1);
  65. this.loadNextPageLocked = true;
  66. }
  67. },
  68. calculateNextPeak() {
  69. const element = this.find('.main-body');
  70. if (element) {
  71. const altitude = element.scrollHeight;
  72. this.callFirstWith(this, 'setNextPeak', altitude);
  73. }
  74. },
  75. reachNextPeak() {
  76. this.loadNextPage();
  77. },
  78. setError(error) {
  79. this.error.set(error);
  80. },
  81. setLoading(w) {
  82. this.loading.set(w);
  83. },
  84. peopleList() {
  85. const users = Users.find(this.findUsersOptions.get(), {
  86. fields: { _id: true },
  87. });
  88. this.number.set(users.count(false));
  89. return users;
  90. },
  91. peopleNumber() {
  92. return this.number.get();
  93. },
  94. }).register('people');
  95. Template.peopleRow.helpers({
  96. userData() {
  97. const userCollection = this.esSearch ? ESSearchResults : Users;
  98. return userCollection.findOne(this.userId);
  99. },
  100. });
  101. Template.editUserPopup.onCreated(function() {
  102. this.authenticationMethods = new ReactiveVar([]);
  103. this.errorMessage = new ReactiveVar('');
  104. Meteor.call('getAuthenticationsEnabled', (_, result) => {
  105. if (result) {
  106. // TODO : add a management of different languages
  107. // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
  108. this.authenticationMethods.set([
  109. { value: 'password' },
  110. // Gets only the authentication methods availables
  111. ...Object.entries(result)
  112. .filter(e => e[1])
  113. .map(e => ({ value: e[0] })),
  114. ]);
  115. }
  116. });
  117. });
  118. Template.editUserPopup.helpers({
  119. user() {
  120. return Users.findOne(this.userId);
  121. },
  122. authentications() {
  123. return Template.instance().authenticationMethods.get();
  124. },
  125. isSelected(match) {
  126. const userId = Template.instance().data.userId;
  127. const selected = Users.findOne(userId).authenticationMethod;
  128. return selected === match;
  129. },
  130. isLdap() {
  131. const userId = Template.instance().data.userId;
  132. const selected = Users.findOne(userId).authenticationMethod;
  133. return selected === 'ldap';
  134. },
  135. errorMessage() {
  136. return Template.instance().errorMessage.get();
  137. },
  138. });
  139. Template.newUserPopup.onCreated(function() {
  140. this.authenticationMethods = new ReactiveVar([]);
  141. this.errorMessage = new ReactiveVar('');
  142. Meteor.call('getAuthenticationsEnabled', (_, result) => {
  143. if (result) {
  144. // TODO : add a management of different languages
  145. // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
  146. this.authenticationMethods.set([
  147. { value: 'password' },
  148. // Gets only the authentication methods availables
  149. ...Object.entries(result)
  150. .filter(e => e[1])
  151. .map(e => ({ value: e[0] })),
  152. ]);
  153. }
  154. });
  155. });
  156. Template.newUserPopup.helpers({
  157. //user() {
  158. // return Users.findOne(this.userId);
  159. //},
  160. authentications() {
  161. return Template.instance().authenticationMethods.get();
  162. },
  163. //isSelected(match) {
  164. // const userId = Template.instance().data.userId;
  165. // const selected = Users.findOne(userId).authenticationMethod;
  166. // return selected === match;
  167. //},
  168. //isLdap() {
  169. // const userId = Template.instance().data.userId;
  170. // const selected = Users.findOne(userId).authenticationMethod;
  171. // return selected === 'ldap';
  172. //},
  173. errorMessage() {
  174. return Template.instance().errorMessage.get();
  175. },
  176. });
  177. BlazeComponent.extendComponent({
  178. onCreated() {},
  179. user() {
  180. return Users.findOne(this.userId);
  181. },
  182. events() {
  183. return [
  184. {
  185. 'click a.edit-user': Popup.open('editUser'),
  186. 'click a.more-settings-user': Popup.open('settingsUser'),
  187. },
  188. ];
  189. },
  190. }).register('peopleRow');
  191. BlazeComponent.extendComponent({
  192. events() {
  193. return [
  194. {
  195. 'click a.new-user': Popup.open('newUser'),
  196. },
  197. ];
  198. },
  199. }).register('newUserRow');
  200. Template.editUserPopup.events({
  201. submit(event, templateInstance) {
  202. event.preventDefault();
  203. const user = Users.findOne(this.userId);
  204. const fullname = templateInstance.find('.js-profile-fullname').value.trim();
  205. const username = templateInstance.find('.js-profile-username').value.trim();
  206. const password = templateInstance.find('.js-profile-password').value;
  207. const isAdmin = templateInstance.find('.js-profile-isadmin').value.trim();
  208. const isActive = templateInstance.find('.js-profile-isactive').value.trim();
  209. const email = templateInstance.find('.js-profile-email').value.trim();
  210. const authentication = templateInstance
  211. .find('.js-authenticationMethod')
  212. .value.trim();
  213. const isChangePassword = password.length > 0;
  214. const isChangeUserName = username !== user.username;
  215. // If previously email address has not been set, it is undefined,
  216. // check for undefined, and allow adding email address.
  217. const isChangeEmail =
  218. email.toLowerCase() !==
  219. (typeof user.emails !== 'undefined'
  220. ? user.emails[0].address.toLowerCase()
  221. : false);
  222. Users.update(this.userId, {
  223. $set: {
  224. 'profile.fullname': fullname,
  225. isAdmin: isAdmin === 'true',
  226. loginDisabled: isActive === 'true',
  227. authenticationMethod: authentication,
  228. },
  229. });
  230. if (isChangePassword) {
  231. Meteor.call('setPassword', password, this.userId);
  232. }
  233. if (isChangeUserName && isChangeEmail) {
  234. Meteor.call(
  235. 'setUsernameAndEmail',
  236. username,
  237. email.toLowerCase(),
  238. this.userId,
  239. function(error) {
  240. const usernameMessageElement = templateInstance.$('.username-taken');
  241. const emailMessageElement = templateInstance.$('.email-taken');
  242. if (error) {
  243. const errorElement = error.error;
  244. if (errorElement === 'username-already-taken') {
  245. usernameMessageElement.show();
  246. emailMessageElement.hide();
  247. } else if (errorElement === 'email-already-taken') {
  248. usernameMessageElement.hide();
  249. emailMessageElement.show();
  250. }
  251. } else {
  252. usernameMessageElement.hide();
  253. emailMessageElement.hide();
  254. Popup.close();
  255. }
  256. },
  257. );
  258. } else if (isChangeUserName) {
  259. Meteor.call('setUsername', username, this.userId, function(error) {
  260. const usernameMessageElement = templateInstance.$('.username-taken');
  261. if (error) {
  262. const errorElement = error.error;
  263. if (errorElement === 'username-already-taken') {
  264. usernameMessageElement.show();
  265. }
  266. } else {
  267. usernameMessageElement.hide();
  268. Popup.close();
  269. }
  270. });
  271. } else if (isChangeEmail) {
  272. Meteor.call('setEmail', email.toLowerCase(), this.userId, function(
  273. error,
  274. ) {
  275. const emailMessageElement = templateInstance.$('.email-taken');
  276. if (error) {
  277. const errorElement = error.error;
  278. if (errorElement === 'email-already-taken') {
  279. emailMessageElement.show();
  280. }
  281. } else {
  282. emailMessageElement.hide();
  283. Popup.close();
  284. }
  285. });
  286. } else Popup.close();
  287. },
  288. });
  289. Template.newUserPopup.events({
  290. submit(event, templateInstance) {
  291. event.preventDefault();
  292. const fullname = templateInstance.find('.js-profile-fullname').value.trim();
  293. const username = templateInstance.find('.js-profile-username').value.trim();
  294. const password = templateInstance.find('.js-profile-password').value;
  295. const isAdmin = templateInstance.find('.js-profile-isadmin').value.trim();
  296. const isActive = templateInstance.find('.js-profile-isactive').value.trim();
  297. const email = templateInstance.find('.js-profile-email').value.trim();
  298. Meteor.call(
  299. 'setCreateUser',
  300. fullname,
  301. username,
  302. password,
  303. isAdmin,
  304. isActive,
  305. email.toLowerCase(),
  306. function(error) {
  307. const usernameMessageElement = templateInstance.$('.username-taken');
  308. const emailMessageElement = templateInstance.$('.email-taken');
  309. if (error) {
  310. const errorElement = error.error;
  311. if (errorElement === 'username-already-taken') {
  312. usernameMessageElement.show();
  313. emailMessageElement.hide();
  314. } else if (errorElement === 'email-already-taken') {
  315. usernameMessageElement.hide();
  316. emailMessageElement.show();
  317. }
  318. } else {
  319. usernameMessageElement.hide();
  320. emailMessageElement.hide();
  321. Popup.close();
  322. }
  323. },
  324. );
  325. Popup.close();
  326. },
  327. });
  328. Template.settingsUserPopup.events({
  329. 'click .impersonate-user'(event) {
  330. event.preventDefault();
  331. Meteor.call('impersonate', this.userId, err => {
  332. if (!err) {
  333. FlowRouter.go('/');
  334. Meteor.connection.setUserId(this.userId);
  335. }
  336. });
  337. },
  338. 'click #deleteButton'(event) {
  339. event.preventDefault();
  340. /*
  341. // Delete is not enabled yet, because it does leave empty user avatars
  342. // to boards: boards members, card members and assignees have
  343. // empty users. See:
  344. // - wekan/client/components/settings/peopleBody.jade deleteButton
  345. // - wekan/client/components/settings/peopleBody.js deleteButton
  346. // - wekan/client/components/sidebar/sidebar.js Popup.afterConfirm('removeMember'
  347. // that does now remove member from board, card members and assignees correctly,
  348. // but that should be used to remove user from all boards similarly
  349. // - wekan/models/users.js Delete is not enabled
  350. //
  351. //console.log('user id: ' + this.userId);
  352. //Popup.afterConfirm('userDelete', function(event) {
  353. //Boards.find({ members: this.userId }).forEach(board => {
  354. // console.log('board id: ' + board._id);
  355. //Cards.find({ boardId: board._id, members: this.userId }).forEach(card => {
  356. // card.unassignMember(this.userId);
  357. //});
  358. //Cards.find({ boardId: board._id, members: this.userId }).forEach(card => {
  359. // card.unassignMember(this.userId);
  360. //});
  361. //Cards.find({ boardId: board._id, assignees: this.userId }).forEach(card => {
  362. // card.unassignAssignee(this.userId);
  363. //});
  364. //Boards.findOne({ boardId: board._id }).removeMember(this.userId);
  365. //});
  366. //Users.remove(this.userId);
  367. */
  368. Popup.close();
  369. },
  370. });
  371. Template.settingsUserPopup.helpers({
  372. user() {
  373. return Users.findOne(this.userId);
  374. },
  375. authentications() {
  376. return Template.instance().authenticationMethods.get();
  377. },
  378. isSelected(match) {
  379. const userId = Template.instance().data.userId;
  380. const selected = Users.findOne(userId).authenticationMethod;
  381. return selected === match;
  382. },
  383. isLdap() {
  384. const userId = Template.instance().data.userId;
  385. const selected = Users.findOne(userId).authenticationMethod;
  386. return selected === 'ldap';
  387. },
  388. errorMessage() {
  389. return Template.instance().errorMessage.get();
  390. },
  391. });