settingBody.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. import { ReactiveCache } from '/imports/reactiveCache';
  2. import { TAPi18n } from '/imports/i18n';
  3. import { ALLOWED_WAIT_SPINNERS } from '/config/const';
  4. import LockoutSettings from '/models/lockoutSettings';
  5. BlazeComponent.extendComponent({
  6. onCreated() {
  7. this.error = new ReactiveVar('');
  8. this.loading = new ReactiveVar(false);
  9. this.forgotPasswordSetting = new ReactiveVar(true);
  10. this.generalSetting = new ReactiveVar(true);
  11. this.emailSetting = new ReactiveVar(false);
  12. this.accountSetting = new ReactiveVar(false);
  13. this.tableVisibilityModeSetting = new ReactiveVar(false);
  14. this.announcementSetting = new ReactiveVar(false);
  15. this.accessibilitySetting = new ReactiveVar(false);
  16. this.layoutSetting = new ReactiveVar(false);
  17. this.webhookSetting = new ReactiveVar(false);
  18. Meteor.subscribe('setting');
  19. Meteor.subscribe('mailServer');
  20. Meteor.subscribe('accountSettings');
  21. Meteor.subscribe('tableVisibilityModeSettings');
  22. Meteor.subscribe('announcements');
  23. Meteor.subscribe('accessibilitySettings');
  24. Meteor.subscribe('globalwebhooks');
  25. Meteor.subscribe('lockoutSettings');
  26. },
  27. setError(error) {
  28. this.error.set(error);
  29. },
  30. setLoading(w) {
  31. this.loading.set(w);
  32. },
  33. checkField(selector) {
  34. const value = $(selector).val();
  35. if (!value || value.trim() === '') {
  36. $(selector)
  37. .parents('li.smtp-form')
  38. .addClass('has-error');
  39. throw Error('blank field');
  40. } else {
  41. return value;
  42. }
  43. },
  44. boards() {
  45. const ret = ReactiveCache.getBoards(
  46. {
  47. archived: false,
  48. 'members.userId': Meteor.userId(),
  49. 'members.isAdmin': true,
  50. },
  51. {
  52. sort: { sort: 1 /* boards default sorting */ },
  53. },
  54. );
  55. return ret;
  56. },
  57. toggleForgotPassword() {
  58. this.setLoading(true);
  59. const forgotPasswordClosed = ReactiveCache.getCurrentSetting().disableForgotPassword;
  60. Settings.update(ReactiveCache.getCurrentSetting()._id, {
  61. $set: { disableForgotPassword: !forgotPasswordClosed },
  62. });
  63. this.setLoading(false);
  64. },
  65. toggleRegistration() {
  66. this.setLoading(true);
  67. const registrationClosed = ReactiveCache.getCurrentSetting().disableRegistration;
  68. Settings.update(ReactiveCache.getCurrentSetting()._id, {
  69. $set: { disableRegistration: !registrationClosed },
  70. });
  71. this.setLoading(false);
  72. if (registrationClosed) {
  73. $('.invite-people').slideUp();
  74. } else {
  75. $('.invite-people').slideDown();
  76. }
  77. },
  78. toggleTLS() {
  79. $('#mail-server-tls').toggleClass('is-checked');
  80. },
  81. toggleHideLogo() {
  82. $('#hide-logo').toggleClass('is-checked');
  83. },
  84. toggleHideCardCounterList() {
  85. $('#hide-card-counter-list').toggleClass('is-checked');
  86. },
  87. toggleHideBoardMemberList() {
  88. $('#hide-board-member-list').toggleClass('is-checked');
  89. },
  90. toggleAccessibilityPageEnabled() {
  91. $('#accessibility-page-enabled').toggleClass('is-checked');
  92. },
  93. toggleDisplayAuthenticationMethod() {
  94. $('#display-authentication-method').toggleClass('is-checked');
  95. },
  96. switchMenu(event) {
  97. const target = $(event.target);
  98. if (!target.hasClass('active')) {
  99. $('.side-menu li.active').removeClass('active');
  100. target.parent().addClass('active');
  101. const targetID = target.data('id');
  102. this.forgotPasswordSetting.set('forgot-password-setting' === targetID);
  103. this.generalSetting.set('registration-setting' === targetID);
  104. this.emailSetting.set('email-setting' === targetID);
  105. this.accountSetting.set('account-setting' === targetID);
  106. this.announcementSetting.set('announcement-setting' === targetID);
  107. this.accessibilitySetting.set('accessibility-setting' === targetID);
  108. this.layoutSetting.set('layout-setting' === targetID);
  109. this.webhookSetting.set('webhook-setting' === targetID);
  110. this.tableVisibilityModeSetting.set('tableVisibilityMode-setting' === targetID);
  111. }
  112. },
  113. checkBoard(event) {
  114. let target = $(event.target);
  115. if (!target.hasClass('js-toggle-board-choose')) {
  116. target = target.parent();
  117. }
  118. const checkboxId = target.attr('id');
  119. $(`#${checkboxId} .materialCheckBox`).toggleClass('is-checked');
  120. $(`#${checkboxId}`).toggleClass('is-checked');
  121. },
  122. inviteThroughEmail() {
  123. const emails = $('#email-to-invite')
  124. .val()
  125. .toLowerCase()
  126. .trim()
  127. .split('\n')
  128. .join(',')
  129. .split(',');
  130. const boardsToInvite = [];
  131. $('.js-toggle-board-choose .materialCheckBox.is-checked').each(function() {
  132. boardsToInvite.push($(this).data('id'));
  133. });
  134. const validEmails = [];
  135. emails.forEach(email => {
  136. if (email && SimpleSchema.RegEx.Email.test(email.trim())) {
  137. validEmails.push(email.trim());
  138. }
  139. });
  140. if (validEmails.length) {
  141. this.setLoading(true);
  142. Meteor.call('sendInvitation', validEmails, boardsToInvite, () => {
  143. // if (!err) {
  144. // TODO - show more info to user
  145. // }
  146. this.setLoading(false);
  147. });
  148. }
  149. },
  150. saveMailServerInfo() {
  151. this.setLoading(true);
  152. $('li').removeClass('has-error');
  153. try {
  154. const host = this.checkField('#mail-server-host');
  155. const port = this.checkField('#mail-server-port');
  156. const username = $('#mail-server-username')
  157. .val()
  158. .trim();
  159. const password = $('#mail-server-password')
  160. .val()
  161. .trim();
  162. const from = this.checkField('#mail-server-from');
  163. const tls = $('#mail-server-tls.is-checked').length > 0;
  164. Settings.update(ReactiveCache.getCurrentSetting()._id, {
  165. $set: {
  166. 'mailServer.host': host,
  167. 'mailServer.port': port,
  168. 'mailServer.username': username,
  169. 'mailServer.password': password,
  170. 'mailServer.enableTLS': tls,
  171. 'mailServer.from': from,
  172. },
  173. });
  174. } catch (e) {
  175. return;
  176. } finally {
  177. this.setLoading(false);
  178. }
  179. },
  180. saveLayout() {
  181. this.setLoading(true);
  182. $('li').removeClass('has-error');
  183. const productName = ($('#product-name').val() || '').trim();
  184. const customLoginLogoImageUrl = ($('#custom-login-logo-image-url').val() || '').trim();
  185. const customLoginLogoLinkUrl = ($('#custom-login-logo-link-url').val() || '').trim();
  186. const customHelpLinkUrl = ($('#custom-help-link-url').val() || '').trim();
  187. const textBelowCustomLoginLogo = ($('#text-below-custom-login-logo').val() || '').trim();
  188. const automaticLinkedUrlSchemes = ($('#automatic-linked-url-schemes').val() || '').trim();
  189. const customTopLeftCornerLogoImageUrl = ($('#custom-top-left-corner-logo-image-url').val() || '').trim();
  190. const customTopLeftCornerLogoLinkUrl = ($('#custom-top-left-corner-logo-link-url').val() || '').trim();
  191. const customTopLeftCornerLogoHeight = ($('#custom-top-left-corner-logo-height').val() || '').trim();
  192. const oidcBtnText = ($('#oidcBtnTextvalue').val() || '').trim();
  193. const mailDomainName = ($('#mailDomainNamevalue').val() || '').trim();
  194. const legalNotice = ($('#legalNoticevalue').val() || '').trim();
  195. const hideLogoChange = $('input[name=hideLogo]:checked').val() === 'true';
  196. const hideCardCounterListChange = $('input[name=hideCardCounterList]:checked').val() === 'true';
  197. const hideBoardMemberListChange = $('input[name=hideBoardMemberList]:checked').val() === 'true';
  198. const displayAuthenticationMethod =
  199. $('input[name=displayAuthenticationMethod]:checked').val() === 'true';
  200. const defaultAuthenticationMethod = $('#defaultAuthenticationMethod').val();
  201. const accessibilityPageEnabled = $('input[name=accessibilityPageEnabled]:checked').val() === 'true';
  202. const accessibilityTitle = ($('#accessibility-title').val() || '').trim();
  203. const accessibilityContent = ($('#accessibility-content').val() || '').trim();
  204. const spinnerName = ($('#spinnerName').val() || '').trim();
  205. try {
  206. Settings.update(ReactiveCache.getCurrentSetting()._id, {
  207. $set: {
  208. productName,
  209. hideLogo: hideLogoChange,
  210. hideCardCounterList: hideCardCounterListChange,
  211. hideBoardMemberList: hideBoardMemberListChange,
  212. customLoginLogoImageUrl,
  213. customLoginLogoLinkUrl,
  214. customHelpLinkUrl,
  215. textBelowCustomLoginLogo,
  216. customTopLeftCornerLogoImageUrl,
  217. customTopLeftCornerLogoLinkUrl,
  218. customTopLeftCornerLogoHeight,
  219. displayAuthenticationMethod,
  220. defaultAuthenticationMethod,
  221. automaticLinkedUrlSchemes,
  222. spinnerName,
  223. oidcBtnText,
  224. mailDomainName,
  225. legalNotice,
  226. accessibilityPageEnabled,
  227. accessibilityTitle,
  228. accessibilityContent,
  229. },
  230. });
  231. } catch (e) {
  232. return;
  233. } finally {
  234. this.setLoading(false);
  235. }
  236. DocHead.setTitle(productName);
  237. },
  238. sendSMTPTestEmail() {
  239. Meteor.call('sendSMTPTestEmail', (err, ret) => {
  240. if (!err && ret) {
  241. const message = `${TAPi18n.__(ret.message)}: ${ret.email}`;
  242. alert(message);
  243. } else {
  244. const reason = err.reason || '';
  245. const message = `${TAPi18n.__(err.error)}\n${reason}`;
  246. alert(message);
  247. }
  248. });
  249. },
  250. events() {
  251. return [
  252. {
  253. 'click a.js-toggle-forgot-password': this.toggleForgotPassword,
  254. 'click a.js-toggle-registration': this.toggleRegistration,
  255. 'click a.js-toggle-tls': this.toggleTLS,
  256. 'click a.js-setting-menu': this.switchMenu,
  257. 'click a.js-toggle-board-choose': this.checkBoard,
  258. 'click button.js-email-invite': this.inviteThroughEmail,
  259. 'click button.js-save': this.saveMailServerInfo,
  260. 'click button.js-send-smtp-test-email': this.sendSMTPTestEmail,
  261. 'click a.js-toggle-hide-logo': this.toggleHideLogo,
  262. 'click a.js-toggle-hide-card-counter-list': this.toggleHideCardCounterList,
  263. 'click a.js-toggle-hide-board-member-list': this.toggleHideBoardMemberList,
  264. 'click button.js-save-layout': this.saveLayout,
  265. 'click a.js-toggle-display-authentication-method': this
  266. .toggleDisplayAuthenticationMethod,
  267. },
  268. ];
  269. },
  270. }).register('setting');
  271. BlazeComponent.extendComponent({
  272. saveAccountsChange() {
  273. const allowEmailChange =
  274. $('input[name=allowEmailChange]:checked').val() === 'true';
  275. const allowUserNameChange =
  276. $('input[name=allowUserNameChange]:checked').val() === 'true';
  277. const allowUserDelete =
  278. $('input[name=allowUserDelete]:checked').val() === 'true';
  279. AccountSettings.update('accounts-allowEmailChange', {
  280. $set: { booleanValue: allowEmailChange },
  281. });
  282. AccountSettings.update('accounts-allowUserNameChange', {
  283. $set: { booleanValue: allowUserNameChange },
  284. });
  285. AccountSettings.update('accounts-allowUserDelete', {
  286. $set: { booleanValue: allowUserDelete },
  287. });
  288. },
  289. // Brute force lockout settings method moved to lockedUsersBody.js
  290. allowEmailChange() {
  291. return AccountSettings.findOne('accounts-allowEmailChange')?.booleanValue || false;
  292. },
  293. allowUserNameChange() {
  294. return AccountSettings.findOne('accounts-allowUserNameChange')?.booleanValue || false;
  295. },
  296. allowUserDelete() {
  297. return AccountSettings.findOne('accounts-allowUserDelete')?.booleanValue || false;
  298. },
  299. // Lockout settings helper methods moved to lockedUsersBody.js
  300. allBoardsHideActivities() {
  301. Meteor.call('setAllBoardsHideActivities', (err, ret) => {
  302. if (!err && ret) {
  303. if (ret === true) {
  304. const message = `${TAPi18n.__(
  305. 'now-activities-of-all-boards-are-hidden',
  306. )}`;
  307. alert(message);
  308. }
  309. } else {
  310. const reason = err.reason || '';
  311. const message = `${TAPi18n.__(err.error)}\n${reason}`;
  312. alert(message);
  313. }
  314. });
  315. },
  316. events() {
  317. return [
  318. {
  319. 'click button.js-accounts-save': this.saveAccountsChange,
  320. },
  321. {
  322. 'click button.js-all-boards-hide-activities': this.allBoardsHideActivities,
  323. },
  324. ];
  325. },
  326. }).register('accountSettings');
  327. BlazeComponent.extendComponent({
  328. saveTableVisibilityChange() {
  329. const allowPrivateOnly =
  330. $('input[name=allowPrivateOnly]:checked').val() === 'true';
  331. TableVisibilityModeSettings.update('tableVisibilityMode-allowPrivateOnly', {
  332. $set: { booleanValue: allowPrivateOnly },
  333. });
  334. },
  335. allowPrivateOnly() {
  336. return TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly').booleanValue;
  337. },
  338. allBoardsHideActivities() {
  339. Meteor.call('setAllBoardsHideActivities', (err, ret) => {
  340. if (!err && ret) {
  341. if (ret === true) {
  342. const message = `${TAPi18n.__(
  343. 'now-activities-of-all-boards-are-hidden',
  344. )}`;
  345. alert(message);
  346. }
  347. } else {
  348. const reason = err.reason || '';
  349. const message = `${TAPi18n.__(err.error)}\n${reason}`;
  350. alert(message);
  351. }
  352. });
  353. },
  354. events() {
  355. return [
  356. {
  357. 'click button.js-tableVisibilityMode-save': this.saveTableVisibilityChange,
  358. },
  359. {
  360. 'click button.js-all-boards-hide-activities': this.allBoardsHideActivities,
  361. },
  362. ];
  363. },
  364. }).register('tableVisibilityModeSettings');
  365. BlazeComponent.extendComponent({
  366. onCreated() {
  367. this.loading = new ReactiveVar(false);
  368. },
  369. setLoading(w) {
  370. this.loading.set(w);
  371. },
  372. currentAnnouncements() {
  373. return Announcements.findOne();
  374. },
  375. saveMessage() {
  376. const message = $('#admin-announcement')
  377. .val()
  378. .trim();
  379. Announcements.update(Announcements.findOne()._id, {
  380. $set: { body: message },
  381. });
  382. },
  383. toggleActive() {
  384. this.setLoading(true);
  385. const announcements = this.currentAnnouncements();
  386. const isActive = announcements.enabled;
  387. Announcements.update(announcements._id, {
  388. $set: { enabled: !isActive },
  389. });
  390. this.setLoading(false);
  391. if (isActive) {
  392. $('.admin-announcement').slideUp();
  393. } else {
  394. $('.admin-announcement').slideDown();
  395. }
  396. },
  397. events() {
  398. return [
  399. {
  400. 'click a.js-toggle-activemessage': this.toggleActive,
  401. 'click button.js-announcement-save': this.saveMessage,
  402. },
  403. ];
  404. },
  405. }).register('announcementSettings');
  406. BlazeComponent.extendComponent({
  407. onCreated() {
  408. this.loading = new ReactiveVar(false);
  409. },
  410. setLoading(w) {
  411. this.loading.set(w);
  412. },
  413. currentAccessibility() {
  414. return AccessibilitySettings.findOne();
  415. },
  416. saveAccessibility() {
  417. const title = $('#admin-accessibility-title')
  418. .val()
  419. .trim();
  420. const content = $('#admin-accessibility-content')
  421. .val()
  422. .trim();
  423. AccessibilitySettings.update(AccessibilitySettings.findOne()._id, {
  424. $set: {
  425. title: title,
  426. body: content
  427. },
  428. });
  429. },
  430. toggleAccessibility() {
  431. this.setLoading(true);
  432. const accessibilitySetting = this.currentAccessibility();
  433. const isActive = accessibilitySetting.enabled;
  434. AccessibilitySettings.update(accessibilitySetting._id, {
  435. $set: { enabled: !isActive },
  436. });
  437. this.setLoading(false);
  438. if (isActive) {
  439. $('.accessibility-content').slideUp();
  440. } else {
  441. $('.accessibility-content').slideDown();
  442. }
  443. },
  444. events() {
  445. return [
  446. {
  447. 'click a.js-toggle-accessibility': this.toggleAccessibility,
  448. 'click button.js-accessibility-save': this.saveAccessibility,
  449. },
  450. ];
  451. },
  452. }).register('accessibilitySettings');
  453. Template.selectAuthenticationMethod.onCreated(function() {
  454. this.authenticationMethods = new ReactiveVar([]);
  455. Meteor.call('getAuthenticationsEnabled', (_, result) => {
  456. if (result) {
  457. // TODO : add a management of different languages
  458. // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
  459. this.authenticationMethods.set([
  460. { value: 'password' },
  461. // Gets only the authentication methods availables
  462. ...Object.entries(result)
  463. .filter(e => e[1])
  464. .map(e => ({ value: e[0] })),
  465. ]);
  466. }
  467. });
  468. });
  469. Template.selectAuthenticationMethod.helpers({
  470. authentications() {
  471. return Template.instance().authenticationMethods.get();
  472. },
  473. isSelected(match) {
  474. return Template.instance().data.authenticationMethod === match;
  475. },
  476. });
  477. Template.selectSpinnerName.helpers({
  478. spinners() {
  479. return ALLOWED_WAIT_SPINNERS;
  480. },
  481. isSelected(match) {
  482. return Template.instance().data.spinnerName === match;
  483. },
  484. });