settingBody.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658
  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. setLoading(w) {
  33. this.loading.set(w);
  34. },
  35. checkField(selector) {
  36. const value = $(selector).val();
  37. if (!value || value.trim() === '') {
  38. $(selector)
  39. .parents('li.smtp-form')
  40. .addClass('has-error');
  41. throw Error('blank field');
  42. } else {
  43. return value;
  44. }
  45. },
  46. boards() {
  47. const ret = ReactiveCache.getBoards(
  48. {
  49. archived: false,
  50. 'members.userId': Meteor.userId(),
  51. 'members.isAdmin': true,
  52. },
  53. {
  54. sort: { sort: 1 /* boards default sorting */ },
  55. },
  56. );
  57. return ret;
  58. },
  59. toggleForgotPassword() {
  60. this.setLoading(true);
  61. const forgotPasswordClosed = ReactiveCache.getCurrentSetting().disableForgotPassword;
  62. Settings.update(ReactiveCache.getCurrentSetting()._id, {
  63. $set: { disableForgotPassword: !forgotPasswordClosed },
  64. });
  65. this.setLoading(false);
  66. },
  67. toggleRegistration() {
  68. this.setLoading(true);
  69. const registrationClosed = ReactiveCache.getCurrentSetting().disableRegistration;
  70. Settings.update(ReactiveCache.getCurrentSetting()._id, {
  71. $set: { disableRegistration: !registrationClosed },
  72. });
  73. this.setLoading(false);
  74. if (registrationClosed) {
  75. $('.invite-people').slideUp();
  76. } else {
  77. $('.invite-people').slideDown();
  78. }
  79. },
  80. toggleTLS() {
  81. $('#mail-server-tls').toggleClass('is-checked');
  82. },
  83. toggleHideLogo() {
  84. $('#hide-logo').toggleClass('is-checked');
  85. },
  86. toggleHideCardCounterList() {
  87. $('#hide-card-counter-list').toggleClass('is-checked');
  88. },
  89. toggleHideBoardMemberList() {
  90. $('#hide-board-member-list').toggleClass('is-checked');
  91. },
  92. toggleAccessibilityPageEnabled() {
  93. $('#accessibility-page-enabled').toggleClass('is-checked');
  94. },
  95. toggleDisplayAuthenticationMethod() {
  96. $('#display-authentication-method').toggleClass('is-checked');
  97. },
  98. switchAttachmentTab(event) {
  99. event.preventDefault();
  100. const target = $(event.target);
  101. const targetID = target.data('id');
  102. // Update active tab
  103. $('.tab-nav li.active').removeClass('active');
  104. target.parent().addClass('active');
  105. // Call the attachment settings component method if available
  106. if (window.attachmentSettings && window.attachmentSettings.switchMenu) {
  107. window.attachmentSettings.switchMenu(event, targetID);
  108. }
  109. },
  110. switchCronTab(event) {
  111. event.preventDefault();
  112. const target = $(event.target);
  113. const targetID = target.data('id');
  114. // Update active tab
  115. $('.tab-nav li.active').removeClass('active');
  116. target.parent().addClass('active');
  117. // Call the cron settings template method if available
  118. const cronTemplate = Template.instance();
  119. if (cronTemplate && cronTemplate.switchMenu) {
  120. cronTemplate.switchMenu(event, targetID);
  121. }
  122. },
  123. initializeAttachmentSubMenu() {
  124. // Set default sub-menu state for attachment settings
  125. // This will be handled by the attachment settings component
  126. console.log('Initializing attachment sub-menu');
  127. },
  128. initializeCronSubMenu() {
  129. // Set default sub-menu state for cron settings
  130. // This will be handled by the cron settings template
  131. console.log('Initializing cron sub-menu');
  132. },
  133. switchMenu(event) {
  134. const target = $(event.target);
  135. if (!target.hasClass('active')) {
  136. $('.side-menu li.active').removeClass('active');
  137. target.parent().addClass('active');
  138. const targetID = target.data('id');
  139. // Reset all settings to false
  140. this.forgotPasswordSetting.set(false);
  141. this.generalSetting.set(false);
  142. this.emailSetting.set(false);
  143. this.accountSetting.set(false);
  144. this.tableVisibilityModeSetting.set(false);
  145. this.announcementSetting.set(false);
  146. this.accessibilitySetting.set(false);
  147. this.layoutSetting.set(false);
  148. this.webhookSetting.set(false);
  149. this.attachmentSettings.set(false);
  150. this.cronSettings.set(false);
  151. // Set the selected setting to true
  152. if (targetID === 'registration-setting') {
  153. this.generalSetting.set(true);
  154. } else if (targetID === 'email-setting') {
  155. this.emailSetting.set(true);
  156. } else if (targetID === 'account-setting') {
  157. this.accountSetting.set(true);
  158. } else if (targetID === 'tableVisibilityMode-setting') {
  159. this.tableVisibilityModeSetting.set(true);
  160. } else if (targetID === 'announcement-setting') {
  161. this.announcementSetting.set(true);
  162. } else if (targetID === 'accessibility-setting') {
  163. this.accessibilitySetting.set(true);
  164. } else if (targetID === 'layout-setting') {
  165. this.layoutSetting.set(true);
  166. } else if (targetID === 'webhook-setting') {
  167. this.webhookSetting.set(true);
  168. } else if (targetID === 'attachment-settings') {
  169. this.attachmentSettings.set(true);
  170. this.initializeAttachmentSubMenu();
  171. } else if (targetID === 'cron-settings') {
  172. this.cronSettings.set(true);
  173. this.initializeCronSubMenu();
  174. }
  175. }
  176. },
  177. checkBoard(event) {
  178. let target = $(event.target);
  179. if (!target.hasClass('js-toggle-board-choose')) {
  180. target = target.parent();
  181. }
  182. const checkboxId = target.attr('id');
  183. $(`#${checkboxId} .materialCheckBox`).toggleClass('is-checked');
  184. $(`#${checkboxId}`).toggleClass('is-checked');
  185. },
  186. inviteThroughEmail() {
  187. const emails = $('#email-to-invite')
  188. .val()
  189. .toLowerCase()
  190. .trim()
  191. .split('\n')
  192. .join(',')
  193. .split(',');
  194. const boardsToInvite = [];
  195. $('.js-toggle-board-choose .materialCheckBox.is-checked').each(function() {
  196. boardsToInvite.push($(this).data('id'));
  197. });
  198. const validEmails = [];
  199. emails.forEach(email => {
  200. if (email && SimpleSchema.RegEx.Email.test(email.trim())) {
  201. validEmails.push(email.trim());
  202. }
  203. });
  204. if (validEmails.length) {
  205. this.setLoading(true);
  206. Meteor.call('sendInvitation', validEmails, boardsToInvite, () => {
  207. // if (!err) {
  208. // TODO - show more info to user
  209. // }
  210. this.setLoading(false);
  211. });
  212. }
  213. },
  214. saveMailServerInfo() {
  215. this.setLoading(true);
  216. $('li').removeClass('has-error');
  217. try {
  218. const host = this.checkField('#mail-server-host');
  219. const port = this.checkField('#mail-server-port');
  220. const username = $('#mail-server-username')
  221. .val()
  222. .trim();
  223. const password = $('#mail-server-password')
  224. .val()
  225. .trim();
  226. const from = this.checkField('#mail-server-from');
  227. const tls = $('#mail-server-tls.is-checked').length > 0;
  228. Settings.update(ReactiveCache.getCurrentSetting()._id, {
  229. $set: {
  230. 'mailServer.host': host,
  231. 'mailServer.port': port,
  232. 'mailServer.username': username,
  233. 'mailServer.password': password,
  234. 'mailServer.enableTLS': tls,
  235. 'mailServer.from': from,
  236. },
  237. });
  238. } catch (e) {
  239. return;
  240. } finally {
  241. this.setLoading(false);
  242. }
  243. },
  244. saveLayout() {
  245. this.setLoading(true);
  246. $('li').removeClass('has-error');
  247. const productName = ($('#product-name').val() || '').trim();
  248. const customLoginLogoImageUrl = ($('#custom-login-logo-image-url').val() || '').trim();
  249. const customLoginLogoLinkUrl = ($('#custom-login-logo-link-url').val() || '').trim();
  250. const customHelpLinkUrl = ($('#custom-help-link-url').val() || '').trim();
  251. const textBelowCustomLoginLogo = ($('#text-below-custom-login-logo').val() || '').trim();
  252. const automaticLinkedUrlSchemes = ($('#automatic-linked-url-schemes').val() || '').trim();
  253. const customTopLeftCornerLogoImageUrl = ($('#custom-top-left-corner-logo-image-url').val() || '').trim();
  254. const customTopLeftCornerLogoLinkUrl = ($('#custom-top-left-corner-logo-link-url').val() || '').trim();
  255. const customTopLeftCornerLogoHeight = ($('#custom-top-left-corner-logo-height').val() || '').trim();
  256. const oidcBtnText = ($('#oidcBtnTextvalue').val() || '').trim();
  257. const mailDomainName = ($('#mailDomainNamevalue').val() || '').trim();
  258. const legalNotice = ($('#legalNoticevalue').val() || '').trim();
  259. const hideLogoChange = $('input[name=hideLogo]:checked').val() === 'true';
  260. const hideCardCounterListChange = $('input[name=hideCardCounterList]:checked').val() === 'true';
  261. const hideBoardMemberListChange = $('input[name=hideBoardMemberList]:checked').val() === 'true';
  262. const displayAuthenticationMethod =
  263. $('input[name=displayAuthenticationMethod]:checked').val() === 'true';
  264. const defaultAuthenticationMethod = $('#defaultAuthenticationMethod').val();
  265. const accessibilityPageEnabled = $('input[name=accessibilityPageEnabled]:checked').val() === 'true';
  266. const accessibilityTitle = ($('#accessibility-title').val() || '').trim();
  267. const accessibilityContent = ($('#accessibility-content').val() || '').trim();
  268. const spinnerName = ($('#spinnerName').val() || '').trim();
  269. try {
  270. Settings.update(ReactiveCache.getCurrentSetting()._id, {
  271. $set: {
  272. productName,
  273. hideLogo: hideLogoChange,
  274. hideCardCounterList: hideCardCounterListChange,
  275. hideBoardMemberList: hideBoardMemberListChange,
  276. customLoginLogoImageUrl,
  277. customLoginLogoLinkUrl,
  278. customHelpLinkUrl,
  279. textBelowCustomLoginLogo,
  280. customTopLeftCornerLogoImageUrl,
  281. customTopLeftCornerLogoLinkUrl,
  282. customTopLeftCornerLogoHeight,
  283. displayAuthenticationMethod,
  284. defaultAuthenticationMethod,
  285. automaticLinkedUrlSchemes,
  286. spinnerName,
  287. oidcBtnText,
  288. mailDomainName,
  289. legalNotice,
  290. accessibilityPageEnabled,
  291. accessibilityTitle,
  292. accessibilityContent,
  293. },
  294. });
  295. } catch (e) {
  296. return;
  297. } finally {
  298. this.setLoading(false);
  299. }
  300. DocHead.setTitle(productName);
  301. },
  302. sendSMTPTestEmail() {
  303. Meteor.call('sendSMTPTestEmail', (err, ret) => {
  304. if (!err && ret) {
  305. const message = `${TAPi18n.__(ret.message)}: ${ret.email}`;
  306. alert(message);
  307. } else {
  308. const reason = err.reason || '';
  309. const message = `${TAPi18n.__(err.error)}\n${reason}`;
  310. alert(message);
  311. }
  312. });
  313. },
  314. events() {
  315. return [
  316. {
  317. 'click a.js-toggle-forgot-password': this.toggleForgotPassword,
  318. 'click a.js-toggle-registration': this.toggleRegistration,
  319. 'click a.js-toggle-tls': this.toggleTLS,
  320. 'click a.js-setting-menu': this.switchMenu,
  321. 'click a.js-toggle-board-choose': this.checkBoard,
  322. 'click button.js-email-invite': this.inviteThroughEmail,
  323. 'click button.js-save': this.saveMailServerInfo,
  324. 'click button.js-send-smtp-test-email': this.sendSMTPTestEmail,
  325. 'click a.js-toggle-hide-logo': this.toggleHideLogo,
  326. 'click a.js-toggle-hide-card-counter-list': this.toggleHideCardCounterList,
  327. 'click a.js-toggle-hide-board-member-list': this.toggleHideBoardMemberList,
  328. 'click button.js-save-layout': this.saveLayout,
  329. 'click a.js-toggle-display-authentication-method': this
  330. .toggleDisplayAuthenticationMethod,
  331. 'click a.js-attachment-storage-settings': this.switchAttachmentTab,
  332. 'click a.js-attachment-migration': this.switchAttachmentTab,
  333. 'click a.js-attachment-monitoring': this.switchAttachmentTab,
  334. 'click a.js-cron-migrations': this.switchCronTab,
  335. 'click a.js-cron-board-operations': this.switchCronTab,
  336. 'click a.js-cron-jobs': this.switchCronTab,
  337. 'click a.js-cron-add': this.switchCronTab,
  338. },
  339. ];
  340. },
  341. }).register('setting');
  342. BlazeComponent.extendComponent({
  343. saveAccountsChange() {
  344. const allowEmailChange =
  345. $('input[name=allowEmailChange]:checked').val() === 'true';
  346. const allowUserNameChange =
  347. $('input[name=allowUserNameChange]:checked').val() === 'true';
  348. const allowUserDelete =
  349. $('input[name=allowUserDelete]:checked').val() === 'true';
  350. AccountSettings.update('accounts-allowEmailChange', {
  351. $set: { booleanValue: allowEmailChange },
  352. });
  353. AccountSettings.update('accounts-allowUserNameChange', {
  354. $set: { booleanValue: allowUserNameChange },
  355. });
  356. AccountSettings.update('accounts-allowUserDelete', {
  357. $set: { booleanValue: allowUserDelete },
  358. });
  359. },
  360. // Brute force lockout settings method moved to lockedUsersBody.js
  361. allowEmailChange() {
  362. return AccountSettings.findOne('accounts-allowEmailChange')?.booleanValue || false;
  363. },
  364. allowUserNameChange() {
  365. return AccountSettings.findOne('accounts-allowUserNameChange')?.booleanValue || false;
  366. },
  367. allowUserDelete() {
  368. return AccountSettings.findOne('accounts-allowUserDelete')?.booleanValue || false;
  369. },
  370. // Lockout settings helper methods moved to lockedUsersBody.js
  371. allBoardsHideActivities() {
  372. Meteor.call('setAllBoardsHideActivities', (err, ret) => {
  373. if (!err && ret) {
  374. if (ret === true) {
  375. const message = `${TAPi18n.__(
  376. 'now-activities-of-all-boards-are-hidden',
  377. )}`;
  378. alert(message);
  379. }
  380. } else {
  381. const reason = err.reason || '';
  382. const message = `${TAPi18n.__(err.error)}\n${reason}`;
  383. alert(message);
  384. }
  385. });
  386. },
  387. events() {
  388. return [
  389. {
  390. 'click button.js-accounts-save': this.saveAccountsChange,
  391. },
  392. {
  393. 'click button.js-all-boards-hide-activities': this.allBoardsHideActivities,
  394. },
  395. ];
  396. },
  397. }).register('accountSettings');
  398. BlazeComponent.extendComponent({
  399. saveTableVisibilityChange() {
  400. const allowPrivateOnly =
  401. $('input[name=allowPrivateOnly]:checked').val() === 'true';
  402. TableVisibilityModeSettings.update('tableVisibilityMode-allowPrivateOnly', {
  403. $set: { booleanValue: allowPrivateOnly },
  404. });
  405. },
  406. allowPrivateOnly() {
  407. return TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly').booleanValue;
  408. },
  409. allBoardsHideActivities() {
  410. Meteor.call('setAllBoardsHideActivities', (err, ret) => {
  411. if (!err && ret) {
  412. if (ret === true) {
  413. const message = `${TAPi18n.__(
  414. 'now-activities-of-all-boards-are-hidden',
  415. )}`;
  416. alert(message);
  417. }
  418. } else {
  419. const reason = err.reason || '';
  420. const message = `${TAPi18n.__(err.error)}\n${reason}`;
  421. alert(message);
  422. }
  423. });
  424. },
  425. events() {
  426. return [
  427. {
  428. 'click button.js-tableVisibilityMode-save': this.saveTableVisibilityChange,
  429. },
  430. {
  431. 'click button.js-all-boards-hide-activities': this.allBoardsHideActivities,
  432. },
  433. ];
  434. },
  435. }).register('tableVisibilityModeSettings');
  436. BlazeComponent.extendComponent({
  437. onCreated() {
  438. this.loading = new ReactiveVar(false);
  439. },
  440. setLoading(w) {
  441. this.loading.set(w);
  442. },
  443. currentAnnouncements() {
  444. return Announcements.findOne();
  445. },
  446. saveMessage() {
  447. const message = $('#admin-announcement')
  448. .val()
  449. .trim();
  450. Announcements.update(Announcements.findOne()._id, {
  451. $set: { body: message },
  452. });
  453. },
  454. toggleActive() {
  455. this.setLoading(true);
  456. const announcements = this.currentAnnouncements();
  457. const isActive = announcements.enabled;
  458. Announcements.update(announcements._id, {
  459. $set: { enabled: !isActive },
  460. });
  461. this.setLoading(false);
  462. if (isActive) {
  463. $('.admin-announcement').slideUp();
  464. } else {
  465. $('.admin-announcement').slideDown();
  466. }
  467. },
  468. events() {
  469. return [
  470. {
  471. 'click a.js-toggle-activemessage': this.toggleActive,
  472. 'click button.js-announcement-save': this.saveMessage,
  473. },
  474. ];
  475. },
  476. }).register('announcementSettings');
  477. BlazeComponent.extendComponent({
  478. onCreated() {
  479. this.loading = new ReactiveVar(false);
  480. },
  481. setLoading(w) {
  482. this.loading.set(w);
  483. },
  484. currentAccessibility() {
  485. return AccessibilitySettings.findOne();
  486. },
  487. saveAccessibility() {
  488. const title = $('#admin-accessibility-title')
  489. .val()
  490. .trim();
  491. const content = $('#admin-accessibility-content')
  492. .val()
  493. .trim();
  494. AccessibilitySettings.update(AccessibilitySettings.findOne()._id, {
  495. $set: {
  496. title: title,
  497. body: content
  498. },
  499. });
  500. },
  501. toggleAccessibility() {
  502. this.setLoading(true);
  503. const accessibilitySetting = this.currentAccessibility();
  504. const isActive = accessibilitySetting.enabled;
  505. AccessibilitySettings.update(accessibilitySetting._id, {
  506. $set: { enabled: !isActive },
  507. });
  508. this.setLoading(false);
  509. if (isActive) {
  510. $('.accessibility-content').slideUp();
  511. } else {
  512. $('.accessibility-content').slideDown();
  513. }
  514. },
  515. events() {
  516. return [
  517. {
  518. 'click a.js-toggle-accessibility': this.toggleAccessibility,
  519. 'click button.js-accessibility-save': this.saveAccessibility,
  520. },
  521. ];
  522. },
  523. }).register('accessibilitySettings');
  524. Template.selectAuthenticationMethod.onCreated(function() {
  525. this.authenticationMethods = new ReactiveVar([]);
  526. Meteor.call('getAuthenticationsEnabled', (_, result) => {
  527. if (result) {
  528. // TODO : add a management of different languages
  529. // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})
  530. this.authenticationMethods.set([
  531. { value: 'password' },
  532. // Gets only the authentication methods availables
  533. ...Object.entries(result)
  534. .filter(e => e[1])
  535. .map(e => ({ value: e[0] })),
  536. ]);
  537. }
  538. });
  539. });
  540. Template.selectAuthenticationMethod.helpers({
  541. authentications() {
  542. return Template.instance().authenticationMethods.get();
  543. },
  544. isSelected(match) {
  545. return Template.instance().data.authenticationMethod === match;
  546. },
  547. });
  548. Template.selectSpinnerName.helpers({
  549. spinners() {
  550. return ALLOWED_WAIT_SPINNERS;
  551. },
  552. isSelected(match) {
  553. return Template.instance().data.spinnerName === match;
  554. },
  555. });
  556. // Template helpers for the setting template
  557. Template.setting.helpers({
  558. generalSetting() {
  559. const instance = Template.instance();
  560. return instance.generalSetting && instance.generalSetting.get();
  561. },
  562. emailSetting() {
  563. const instance = Template.instance();
  564. return instance.emailSetting && instance.emailSetting.get();
  565. },
  566. accountSetting() {
  567. const instance = Template.instance();
  568. return instance.accountSetting && instance.accountSetting.get();
  569. },
  570. tableVisibilityModeSetting() {
  571. const instance = Template.instance();
  572. return instance.tableVisibilityModeSetting && instance.tableVisibilityModeSetting.get();
  573. },
  574. announcementSetting() {
  575. const instance = Template.instance();
  576. return instance.announcementSetting && instance.announcementSetting.get();
  577. },
  578. accessibilitySetting() {
  579. const instance = Template.instance();
  580. return instance.accessibilitySetting && instance.accessibilitySetting.get();
  581. },
  582. layoutSetting() {
  583. const instance = Template.instance();
  584. return instance.layoutSetting && instance.layoutSetting.get();
  585. },
  586. webhookSetting() {
  587. const instance = Template.instance();
  588. return instance.webhookSetting && instance.webhookSetting.get();
  589. },
  590. attachmentSettings() {
  591. const instance = Template.instance();
  592. return instance.attachmentSettings && instance.attachmentSettings.get();
  593. },
  594. cronSettings() {
  595. const instance = Template.instance();
  596. return instance.cronSettings && instance.cronSettings.get();
  597. },
  598. loading() {
  599. const instance = Template.instance();
  600. return instance.loading && instance.loading.get();
  601. },
  602. });