| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804 | import { ReactiveCache } from '/imports/reactiveCache';import { TAPi18n } from '/imports/i18n';import { ALLOWED_WAIT_SPINNERS } from '/config/const';import LockoutSettings from '/models/lockoutSettings';BlazeComponent.extendComponent({  onCreated() {    this.error = new ReactiveVar('');    this.loading = new ReactiveVar(false);    this.forgotPasswordSetting = new ReactiveVar(false);    this.generalSetting = new ReactiveVar(true);    this.emailSetting = new ReactiveVar(false);    this.accountSetting = new ReactiveVar(false);    this.tableVisibilityModeSetting = new ReactiveVar(false);    this.announcementSetting = new ReactiveVar(false);    this.accessibilitySetting = new ReactiveVar(false);    this.layoutSetting = new ReactiveVar(false);    this.webhookSetting = new ReactiveVar(false);    this.attachmentSettings = new ReactiveVar(false);    this.cronSettings = new ReactiveVar(false);    Meteor.subscribe('setting');    Meteor.subscribe('mailServer');    Meteor.subscribe('accountSettings');    Meteor.subscribe('tableVisibilityModeSettings');    Meteor.subscribe('announcements');    Meteor.subscribe('accessibilitySettings');    Meteor.subscribe('globalwebhooks');    Meteor.subscribe('lockoutSettings');  },  setError(error) {    this.error.set(error);  },    // Template helpers moved to BlazeComponent - using different names to avoid conflicts  isGeneralSetting() {    return this.generalSetting && this.generalSetting.get();  },  isEmailSetting() {    return this.emailSetting && this.emailSetting.get();  },  isAccountSetting() {    return this.accountSetting && this.accountSetting.get();  },  isTableVisibilityModeSetting() {    return this.tableVisibilityModeSetting && this.tableVisibilityModeSetting.get();  },  isAnnouncementSetting() {    return this.announcementSetting && this.announcementSetting.get();  },  isAccessibilitySetting() {    return this.accessibilitySetting && this.accessibilitySetting.get();  },  isLayoutSetting() {    return this.layoutSetting && this.layoutSetting.get();  },  isWebhookSetting() {    return this.webhookSetting && this.webhookSetting.get();  },  isAttachmentSettings() {    return this.attachmentSettings && this.attachmentSettings.get();  },  isCronSettings() {    return this.cronSettings && this.cronSettings.get();  },  isLoading() {    return this.loading && this.loading.get();  },  // Attachment settings helpers  filesystemPath() {    return process.env.WRITABLE_PATH || '/data';  },    attachmentsPath() {    const writablePath = process.env.WRITABLE_PATH || '/data';    return `${writablePath}/attachments`;  },    avatarsPath() {    const writablePath = process.env.WRITABLE_PATH || '/data';    return `${writablePath}/avatars`;  },    gridfsEnabled() {    return process.env.GRIDFS_ENABLED === 'true';  },    s3Enabled() {    return process.env.S3_ENABLED === 'true';  },    s3Endpoint() {    return process.env.S3_ENDPOINT || '';  },    s3Bucket() {    return process.env.S3_BUCKET || '';  },    s3Region() {    return process.env.S3_REGION || '';  },    s3SslEnabled() {    return process.env.S3_SSL_ENABLED === 'true';  },    s3Port() {    return process.env.S3_PORT || 443;  },  // Cron settings helpers  migrationStatus() {    return TAPi18n.__('idle'); // Placeholder  },    migrationProgress() {    return 0; // Placeholder  },    cronJobs() {    return []; // Placeholder  },  setLoading(w) {    this.loading.set(w);  },  // Event handlers for attachment settings  'click button.js-test-s3-connection'(event) {    event.preventDefault();    const secretKey = $('#s3-secret-key').val();    if (!secretKey) {      alert(TAPi18n.__('s3-secret-key-required'));      return;    }    Meteor.call('testS3Connection', { secretKey }, (error, result) => {      if (error) {        alert(TAPi18n.__('s3-connection-failed') + ': ' + error.reason);      } else {        alert(TAPi18n.__('s3-connection-success'));      }    });  },  'click button.js-save-s3-settings'(event) {    event.preventDefault();    const secretKey = $('#s3-secret-key').val();    if (!secretKey) {      alert(TAPi18n.__('s3-secret-key-required'));      return;    }    Meteor.call('saveS3Settings', { secretKey }, (error, result) => {      if (error) {        alert(TAPi18n.__('s3-settings-save-failed') + ': ' + error.reason);      } else {        alert(TAPi18n.__('s3-settings-saved'));        $('#s3-secret-key').val(''); // Clear the password field      }    });  },  // Event handlers for cron settings  'click button.js-start-all-migrations'(event) {    event.preventDefault();    Meteor.call('startAllMigrations', (error, result) => {      if (error) {        alert(TAPi18n.__('migration-start-failed') + ': ' + error.reason);      } else {        alert(TAPi18n.__('migration-started'));      }    });  },  'click button.js-pause-all-migrations'(event) {    event.preventDefault();    Meteor.call('pauseAllMigrations', (error, result) => {      if (error) {        alert(TAPi18n.__('migration-pause-failed') + ': ' + error.reason);      } else {        alert(TAPi18n.__('migration-paused'));      }    });  },  'click button.js-stop-all-migrations'(event) {    event.preventDefault();    if (confirm(TAPi18n.__('migration-stop-confirm'))) {      Meteor.call('stopAllMigrations', (error, result) => {        if (error) {          alert(TAPi18n.__('migration-stop-failed') + ': ' + error.reason);        } else {          alert(TAPi18n.__('migration-stopped'));        }      });    }  },  'click button.js-schedule-board-cleanup'(event) {    event.preventDefault();    Meteor.call('scheduleBoardCleanup', (error, result) => {      if (error) {        alert(TAPi18n.__('board-cleanup-failed') + ': ' + error.reason);      } else {        alert(TAPi18n.__('board-cleanup-scheduled'));      }    });  },  'click button.js-schedule-board-archive'(event) {    event.preventDefault();    Meteor.call('scheduleBoardArchive', (error, result) => {      if (error) {        alert(TAPi18n.__('board-archive-failed') + ': ' + error.reason);      } else {        alert(TAPi18n.__('board-archive-scheduled'));      }    });  },  'click button.js-schedule-board-backup'(event) {    event.preventDefault();    Meteor.call('scheduleBoardBackup', (error, result) => {      if (error) {        alert(TAPi18n.__('board-backup-failed') + ': ' + error.reason);      } else {        alert(TAPi18n.__('board-backup-scheduled'));      }    });  },  'click button.js-pause-job'(event) {    event.preventDefault();    const jobId = $(event.target).data('job-id');    Meteor.call('pauseCronJob', jobId, (error, result) => {      if (error) {        alert(TAPi18n.__('cron-job-pause-failed') + ': ' + error.reason);      } else {        alert(TAPi18n.__('cron-job-paused'));      }    });  },  'click button.js-delete-job'(event) {    event.preventDefault();    const jobId = $(event.target).data('job-id');    if (confirm(TAPi18n.__('cron-job-delete-confirm'))) {      Meteor.call('deleteCronJob', jobId, (error, result) => {        if (error) {          alert(TAPi18n.__('cron-job-delete-failed') + ': ' + error.reason);        } else {          alert(TAPi18n.__('cron-job-deleted'));        }      });    }  },  'click button.js-add-cron-job'(event) {    event.preventDefault();    // Placeholder for adding a new cron job (e.g., open a modal)    alert(TAPi18n.__('add-cron-job-placeholder'));  },  checkField(selector) {    const value = $(selector).val();    if (!value || value.trim() === '') {      $(selector)        .parents('li.smtp-form')        .addClass('has-error');      throw Error('blank field');    } else {      return value;    }  },  boards() {    const ret = ReactiveCache.getBoards(      {        archived: false,        'members.userId': Meteor.userId(),        'members.isAdmin': true,      },      {        sort: { sort: 1 /* boards default sorting */ },      },    );    return ret;  },  toggleForgotPassword() {    this.setLoading(true);    const forgotPasswordClosed = ReactiveCache.getCurrentSetting().disableForgotPassword;    Settings.update(ReactiveCache.getCurrentSetting()._id, {      $set: { disableForgotPassword: !forgotPasswordClosed },    });    this.setLoading(false);  },  toggleRegistration() {    this.setLoading(true);    const registrationClosed = ReactiveCache.getCurrentSetting().disableRegistration;    Settings.update(ReactiveCache.getCurrentSetting()._id, {      $set: { disableRegistration: !registrationClosed },    });    this.setLoading(false);    if (registrationClosed) {      $('.invite-people').slideUp();    } else {      $('.invite-people').slideDown();    }  },  toggleTLS() {    $('#mail-server-tls').toggleClass('is-checked');  },  toggleHideLogo() {    $('#hide-logo').toggleClass('is-checked');  },  toggleHideCardCounterList() {    $('#hide-card-counter-list').toggleClass('is-checked');  },  toggleHideBoardMemberList() {    $('#hide-board-member-list').toggleClass('is-checked');  },  toggleAccessibilityPageEnabled() {    $('#accessibility-page-enabled').toggleClass('is-checked');  },  toggleDisplayAuthenticationMethod() {    $('#display-authentication-method').toggleClass('is-checked');  },  initializeAttachmentSubMenu() {    // Set default sub-menu state for attachment settings    // This will be handled by the attachment settings component    console.log('Initializing attachment sub-menu');  },  initializeCronSubMenu() {    // Set default sub-menu state for cron settings    // This will be handled by the cron settings template    console.log('Initializing cron sub-menu');  },  switchMenu(event) {    const target = $(event.target);    if (!target.hasClass('active')) {      $('.side-menu li.active').removeClass('active');      target.parent().addClass('active');      const targetID = target.data('id');            // Reset all settings to false      this.forgotPasswordSetting.set(false);      this.generalSetting.set(false);      this.emailSetting.set(false);      this.accountSetting.set(false);      this.tableVisibilityModeSetting.set(false);      this.announcementSetting.set(false);      this.accessibilitySetting.set(false);      this.layoutSetting.set(false);      this.webhookSetting.set(false);      this.attachmentSettings.set(false);      this.cronSettings.set(false);            // Set the selected setting to true      if (targetID === 'registration-setting') {        this.generalSetting.set(true);      } else if (targetID === 'email-setting') {        this.emailSetting.set(true);      } else if (targetID === 'account-setting') {        this.accountSetting.set(true);      } else if (targetID === 'tableVisibilityMode-setting') {        this.tableVisibilityModeSetting.set(true);      } else if (targetID === 'announcement-setting') {        this.announcementSetting.set(true);      } else if (targetID === 'accessibility-setting') {        this.accessibilitySetting.set(true);      } else if (targetID === 'layout-setting') {        this.layoutSetting.set(true);      } else if (targetID === 'webhook-setting') {        this.webhookSetting.set(true);      } else if (targetID === 'attachment-settings') {        this.attachmentSettings.set(true);        this.initializeAttachmentSubMenu();      } else if (targetID === 'cron-settings') {        this.cronSettings.set(true);        this.initializeCronSubMenu();      }    }  },  checkBoard(event) {    let target = $(event.target);    if (!target.hasClass('js-toggle-board-choose')) {      target = target.parent();    }    const checkboxId = target.attr('id');    $(`#${checkboxId} .materialCheckBox`).toggleClass('is-checked');    $(`#${checkboxId}`).toggleClass('is-checked');  },  inviteThroughEmail() {    const emails = $('#email-to-invite')      .val()      .toLowerCase()      .trim()      .split('\n')      .join(',')      .split(',');    const boardsToInvite = [];    $('.js-toggle-board-choose .materialCheckBox.is-checked').each(function() {      boardsToInvite.push($(this).data('id'));    });    const validEmails = [];    emails.forEach(email => {      if (email && SimpleSchema.RegEx.Email.test(email.trim())) {        validEmails.push(email.trim());      }    });    if (validEmails.length) {      this.setLoading(true);      Meteor.call('sendInvitation', validEmails, boardsToInvite, () => {        // if (!err) {        //   TODO - show more info to user        // }        this.setLoading(false);      });    }  },  saveMailServerInfo() {    this.setLoading(true);    $('li').removeClass('has-error');    try {      const host = this.checkField('#mail-server-host');      const port = this.checkField('#mail-server-port');      const username = $('#mail-server-username')        .val()        .trim();      const password = $('#mail-server-password')        .val()        .trim();      const from = this.checkField('#mail-server-from');      const tls = $('#mail-server-tls.is-checked').length > 0;      Settings.update(ReactiveCache.getCurrentSetting()._id, {        $set: {          'mailServer.host': host,          'mailServer.port': port,          'mailServer.username': username,          'mailServer.password': password,          'mailServer.enableTLS': tls,          'mailServer.from': from,        },      });    } catch (e) {      return;    } finally {      this.setLoading(false);    }  },  saveLayout() {    this.setLoading(true);    $('li').removeClass('has-error');    const productName = ($('#product-name').val() || '').trim();    const customLoginLogoImageUrl = ($('#custom-login-logo-image-url').val() || '').trim();    const customLoginLogoLinkUrl = ($('#custom-login-logo-link-url').val() || '').trim();    const customHelpLinkUrl = ($('#custom-help-link-url').val() || '').trim();    const textBelowCustomLoginLogo = ($('#text-below-custom-login-logo').val() || '').trim();    const automaticLinkedUrlSchemes = ($('#automatic-linked-url-schemes').val() || '').trim();    const customTopLeftCornerLogoImageUrl = ($('#custom-top-left-corner-logo-image-url').val() || '').trim();    const customTopLeftCornerLogoLinkUrl = ($('#custom-top-left-corner-logo-link-url').val() || '').trim();    const customTopLeftCornerLogoHeight = ($('#custom-top-left-corner-logo-height').val() || '').trim();    const oidcBtnText = ($('#oidcBtnTextvalue').val() || '').trim();    const mailDomainName = ($('#mailDomainNamevalue').val() || '').trim();    const legalNotice = ($('#legalNoticevalue').val() || '').trim();    const hideLogoChange = $('input[name=hideLogo]:checked').val() === 'true';    const hideCardCounterListChange = $('input[name=hideCardCounterList]:checked').val() === 'true';    const hideBoardMemberListChange = $('input[name=hideBoardMemberList]:checked').val() === 'true';    const displayAuthenticationMethod =      $('input[name=displayAuthenticationMethod]:checked').val() === 'true';    const defaultAuthenticationMethod = $('#defaultAuthenticationMethod').val();    const spinnerName = ($('#spinnerName').val() || '').trim();    try {      Settings.update(ReactiveCache.getCurrentSetting()._id, {        $set: {          productName,          hideLogo: hideLogoChange,          hideCardCounterList: hideCardCounterListChange,          hideBoardMemberList: hideBoardMemberListChange,          customLoginLogoImageUrl,          customLoginLogoLinkUrl,          customHelpLinkUrl,          textBelowCustomLoginLogo,          customTopLeftCornerLogoImageUrl,          customTopLeftCornerLogoLinkUrl,          customTopLeftCornerLogoHeight,          displayAuthenticationMethod,          defaultAuthenticationMethod,          automaticLinkedUrlSchemes,          spinnerName,          oidcBtnText,          mailDomainName,          legalNotice,        },      });    } catch (e) {      return;    } finally {      this.setLoading(false);    }    DocHead.setTitle(productName);  },  sendSMTPTestEmail() {    Meteor.call('sendSMTPTestEmail', (err, ret) => {      if (!err && ret) {        const message = `${TAPi18n.__(ret.message)}: ${ret.email}`;        alert(message);      } else {        const reason = err.reason || '';        const message = `${TAPi18n.__(err.error)}\n${reason}`;        alert(message);      }    });  },  events() {    return [      {        'click a.js-toggle-forgot-password': this.toggleForgotPassword,        'click a.js-toggle-registration': this.toggleRegistration,        'click a.js-toggle-tls': this.toggleTLS,        'click a.js-setting-menu': this.switchMenu,        'click a.js-toggle-board-choose': this.checkBoard,        'click button.js-email-invite': this.inviteThroughEmail,        'click button.js-save': this.saveMailServerInfo,        'click button.js-send-smtp-test-email': this.sendSMTPTestEmail,        'click a.js-toggle-hide-logo': this.toggleHideLogo,        'click a.js-toggle-hide-card-counter-list': this.toggleHideCardCounterList,        'click a.js-toggle-hide-board-member-list': this.toggleHideBoardMemberList,        'click button.js-save-layout': this.saveLayout,        'click a.js-toggle-display-authentication-method': this          .toggleDisplayAuthenticationMethod,      },    ];  },}).register('setting');BlazeComponent.extendComponent({  saveAccountsChange() {    const allowEmailChange =      $('input[name=allowEmailChange]:checked').val() === 'true';    const allowUserNameChange =      $('input[name=allowUserNameChange]:checked').val() === 'true';    const allowUserDelete =      $('input[name=allowUserDelete]:checked').val() === 'true';    AccountSettings.update('accounts-allowEmailChange', {      $set: { booleanValue: allowEmailChange },    });    AccountSettings.update('accounts-allowUserNameChange', {      $set: { booleanValue: allowUserNameChange },    });    AccountSettings.update('accounts-allowUserDelete', {      $set: { booleanValue: allowUserDelete },    });  },  // Brute force lockout settings method moved to lockedUsersBody.js  allowEmailChange() {    return AccountSettings.findOne('accounts-allowEmailChange')?.booleanValue || false;  },  allowUserNameChange() {    return AccountSettings.findOne('accounts-allowUserNameChange')?.booleanValue || false;  },  allowUserDelete() {    return AccountSettings.findOne('accounts-allowUserDelete')?.booleanValue || false;  },  // Lockout settings helper methods moved to lockedUsersBody.js  allBoardsHideActivities() {    Meteor.call('setAllBoardsHideActivities', (err, ret) => {      if (!err && ret) {        if (ret === true) {          const message = `${TAPi18n.__(            'now-activities-of-all-boards-are-hidden',          )}`;          alert(message);        }      } else {        const reason = err.reason || '';        const message = `${TAPi18n.__(err.error)}\n${reason}`;        alert(message);      }    });  },  events() {    return [      {        'click button.js-accounts-save': this.saveAccountsChange,      },      {        'click button.js-all-boards-hide-activities': this.allBoardsHideActivities,      },    ];  },}).register('accountSettings');BlazeComponent.extendComponent({  saveTableVisibilityChange() {    const allowPrivateOnly =      $('input[name=allowPrivateOnly]:checked').val() === 'true';    TableVisibilityModeSettings.update('tableVisibilityMode-allowPrivateOnly', {      $set: { booleanValue: allowPrivateOnly },    });  },  allowPrivateOnly() {    return TableVisibilityModeSettings.findOne('tableVisibilityMode-allowPrivateOnly').booleanValue;  },  allBoardsHideActivities() {    Meteor.call('setAllBoardsHideActivities', (err, ret) => {      if (!err && ret) {        if (ret === true) {          const message = `${TAPi18n.__(            'now-activities-of-all-boards-are-hidden',          )}`;          alert(message);        }      } else {        const reason = err.reason || '';        const message = `${TAPi18n.__(err.error)}\n${reason}`;        alert(message);      }    });  },  events() {    return [      {        'click button.js-tableVisibilityMode-save': this.saveTableVisibilityChange,      },      {        'click button.js-all-boards-hide-activities': this.allBoardsHideActivities,      },    ];  },}).register('tableVisibilityModeSettings');BlazeComponent.extendComponent({  onCreated() {    this.loading = new ReactiveVar(false);  },  setLoading(w) {    this.loading.set(w);  },  currentAnnouncements() {    return Announcements.findOne();  },  saveMessage() {    const message = $('#admin-announcement')      .val()      .trim();    Announcements.update(Announcements.findOne()._id, {      $set: { body: message },    });  },  toggleActive() {    this.setLoading(true);    const announcements = this.currentAnnouncements();    const isActive = announcements.enabled;    Announcements.update(announcements._id, {      $set: { enabled: !isActive },    });    this.setLoading(false);    if (isActive) {      $('.admin-announcement').slideUp();    } else {      $('.admin-announcement').slideDown();    }  },  events() {    return [      {        'click a.js-toggle-activemessage': this.toggleActive,        'click button.js-announcement-save': this.saveMessage,      },    ];  },}).register('announcementSettings');BlazeComponent.extendComponent({  onCreated() {    this.loading = new ReactiveVar(false);  },  setLoading(w) {    this.loading.set(w);  },  currentAccessibility() {    return AccessibilitySettings.findOne();  },  saveAccessibility() {    this.setLoading(true);    const title = $('#admin-accessibility-title')      .val()      .trim();    const content = $('#admin-accessibility-content')      .val()      .trim();        try {      AccessibilitySettings.update(AccessibilitySettings.findOne()._id, {        $set: {          title: title,          body: content        },      });    } catch (e) {      console.error('Error saving accessibility settings:', e);      return;    } finally {      this.setLoading(false);    }  },  toggleAccessibility() {    this.setLoading(true);    const accessibilitySetting = this.currentAccessibility();    const isActive = accessibilitySetting.enabled;    AccessibilitySettings.update(accessibilitySetting._id, {      $set: { enabled: !isActive },    });    this.setLoading(false);    if (isActive) {      $('.accessibility-content').slideUp();    } else {      $('.accessibility-content').slideDown();    }  },  events() {    return [      {        'click a.js-toggle-accessibility': this.toggleAccessibility,        'click button.js-accessibility-save': this.saveAccessibility,      },    ];  },}).register('accessibilitySettings');Template.selectAuthenticationMethod.onCreated(function() {  this.authenticationMethods = new ReactiveVar([]);  Meteor.call('getAuthenticationsEnabled', (_, result) => {    if (result) {      // TODO : add a management of different languages      // (ex {value: ldap, text: TAPi18n.__('ldap', {}, T9n.getLanguage() || 'en')})      this.authenticationMethods.set([        { value: 'password' },        // Gets only the authentication methods availables        ...Object.entries(result)          .filter(e => e[1])          .map(e => ({ value: e[0] })),      ]);    }  });});Template.selectAuthenticationMethod.helpers({  authentications() {    return Template.instance().authenticationMethods.get();  },  isSelected(match) {    return Template.instance().data.authenticationMethod === match;  },});Template.selectSpinnerName.helpers({  spinners() {    return ALLOWED_WAIT_SPINNERS;  },  isSelected(match) {    return Template.instance().data.spinnerName === match;  },});
 |