settingBody.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  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(false);
  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. this.attachmentSettings = new ReactiveVar(false);
  19. this.cronSettings = new ReactiveVar(false);
  20. Meteor.subscribe('setting');
  21. Meteor.subscribe('mailServer');
  22. Meteor.subscribe('accountSettings');
  23. Meteor.subscribe('tableVisibilityModeSettings');
  24. Meteor.subscribe('announcements');
  25. Meteor.subscribe('accessibilitySettings');
  26. Meteor.subscribe('globalwebhooks');
  27. Meteor.subscribe('lockoutSettings');
  28. },
  29. setError(error) {
  30. this.error.set(error);
  31. },
  32. // Template helpers moved to BlazeComponent - using different names to avoid conflicts
  33. isGeneralSetting() {
  34. return this.generalSetting && this.generalSetting.get();
  35. },
  36. isEmailSetting() {
  37. return this.emailSetting && this.emailSetting.get();
  38. },
  39. isAccountSetting() {
  40. return this.accountSetting && this.accountSetting.get();
  41. },
  42. isTableVisibilityModeSetting() {
  43. return this.tableVisibilityModeSetting && this.tableVisibilityModeSetting.get();
  44. },
  45. isAnnouncementSetting() {
  46. return this.announcementSetting && this.announcementSetting.get();
  47. },
  48. isAccessibilitySetting() {
  49. return this.accessibilitySetting && this.accessibilitySetting.get();
  50. },
  51. isLayoutSetting() {
  52. return this.layoutSetting && this.layoutSetting.get();
  53. },
  54. isWebhookSetting() {
  55. return this.webhookSetting && this.webhookSetting.get();
  56. },
  57. isAttachmentSettings() {
  58. return this.attachmentSettings && this.attachmentSettings.get();
  59. },
  60. isCronSettings() {
  61. return this.cronSettings && this.cronSettings.get();
  62. },
  63. isLoading() {
  64. return this.loading && this.loading.get();
  65. },
  66. // Attachment settings helpers
  67. filesystemPath() {
  68. return process.env.WRITABLE_PATH || '/data';
  69. },
  70. attachmentsPath() {
  71. const writablePath = process.env.WRITABLE_PATH || '/data';
  72. return `${writablePath}/attachments`;
  73. },
  74. avatarsPath() {
  75. const writablePath = process.env.WRITABLE_PATH || '/data';
  76. return `${writablePath}/avatars`;
  77. },
  78. gridfsEnabled() {
  79. return process.env.GRIDFS_ENABLED === 'true';
  80. },
  81. s3Enabled() {
  82. return process.env.S3_ENABLED === 'true';
  83. },
  84. s3Endpoint() {
  85. return process.env.S3_ENDPOINT || '';
  86. },
  87. s3Bucket() {
  88. return process.env.S3_BUCKET || '';
  89. },
  90. s3Region() {
  91. return process.env.S3_REGION || '';
  92. },
  93. s3SslEnabled() {
  94. return process.env.S3_SSL_ENABLED === 'true';
  95. },
  96. s3Port() {
  97. return process.env.S3_PORT || 443;
  98. },
  99. // Cron settings helpers
  100. migrationStatus() {
  101. return TAPi18n.__('idle'); // Placeholder
  102. },
  103. migrationProgress() {
  104. return 0; // Placeholder
  105. },
  106. cronJobs() {
  107. return []; // Placeholder
  108. },
  109. setLoading(w) {
  110. this.loading.set(w);
  111. },
  112. // Event handlers for attachment settings
  113. 'click button.js-test-s3-connection'(event) {
  114. event.preventDefault();
  115. const secretKey = $('#s3-secret-key').val();
  116. if (!secretKey) {
  117. alert(TAPi18n.__('s3-secret-key-required'));
  118. return;
  119. }
  120. Meteor.call('testS3Connection', { secretKey }, (error, result) => {
  121. if (error) {
  122. alert(TAPi18n.__('s3-connection-failed') + ': ' + error.reason);
  123. } else {
  124. alert(TAPi18n.__('s3-connection-success'));
  125. }
  126. });
  127. },
  128. 'click button.js-save-s3-settings'(event) {
  129. event.preventDefault();
  130. const secretKey = $('#s3-secret-key').val();
  131. if (!secretKey) {
  132. alert(TAPi18n.__('s3-secret-key-required'));
  133. return;
  134. }
  135. Meteor.call('saveS3Settings', { secretKey }, (error, result) => {
  136. if (error) {
  137. alert(TAPi18n.__('s3-settings-save-failed') + ': ' + error.reason);
  138. } else {
  139. alert(TAPi18n.__('s3-settings-saved'));
  140. $('#s3-secret-key').val(''); // Clear the password field
  141. }
  142. });
  143. },
  144. // Event handlers for cron settings
  145. 'click button.js-start-all-migrations'(event) {
  146. event.preventDefault();
  147. Meteor.call('startAllMigrations', (error, result) => {
  148. if (error) {
  149. alert(TAPi18n.__('migration-start-failed') + ': ' + error.reason);
  150. } else {
  151. alert(TAPi18n.__('migration-started'));
  152. }
  153. });
  154. },
  155. 'click button.js-pause-all-migrations'(event) {
  156. event.preventDefault();
  157. Meteor.call('pauseAllMigrations', (error, result) => {
  158. if (error) {
  159. alert(TAPi18n.__('migration-pause-failed') + ': ' + error.reason);
  160. } else {
  161. alert(TAPi18n.__('migration-paused'));
  162. }
  163. });
  164. },
  165. 'click button.js-stop-all-migrations'(event) {
  166. event.preventDefault();
  167. if (confirm(TAPi18n.__('migration-stop-confirm'))) {
  168. Meteor.call('stopAllMigrations', (error, result) => {
  169. if (error) {
  170. alert(TAPi18n.__('migration-stop-failed') + ': ' + error.reason);
  171. } else {
  172. alert(TAPi18n.__('migration-stopped'));
  173. }
  174. });
  175. }
  176. },
  177. 'click button.js-schedule-board-cleanup'(event) {
  178. event.preventDefault();
  179. Meteor.call('scheduleBoardCleanup', (error, result) => {
  180. if (error) {
  181. alert(TAPi18n.__('board-cleanup-failed') + ': ' + error.reason);
  182. } else {
  183. alert(TAPi18n.__('board-cleanup-scheduled'));
  184. }
  185. });
  186. },
  187. 'click button.js-schedule-board-archive'(event) {
  188. event.preventDefault();
  189. Meteor.call('scheduleBoardArchive', (error, result) => {
  190. if (error) {
  191. alert(TAPi18n.__('board-archive-failed') + ': ' + error.reason);
  192. } else {
  193. alert(TAPi18n.__('board-archive-scheduled'));
  194. }
  195. });
  196. },
  197. 'click button.js-schedule-board-backup'(event) {
  198. event.preventDefault();
  199. Meteor.call('scheduleBoardBackup', (error, result) => {
  200. if (error) {
  201. alert(TAPi18n.__('board-backup-failed') + ': ' + error.reason);
  202. } else {
  203. alert(TAPi18n.__('board-backup-scheduled'));
  204. }
  205. });
  206. },
  207. 'click button.js-pause-job'(event) {
  208. event.preventDefault();
  209. const jobId = $(event.target).data('job-id');
  210. Meteor.call('pauseCronJob', jobId, (error, result) => {
  211. if (error) {
  212. alert(TAPi18n.__('cron-job-pause-failed') + ': ' + error.reason);
  213. } else {
  214. alert(TAPi18n.__('cron-job-paused'));
  215. }
  216. });
  217. },
  218. 'click button.js-delete-job'(event) {
  219. event.preventDefault();
  220. const jobId = $(event.target).data('job-id');
  221. if (confirm(TAPi18n.__('cron-job-delete-confirm'))) {
  222. Meteor.call('deleteCronJob', jobId, (error, result) => {
  223. if (error) {
  224. alert(TAPi18n.__('cron-job-delete-failed') + ': ' + error.reason);
  225. } else {
  226. alert(TAPi18n.__('cron-job-deleted'));
  227. }
  228. });
  229. }
  230. },
  231. 'click button.js-add-cron-job'(event) {
  232. event.preventDefault();
  233. // Placeholder for adding a new cron job (e.g., open a modal)
  234. alert(TAPi18n.__('add-cron-job-placeholder'));
  235. },
  236. checkField(selector) {
  237. const value = $(selector).val();
  238. if (!value || value.trim() === '') {
  239. $(selector)
  240. .parents('li.smtp-form')
  241. .addClass('has-error');
  242. throw Error('blank field');
  243. } else {
  244. return value;
  245. }
  246. },
  247. boards() {
  248. const ret = ReactiveCache.getBoards(
  249. {
  250. archived: false,
  251. 'members.userId': Meteor.userId(),
  252. 'members.isAdmin': true,
  253. },
  254. {
  255. sort: { sort: 1 /* boards default sorting */ },
  256. },
  257. );
  258. return ret;
  259. },
  260. toggleForgotPassword() {
  261. this.setLoading(true);
  262. const forgotPasswordClosed = ReactiveCache.getCurrentSetting().disableForgotPassword;
  263. Settings.update(ReactiveCache.getCurrentSetting()._id, {
  264. $set: { disableForgotPassword: !forgotPasswordClosed },
  265. });
  266. this.setLoading(false);
  267. },
  268. toggleRegistration() {
  269. this.setLoading(true);
  270. const registrationClosed = ReactiveCache.getCurrentSetting().disableRegistration;
  271. Settings.update(ReactiveCache.getCurrentSetting()._id, {
  272. $set: { disableRegistration: !registrationClosed },
  273. });
  274. this.setLoading(false);
  275. if (registrationClosed) {
  276. $('.invite-people').slideUp();
  277. } else {
  278. $('.invite-people').slideDown();
  279. }
  280. },
  281. toggleTLS() {
  282. $('#mail-server-tls').toggleClass('is-checked');
  283. },
  284. toggleHideLogo() {
  285. $('#hide-logo').toggleClass('is-checked');
  286. },
  287. toggleHideCardCounterList() {
  288. $('#hide-card-counter-list').toggleClass('is-checked');
  289. },
  290. toggleHideBoardMemberList() {
  291. $('#hide-board-member-list').toggleClass('is-checked');
  292. },
  293. toggleAccessibilityPageEnabled() {
  294. $('#accessibility-page-enabled').toggleClass('is-checked');
  295. },
  296. toggleDisplayAuthenticationMethod() {
  297. $('#display-authentication-method').toggleClass('is-checked');
  298. },
  299. initializeAttachmentSubMenu() {
  300. // Set default sub-menu state for attachment settings
  301. // This will be handled by the attachment settings component
  302. console.log('Initializing attachment sub-menu');
  303. },
  304. initializeCronSubMenu() {
  305. // Set default sub-menu state for cron settings
  306. // This will be handled by the cron settings template
  307. console.log('Initializing cron sub-menu');
  308. },
  309. switchMenu(event) {
  310. const target = $(event.target);
  311. if (!target.hasClass('active')) {
  312. $('.side-menu li.active').removeClass('active');
  313. target.parent().addClass('active');
  314. const targetID = target.data('id');
  315. // Reset all settings to false
  316. this.forgotPasswordSetting.set(false);
  317. this.generalSetting.set(false);
  318. this.emailSetting.set(false);
  319. this.accountSetting.set(false);
  320. this.tableVisibilityModeSetting.set(false);
  321. this.announcementSetting.set(false);
  322. this.accessibilitySetting.set(false);
  323. this.layoutSetting.set(false);
  324. this.webhookSetting.set(false);
  325. this.attachmentSettings.set(false);
  326. this.cronSettings.set(false);
  327. // Set the selected setting to true
  328. if (targetID === 'registration-setting') {
  329. this.generalSetting.set(true);
  330. } else if (targetID === 'email-setting') {
  331. this.emailSetting.set(true);
  332. } else if (targetID === 'account-setting') {
  333. this.accountSetting.set(true);
  334. } else if (targetID === 'tableVisibilityMode-setting') {
  335. this.tableVisibilityModeSetting.set(true);
  336. } else if (targetID === 'announcement-setting') {
  337. this.announcementSetting.set(true);
  338. } else if (targetID === 'accessibility-setting') {
  339. this.accessibilitySetting.set(true);
  340. } else if (targetID === 'layout-setting') {
  341. this.layoutSetting.set(true);
  342. } else if (targetID === 'webhook-setting') {
  343. this.webhookSetting.set(true);
  344. } else if (targetID === 'attachment-settings') {
  345. this.attachmentSettings.set(true);
  346. this.initializeAttachmentSubMenu();
  347. } else if (targetID === 'cron-settings') {
  348. this.cronSettings.set(true);
  349. this.initializeCronSubMenu();
  350. }
  351. }
  352. },
  353. checkBoard(event) {
  354. let target = $(event.target);
  355. if (!target.hasClass('js-toggle-board-choose')) {
  356. target = target.parent();
  357. }
  358. const checkboxId = target.attr('id');
  359. $(`#${checkboxId} .materialCheckBox`).toggleClass('is-checked');
  360. $(`#${checkboxId}`).toggleClass('is-checked');
  361. },
  362. inviteThroughEmail() {
  363. const emails = $('#email-to-invite')
  364. .val()
  365. .toLowerCase()
  366. .trim()
  367. .split('\n')
  368. .join(',')
  369. .split(',');
  370. const boardsToInvite = [];
  371. $('.js-toggle-board-choose .materialCheckBox.is-checked').each(function() {
  372. boardsToInvite.push($(this).data('id'));
  373. });
  374. const validEmails = [];
  375. emails.forEach(email => {
  376. if (email && SimpleSchema.RegEx.Email.test(email.trim())) {
  377. validEmails.push(email.trim());
  378. }
  379. });
  380. if (validEmails.length) {
  381. this.setLoading(true);
  382. Meteor.call('sendInvitation', validEmails, boardsToInvite, () => {
  383. // if (!err) {
  384. // TODO - show more info to user
  385. // }
  386. this.setLoading(false);
  387. });
  388. }
  389. },
  390. saveMailServerInfo() {
  391. this.setLoading(true);
  392. $('li').removeClass('has-error');
  393. try {
  394. const host = this.checkField('#mail-server-host');
  395. const port = this.checkField('#mail-server-port');
  396. const username = $('#mail-server-username')
  397. .val()
  398. .trim();
  399. const password = $('#mail-server-password')
  400. .val()
  401. .trim();
  402. const from = this.checkField('#mail-server-from');
  403. const tls = $('#mail-server-tls.is-checked').length > 0;
  404. Settings.update(ReactiveCache.getCurrentSetting()._id, {
  405. $set: {
  406. 'mailServer.host': host,
  407. 'mailServer.port': port,
  408. 'mailServer.username': username,
  409. 'mailServer.password': password,
  410. 'mailServer.enableTLS': tls,
  411. 'mailServer.from': from,
  412. },
  413. });
  414. } catch (e) {
  415. return;
  416. } finally {
  417. this.setLoading(false);
  418. }
  419. },
  420. saveLayout() {
  421. this.setLoading(true);
  422. $('li').removeClass('has-error');
  423. const productName = ($('#product-name').val() || '').trim();
  424. const customLoginLogoImageUrl = ($('#custom-login-logo-image-url').val() || '').trim();
  425. const customLoginLogoLinkUrl = ($('#custom-login-logo-link-url').val() || '').trim();
  426. const customHelpLinkUrl = ($('#custom-help-link-url').val() || '').trim();
  427. const textBelowCustomLoginLogo = ($('#text-below-custom-login-logo').val() || '').trim();
  428. const automaticLinkedUrlSchemes = ($('#automatic-linked-url-schemes').val() || '').trim();
  429. const customTopLeftCornerLogoImageUrl = ($('#custom-top-left-corner-logo-image-url').val() || '').trim();
  430. const customTopLeftCornerLogoLinkUrl = ($('#custom-top-left-corner-logo-link-url').val() || '').trim();
  431. const customTopLeftCornerLogoHeight = ($('#custom-top-left-corner-logo-height').val() || '').trim();
  432. const oidcBtnText = ($('#oidcBtnTextvalue').val() || '').trim();
  433. const mailDomainName = ($('#mailDomainNamevalue').val() || '').trim();
  434. const legalNotice = ($('#legalNoticevalue').val() || '').trim();
  435. const hideLogoChange = $('input[name=hideLogo]:checked').val() === 'true';
  436. const hideCardCounterListChange = $('input[name=hideCardCounterList]:checked').val() === 'true';
  437. const hideBoardMemberListChange = $('input[name=hideBoardMemberList]:checked').val() === 'true';
  438. const displayAuthenticationMethod =
  439. $('input[name=displayAuthenticationMethod]:checked').val() === 'true';
  440. const defaultAuthenticationMethod = $('#defaultAuthenticationMethod').val();
  441. const spinnerName = ($('#spinnerName').val() || '').trim();
  442. try {
  443. Settings.update(ReactiveCache.getCurrentSetting()._id, {
  444. $set: {
  445. productName,
  446. hideLogo: hideLogoChange,
  447. hideCardCounterList: hideCardCounterListChange,
  448. hideBoardMemberList: hideBoardMemberListChange,
  449. customLoginLogoImageUrl,
  450. customLoginLogoLinkUrl,
  451. customHelpLinkUrl,
  452. textBelowCustomLoginLogo,
  453. customTopLeftCornerLogoImageUrl,
  454. customTopLeftCornerLogoLinkUrl,
  455. customTopLeftCornerLogoHeight,
  456. displayAuthenticationMethod,
  457. defaultAuthenticationMethod,
  458. automaticLinkedUrlSchemes,
  459. spinnerName,
  460. oidcBtnText,
  461. mailDomainName,
  462. legalNotice,
  463. },
  464. });
  465. } catch (e) {
  466. return;
  467. } finally {
  468. this.setLoading(false);
  469. }
  470. DocHead.setTitle(productName);
  471. },
  472. sendSMTPTestEmail() {
  473. Meteor.call('sendSMTPTestEmail', (err, ret) => {
  474. if (!err && ret) {
  475. const message = `${TAPi18n.__(ret.message)}: ${ret.email}`;
  476. alert(message);
  477. } else {
  478. const reason = err.reason || '';
  479. const message = `${TAPi18n.__(err.error)}\n${reason}`;
  480. alert(message);
  481. }
  482. });
  483. },
  484. events() {
  485. return [
  486. {
  487. 'click a.js-toggle-forgot-password': this.toggleForgotPassword,
  488. 'click a.js-toggle-registration': this.toggleRegistration,
  489. 'click a.js-toggle-tls': this.toggleTLS,
  490. 'click a.js-setting-menu': this.switchMenu,
  491. 'click a.js-toggle-board-choose': this.checkBoard,
  492. 'click button.js-email-invite': this.inviteThroughEmail,
  493. 'click button.js-save': this.saveMailServerInfo,
  494. 'click button.js-send-smtp-test-email': this.sendSMTPTestEmail,
  495. 'click a.js-toggle-hide-logo': this.toggleHideLogo,
  496. 'click a.js-toggle-hide-card-counter-list': this.toggleHideCardCounterList,
  497. 'click a.js-toggle-hide-board-member-list': this.toggleHideBoardMemberList,
  498. 'click button.js-save-layout': this.saveLayout,
  499. 'click a.js-toggle-display-authentication-method': this
  500. .toggleDisplayAuthenticationMethod,
  501. },
  502. ];
  503. },
  504. }).register('setting');
  505. BlazeComponent.extendComponent({
  506. saveAccountsChange() {
  507. const allowEmailChange =
  508. $('input[name=allowEmailChange]:checked').val() === 'true';
  509. const allowUserNameChange =
  510. $('input[name=allowUserNameChange]:checked').val() === 'true';
  511. const allowUserDelete =
  512. $('input[name=allowUserDelete]:checked').val() === 'true';
  513. AccountSettings.update('accounts-allowEmailChange', {
  514. $set: { booleanValue: allowEmailChange },
  515. });
  516. AccountSettings.update('accounts-allowUserNameChange', {
  517. $set: { booleanValue: allowUserNameChange },
  518. });
  519. AccountSettings.update('accounts-allowUserDelete', {
  520. $set: { booleanValue: allowUserDelete },
  521. });
  522. },
  523. // Brute force lockout settings method moved to lockedUsersBody.js
  524. allowEmailChange() {
  525. return AccountSettings.findOne('accounts-allowEmailChange')?.booleanValue || false;
  526. },
  527. allowUserNameChange() {
  528. return AccountSettings.findOne('accounts-allowUserNameChange')?.booleanValue || false;
  529. },
  530. allowUserDelete() {
  531. return AccountSettings.findOne('accounts-allowUserDelete')?.booleanValue || false;
  532. },
  533. // Lockout settings helper methods moved to lockedUsersBody.js
  534. allBoardsHideActivities() {
  535. Meteor.call('setAllBoardsHideActivities', (err, ret) => {
  536. if (!err && ret) {
  537. if (ret === true) {
  538. const message = `${TAPi18n.__(
  539. 'now-activities-of-all-boards-are-hidden',
  540. )}`;
  541. alert(message);
  542. }
  543. } else {
  544. const reason = err.reason || '';
  545. const message = `${TAPi18n.__(err.error)}\n${reason}`;
  546. alert(message);
  547. }
  548. });
  549. },
  550. events() {
  551. return [
  552. {
  553. 'click button.js-accounts-save': this.saveAccountsChange,
  554. },
  555. {
  556. 'click button.js-all-boards-hide-activities': this.allBoardsHideActivities,
  557. },
  558. ];
  559. },
  560. }).register('accountSettings');
  561. BlazeComponent.extendComponent({
  562. saveTableVisibilityChange() {
  563. const allowPrivateOnly =
  564. $('input[name=allowPrivateOnly]:checked').val() === 'true';
  565. TableVisibilityModeSettings.update('tableVisibilityMode-allowPrivateOnly', {
  566. $set: { booleanValue: allowPrivateOnly },
  567. });
  568. },
  569. allowPrivateOnly() {
  570. return TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly').booleanValue;
  571. },
  572. allBoardsHideActivities() {
  573. Meteor.call('setAllBoardsHideActivities', (err, ret) => {
  574. if (!err && ret) {
  575. if (ret === true) {
  576. const message = `${TAPi18n.__(
  577. 'now-activities-of-all-boards-are-hidden',
  578. )}`;
  579. alert(message);
  580. }
  581. } else {
  582. const reason = err.reason || '';
  583. const message = `${TAPi18n.__(err.error)}\n${reason}`;
  584. alert(message);
  585. }
  586. });
  587. },
  588. events() {
  589. return [
  590. {
  591. 'click button.js-tableVisibilityMode-save': this.saveTableVisibilityChange,
  592. },
  593. {
  594. 'click button.js-all-boards-hide-activities': this.allBoardsHideActivities,
  595. },
  596. ];
  597. },
  598. }).register('tableVisibilityModeSettings');
  599. BlazeComponent.extendComponent({
  600. onCreated() {
  601. this.loading = new ReactiveVar(false);
  602. },
  603. setLoading(w) {
  604. this.loading.set(w);
  605. },
  606. currentAnnouncements() {
  607. return Announcements.findOne();
  608. },
  609. saveMessage() {
  610. const message = $('#admin-announcement')
  611. .val()
  612. .trim();
  613. Announcements.update(Announcements.findOne()._id, {
  614. $set: { body: message },
  615. });
  616. },
  617. toggleActive() {
  618. this.setLoading(true);
  619. const announcements = this.currentAnnouncements();
  620. const isActive = announcements.enabled;
  621. Announcements.update(announcements._id, {
  622. $set: { enabled: !isActive },
  623. });
  624. this.setLoading(false);
  625. if (isActive) {
  626. $('.admin-announcement').slideUp();
  627. } else {
  628. $('.admin-announcement').slideDown();
  629. }
  630. },
  631. events() {
  632. return [
  633. {
  634. 'click a.js-toggle-activemessage': this.toggleActive,
  635. 'click button.js-announcement-save': this.saveMessage,
  636. },
  637. ];
  638. },
  639. }).register('announcementSettings');
  640. BlazeComponent.extendComponent({
  641. onCreated() {
  642. this.loading = new ReactiveVar(false);
  643. },
  644. setLoading(w) {
  645. this.loading.set(w);
  646. },
  647. currentAccessibility() {
  648. return AccessibilitySettings.findOne();
  649. },
  650. saveAccessibility() {
  651. this.setLoading(true);
  652. const title = $('#admin-accessibility-title')
  653. .val()
  654. .trim();
  655. const content = $('#admin-accessibility-content')
  656. .val()
  657. .trim();
  658. try {
  659. AccessibilitySettings.update(AccessibilitySettings.findOne()._id, {
  660. $set: {
  661. title: title,
  662. body: content
  663. },
  664. });
  665. } catch (e) {
  666. console.error('Error saving accessibility settings:', e);
  667. return;
  668. } finally {
  669. this.setLoading(false);
  670. }
  671. },
  672. toggleAccessibility() {
  673. this.setLoading(true);
  674. const accessibilitySetting = this.currentAccessibility();
  675. const isActive = accessibilitySetting.enabled;
  676. AccessibilitySettings.update(accessibilitySetting._id, {
  677. $set: { enabled: !isActive },
  678. });
  679. this.setLoading(false);
  680. if (isActive) {
  681. $('.accessibility-content').slideUp();
  682. } else {
  683. $('.accessibility-content').slideDown();
  684. }
  685. },
  686. events() {
  687. return [
  688. {
  689. 'click a.js-toggle-accessibility': this.toggleAccessibility,
  690. 'click button.js-accessibility-save': this.saveAccessibility,
  691. },
  692. ];
  693. },
  694. }).register('accessibilitySettings');
  695. Template.selectAuthenticationMethod.onCreated(function() {
  696. this.authenticationMethods = new ReactiveVar([]);
  697. Meteor.call('getAuthenticationsEnabled', (_, result) => {
  698. if (result) {
  699. // TODO : add a management of different languages
  700. // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
  701. this.authenticationMethods.set([
  702. { value: 'password' },
  703. // Gets only the authentication methods availables
  704. ...Object.entries(result)
  705. .filter(e => e[1])
  706. .map(e => ({ value: e[0] })),
  707. ]);
  708. }
  709. });
  710. });
  711. Template.selectAuthenticationMethod.helpers({
  712. authentications() {
  713. return Template.instance().authenticationMethods.get();
  714. },
  715. isSelected(match) {
  716. return Template.instance().data.authenticationMethod === match;
  717. },
  718. });
  719. Template.selectSpinnerName.helpers({
  720. spinners() {
  721. return ALLOWED_WAIT_SPINNERS;
  722. },
  723. isSelected(match) {
  724. return Template.instance().data.spinnerName === match;
  725. },
  726. });