diff --git a/app/cloud/client/admin/callback.html b/app/cloud/client/admin/callback.html
deleted file mode 100644
index 5d6c9c432f4c..000000000000
--- a/app/cloud/client/admin/callback.html
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
- {{> header sectionName="Cloud_connect"}}
-
- {{#requiresPermission 'manage-cloud'}}
- {{#if callbackError.error}}
-
{{_ "Cloud_error_in_authenticating"}}
-
-
{{_ "Cloud_error_code"}} {{ callbackError.errorCode }}
- {{/if}}
- {{/requiresPermission}}
-
-
-
-
diff --git a/app/cloud/client/admin/callback.js b/app/cloud/client/admin/callback.js
deleted file mode 100644
index de860026192e..000000000000
--- a/app/cloud/client/admin/callback.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import './callback.html';
-
-import { Meteor } from 'meteor/meteor';
-import { ReactiveVar } from 'meteor/reactive-var';
-import { Template } from 'meteor/templating';
-import { Tracker } from 'meteor/tracker';
-import { FlowRouter } from 'meteor/kadira:flow-router';
-import queryString from 'query-string';
-
-import { SideNav } from '../../../ui-utils/client';
-
-
-Template.cloudCallback.onCreated(function() {
- const instance = this;
-
- instance.loading = new ReactiveVar(true);
- instance.callbackError = new ReactiveVar({ error: false });
-
- const params = queryString.parse(location.search);
-
- if (params.error_code) {
- instance.callbackError.set({ error: true, errorCode: params.error_code });
- } else {
- Meteor.call('cloud:finishOAuthAuthorization', params.code, params.state, (error) => {
- if (error) {
- console.warn('cloud:finishOAuthAuthorization', error);
- return;
- }
-
- FlowRouter.go('/admin/cloud');
- });
- }
-});
-
-Template.cloudCallback.helpers({
- callbackError() {
- return Template.instance().callbackError.get();
- },
-});
-
-Template.cloudCallback.onRendered(() => {
- Tracker.afterFlush(() => {
- SideNav.setFlex('adminFlex');
- SideNav.openFlex();
- });
-});
diff --git a/app/cloud/client/admin/cloud.html b/app/cloud/client/admin/cloud.html
deleted file mode 100644
index 78c1e053390d..000000000000
--- a/app/cloud/client/admin/cloud.html
+++ /dev/null
@@ -1,145 +0,0 @@
-
-
-
- {{#header sectionName="Connectivity_Services" hideHelp=true fixedHeight=true fullpage=true}}
-
- {{/header}}
-
- {{#requiresPermission 'manage-cloud'}}
-
-
-
-
- {{_ "Cloud_what_is_it"}}
-
-
-
-
-
{{_ "Cloud_what_is_it_description"}}
-
-
-
-
-
{{_ "Cloud_what_is_it_services_like"}}
-
- {{_ "Register_Server_Registered_Push_Notifications"}}
- {{_ "Register_Server_Registered_Livechat"}}
- {{_ "Register_Server_Registered_OAuth"}}
- {{_ "Register_Server_Registered_Marketplace"}}
-
-
-
- {{_ "Cloud_what_is_it_additional"}}
-
-
-
-
- {{#if info.connectToCloud}}
- {{#if info.workspaceRegistered}}
-
-
{{_ "Cloud_workspace_connected"}}
-
-
-
-
-
{{_ "Cloud_workspace_disconnect"}}
-
-
- {{else}}
-
-
-
-
-
-
-
-
{{_ "Cloud_connect_support"}}: support@rocket.chat
-
- {{/if}}
- {{else}}
-
-
- {{_ "Cloud_registration_required"}}
-
-
-
-
{{_ "Cloud_registration_required_description"}}
-
{{_ "Cloud_registration_requried_link_text"}}
-
- {{/if}}
-
- {{#if info.connectToCloud}}
-
-
-
- {{_ "Cloud_troubleshooting"}}
-
-
-
-
-
{{_ "Cloud_workspace_support"}}
-
-
-
-
- {{_ "Cloud_status_page_description"}}:
status.rocket.chat
-
-
- {{/if}}
- {{/requiresPermission}}
-
-
-
-
diff --git a/app/cloud/client/admin/cloud.js b/app/cloud/client/admin/cloud.js
deleted file mode 100644
index cd9edf0a41a6..000000000000
--- a/app/cloud/client/admin/cloud.js
+++ /dev/null
@@ -1,233 +0,0 @@
-import './cloud.html';
-
-import { Meteor } from 'meteor/meteor';
-import { ReactiveVar } from 'meteor/reactive-var';
-import { Template } from 'meteor/templating';
-import { Tracker } from 'meteor/tracker';
-import queryString from 'query-string';
-import toastr from 'toastr';
-
-import { t } from '../../../utils';
-import { SideNav, modal } from '../../../ui-utils/client';
-
-
-Template.cloud.onCreated(function() {
- const instance = this;
- instance.info = new ReactiveVar();
- instance.loading = new ReactiveVar(true);
- instance.isLoggedIn = new ReactiveVar(false);
-
- instance.loadRegStatus = function _loadRegStatus() {
- Meteor.call('cloud:checkRegisterStatus', (error, info) => {
- if (error) {
- console.warn('cloud:checkRegisterStatus', error);
- return;
- }
-
- instance.info.set(info);
- instance.loading.set(false);
- });
- };
-
- instance.getLoggedIn = function _getLoggedIn() {
- Meteor.call('cloud:checkUserLoggedIn', (error, result) => {
- if (error) {
- console.warn(error);
- return;
- }
-
- instance.isLoggedIn.set(result);
- });
- };
-
- instance.oauthAuthorize = function _oauthAuthorize() {
- Meteor.call('cloud:getOAuthAuthorizationUrl', (error, url) => {
- if (error) {
- console.warn(error);
- return;
- }
-
- window.location.href = url;
- });
- };
-
- instance.logout = function _logout() {
- Meteor.call('cloud:logout', (error) => {
- if (error) {
- console.warn(error);
- return;
- }
-
- instance.getLoggedIn();
- });
- };
-
- instance.connectWorkspace = function _connectWorkspace(token) {
- Meteor.call('cloud:connectWorkspace', token, (error, success) => {
- if (error) {
- toastr.error(error);
- instance.loadRegStatus();
- return;
- }
-
- if (!success) {
- toastr.error('An error occured connecting');
- instance.loadRegStatus();
- return;
- }
-
- toastr.success(t('Connected'));
-
- instance.loadRegStatus();
- });
- };
-
- instance.disconnectWorkspace = function _disconnectWorkspace() {
- Meteor.call('cloud:disconnectWorkspace', (error, success) => {
- if (error) {
- toastr.error(error);
- instance.loadRegStatus();
- return;
- }
-
- if (!success) {
- toastr.error('An error occured disconnecting');
- instance.loadRegStatus();
- return;
- }
-
- toastr.success(t('Disconnected'));
-
- instance.loadRegStatus();
- });
- };
-
- instance.syncWorkspace = function _syncWorkspace() {
- Meteor.call('cloud:syncWorkspace', (error, success) => {
- if (error) {
- toastr.error(error);
- instance.loadRegStatus();
- return;
- }
-
- if (!success) {
- toastr.error('An error occured syncing');
- instance.loadRegStatus();
- return;
- }
-
- toastr.success(t('Sync Complete'));
-
- instance.loadRegStatus();
- });
- };
-
- instance.registerWorkspace = function _registerWorkspace() {
- Meteor.call('cloud:registerWorkspace', (error, success) => {
- if (error) {
- toastr.error(error);
- instance.loadRegStatus();
- return;
- }
-
- if (!success) {
- toastr.error('An error occured');
- instance.loadRegStatus();
- return;
- }
-
- return instance.syncWorkspace();
- });
- };
-
- const params = queryString.parse(location.search);
-
- if (params.token) {
- instance.connectWorkspace(params.token);
- } else {
- instance.loadRegStatus();
- }
-
- instance.getLoggedIn();
-});
-
-Template.cloud.helpers({
- info() {
- return Template.instance().info.get();
- },
- isLoggedIn() {
- return Template.instance().isLoggedIn.get();
- },
-});
-
-Template.cloud.events({
- 'click .js-register'() {
- modal.open({
- template: 'cloudRegisterManually',
- showCancelButton: false,
- showConfirmButton: false,
- showFooter: false,
- closeOnCancel: true,
- html: true,
- confirmOnEnter: false,
- });
- },
- 'click .update-email-btn'() {
- const val = $('input[name=cloudEmail]').val();
-
- Meteor.call('cloud:updateEmail', val, false, (error) => {
- if (error) {
- console.warn(error);
- return;
- }
-
- toastr.success(t('Saved'));
- });
- },
-
- 'click .resend-email-btn'() {
- const val = $('input[name=cloudEmail]').val();
-
- Meteor.call('cloud:updateEmail', val, true, (error) => {
- if (error) {
- console.warn(error);
- return;
- }
-
- toastr.success(t('Requested'));
- });
- },
-
- 'click .login-btn'(e, i) {
- i.oauthAuthorize();
- },
-
- 'click .logout-btn'(e, i) {
- i.logout();
- },
-
- 'click .connect-btn'(e, i) {
- const token = $('input[name=cloudToken]').val();
-
- i.connectWorkspace(token);
- },
-
- 'click .register-btn'(e, i) {
- i.registerWorkspace();
- },
-
- 'click .disconnect-btn'(e, i) {
- i.disconnectWorkspace();
- },
-
- 'click .sync-btn'(e, i) {
- i.syncWorkspace();
- },
-});
-
-Template.cloud.onRendered(() => {
- Tracker.afterFlush(() => {
- SideNav.setFlex('adminFlex');
- SideNav.openFlex();
- });
-});
diff --git a/app/cloud/client/admin/cloudRegisterManually.css b/app/cloud/client/admin/cloudRegisterManually.css
deleted file mode 100644
index 8cd3c5628e34..000000000000
--- a/app/cloud/client/admin/cloudRegisterManually.css
+++ /dev/null
@@ -1,26 +0,0 @@
-.rc-promtp {
- display: flex;
-
- min-height: 188px;
- padding: 1rem;
-
- border-radius: 2px;
- background: #2f343d;
- flex-flow: column wrap;
- justify-content: space-between;
-
- &--element,
- &--element[disabled] {
- flex: 1 1 auto;
-
- resize: none;
-
- color: #cbced1;
- border: none;
- background: none;
-
- font-family: Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
- font-size: 14px;
- line-height: 20px;
- }
-}
diff --git a/app/cloud/client/admin/cloudRegisterManually.html b/app/cloud/client/admin/cloudRegisterManually.html
deleted file mode 100644
index 738a36423501..000000000000
--- a/app/cloud/client/admin/cloudRegisterManually.html
+++ /dev/null
@@ -1,36 +0,0 @@
-
- {{> header sectionName="Cloud_Register_manually" hideHelp=true fullpage=true}}
- {{# if copyStep }}
-
-
-
-
- {{else}}
-
-
- {{_ "Cloud_register_offline_finish_helper"}}
-
-
-
-
-
-
- {{/if}}
-
diff --git a/app/cloud/client/admin/cloudRegisterManually.js b/app/cloud/client/admin/cloudRegisterManually.js
deleted file mode 100644
index 223d9bc609ce..000000000000
--- a/app/cloud/client/admin/cloudRegisterManually.js
+++ /dev/null
@@ -1,106 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-import { ReactiveDict } from 'meteor/reactive-dict';
-import { ReactiveVar } from 'meteor/reactive-var';
-import { Template } from 'meteor/templating';
-import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
-import Clipboard from 'clipboard';
-import toastr from 'toastr';
-
-import { APIClient } from '../../../utils/client';
-import { modal } from '../../../ui-utils/client';
-
-import './cloudRegisterManually.html';
-import './cloudRegisterManually.css';
-
-const CLOUD_STEPS = {
- COPY: 0,
- PASTE: 1,
- DONE: 2,
- ERROR: 3,
-};
-
-Template.cloudRegisterManually.events({
- 'submit form'(e) {
- e.preventDefault();
- },
- 'input .js-cloud-key'(e, instance) {
- instance.state.set('cloudKey', e.currentTarget.value);
- },
- 'click .js-next'(event, instance) {
- instance.state.set('step', CLOUD_STEPS.PASTE);
- },
- 'click .js-back'(event, instance) {
- instance.state.set('step', CLOUD_STEPS.COPY);
- },
- 'click .js-finish'(event, instance) {
- instance.state.set('loading', true);
-
- APIClient
- .post('v1/cloud.manualRegister', {}, { cloudBlob: instance.state.get('cloudKey') })
- .then(() => modal.open({
- type: 'success',
- title: TAPi18n.__('Success'),
- text: TAPi18n.__('Cloud_register_success'),
- confirmButtonText: TAPi18n.__('Ok'),
- closeOnConfirm: false,
- showCancelButton: false,
- }, () => window.location.reload()))
- .catch(() => modal.open({
- type: 'error',
- title: TAPi18n.__('Error'),
- text: TAPi18n.__('Cloud_register_error'),
- }))
- .then(() => instance.state.set('loading', false));
- },
-});
-
-Template.cloudRegisterManually.helpers({
- cloudLink() {
- return Template.instance().cloudLink.get();
- },
- copyStep() {
- return Template.instance().state.get('step') === CLOUD_STEPS.COPY;
- },
- clientKey() {
- return Template.instance().state.get('clientKey');
- },
- isLoading() {
- return Template.instance().state.get('loading');
- },
- step() {
- return Template.instance().state.get('step');
- },
- disabled() {
- const { state } = Template.instance();
-
- const shouldDisable = state.get('cloudKey').trim().length === 0 || state.get('loading');
-
- return shouldDisable && 'disabled';
- },
-});
-
-Template.cloudRegisterManually.onRendered(function() {
- const clipboard = new Clipboard('.js-copy');
- clipboard.on('success', function() {
- toastr.success(TAPi18n.__('Copied'));
- });
-
- const btn = this.find('.cloud-console-btn');
- // After_copy_the_text_go_to_cloud
- this.cloudLink.set(TAPi18n.__('Cloud_click_here').replace(/(\[(.*)\]\(\))/ig, (_, __, text) => btn.outerHTML.replace('', `${ text }`)));
-});
-
-Template.cloudRegisterManually.onCreated(function() {
- this.cloudLink = new ReactiveVar();
- this.state = new ReactiveDict({
- step: CLOUD_STEPS.COPY,
- loading: false,
- clientKey: '',
- cloudKey: '',
- error: '',
- });
-
- Meteor.call('cloud:getWorkspaceRegisterData', (error, result) => {
- this.state.set('clientKey', result);
- });
-});
diff --git a/app/cloud/client/admin/index.js b/app/cloud/client/admin/index.js
deleted file mode 100644
index 92cb6032c24e..000000000000
--- a/app/cloud/client/admin/index.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import './cloud';
-import './callback';
diff --git a/app/cloud/client/index.js b/app/cloud/client/index.js
deleted file mode 100644
index ce6ceecd3d5c..000000000000
--- a/app/cloud/client/index.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import './admin/callback';
-import './admin/cloud';
-import './admin/cloudRegisterManually';
-
-import { BlazeLayout } from 'meteor/kadira:blaze-layout';
-
-import { registerAdminRoute, registerAdminSidebarItem } from '../../../client/admin';
-import { hasAtLeastOnePermission } from '../../authorization';
-
-registerAdminRoute('/cloud', {
- name: 'cloud',
- async action() {
- await import('./admin');
- BlazeLayout.render('main', { center: 'cloud', old: true });
- },
-});
-
-registerAdminRoute('/cloud/oauth-callback', {
- name: 'cloud-oauth-callback',
- async action() {
- await import('./admin');
- BlazeLayout.render('main', { center: 'cloudCallback', old: true });
- },
-});
-
-registerAdminSidebarItem({
- icon: 'cloud-plus',
- href: 'cloud',
- i18nLabel: 'Connectivity_Services',
- permissionGranted() {
- return hasAtLeastOnePermission(['manage-cloud']);
- },
-});
diff --git a/app/integrations/client/getIntegration.js b/app/integrations/client/getIntegration.js
deleted file mode 100644
index d80c8ee690e5..000000000000
--- a/app/integrations/client/getIntegration.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
-import { FlowRouter } from 'meteor/kadira:flow-router';
-import toastr from 'toastr';
-
-import { hasAllPermission } from '../../authorization/client';
-import { APIClient } from '../../utils/client';
-
-export async function getIntegration(integrationId, uid) {
- if (!integrationId) {
- return;
- }
-
- const reqParams = {
- integrationId,
- };
-
- if (!hasAllPermission('manage-outgoing-integrations')) {
- if (!hasAllPermission('manage-own-outgoing-integrations')) {
- toastr.error(TAPi18n.__('No_integration_found'));
- FlowRouter.go('admin-integrations');
- return;
- }
- reqParams.createdBy = uid;
- }
-
- try {
- const { integration } = await APIClient.v1.get('integrations.get', reqParams);
-
- return integration;
- } catch (e) {
- toastr.error(TAPi18n.__('Error'));
- console.error(e);
- }
-}
diff --git a/app/integrations/client/index.js b/app/integrations/client/index.js
deleted file mode 100644
index f7c2445d187d..000000000000
--- a/app/integrations/client/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import '../lib/rocketchat';
-import './startup';
-import './route';
diff --git a/app/integrations/client/route.js b/app/integrations/client/route.js
deleted file mode 100644
index 08a7eb444653..000000000000
--- a/app/integrations/client/route.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import { BlazeLayout } from 'meteor/kadira:blaze-layout';
-
-import { registerAdminRoute } from '../../../client/admin';
-import { t } from '../../utils';
-
-const dynamic = () => import('./views');
-
-registerAdminRoute('/integrations', {
- name: 'admin-integrations',
- async action() {
- await dynamic();
- return BlazeLayout.render('main', {
- center: 'integrations',
- pageTitle: t('Integrations'),
- });
- },
-});
-
-registerAdminRoute('/integrations/new', {
- name: 'admin-integrations-new',
- async action() {
- await dynamic();
- return BlazeLayout.render('main', {
- center: 'integrationsNew',
- pageTitle: t('Integration_New'),
- });
- },
-});
-
-registerAdminRoute('/integrations/incoming/:id?', {
- name: 'admin-integrations-incoming',
- async action(params) {
- await dynamic();
- return BlazeLayout.render('main', {
- center: 'pageSettingsContainer',
- pageTitle: t('Integration_Incoming_WebHook'),
- pageTemplate: 'integrationsIncoming',
- params,
- });
- },
-});
-
-registerAdminRoute('/integrations/outgoing/:id?', {
- name: 'admin-integrations-outgoing',
- async action(params) {
- await dynamic();
- return BlazeLayout.render('main', {
- center: 'integrationsOutgoing',
- pageTitle: t('Integration_Outgoing_WebHook'),
- params,
- });
- },
-});
-
-registerAdminRoute('/integrations/outgoing/:id?/history', {
- name: 'admin-integrations-outgoing-history',
- async action(params) {
- await dynamic();
- return BlazeLayout.render('main', {
- center: 'integrationsOutgoingHistory',
- pageTitle: t('Integration_Outgoing_WebHook_History'),
- params,
- });
- },
-});
-
-registerAdminRoute('/integrations/additional/zapier', {
- name: 'admin-integrations-additional-zapier',
- async action() {
- await dynamic();
- BlazeLayout.render('main', {
- center: 'integrationsAdditionalZapier',
- });
- },
-});
diff --git a/app/integrations/client/streamer.js b/app/integrations/client/streamer.js
deleted file mode 100644
index 6bc2d6dc1327..000000000000
--- a/app/integrations/client/streamer.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-
-export const integrationHistoryStreamer = new Meteor.Streamer('integrationHistory');
diff --git a/app/integrations/client/stylesheets/integrations.css b/app/integrations/client/stylesheets/integrations.css
deleted file mode 100644
index 83ee9e64cfe6..000000000000
--- a/app/integrations/client/stylesheets/integrations.css
+++ /dev/null
@@ -1,75 +0,0 @@
-.admin-integrations-new-panel {
- & .admin-integrations-new-item {
- display: flex;
-
- padding: 20px 10px;
-
- cursor: pointer;
-
- color: #444444;
- border-bottom: 1px solid #dddddd;
- align-items: center;
-
- &:hover {
- background-color: #fafafa;
- }
-
- & > i {
- color: #aaaaaa;
-
- font-size: 2rem;
- }
-
- & .admin-integrations-new-item-body {
- display: flex;
- flex-direction: column;
-
- padding: 0 20px;
- flex-grow: 1;
- }
-
- & .admin-integrations-new-item-title {
- font-size: 1.4rem;
- font-weight: 500;
- line-height: 2.1rem;
- }
-
- & .admin-integrations-new-item-description {
- color: #aaaaaa;
-
- font-size: 1rem;
- line-height: 1.5rem;
- }
- }
-
- & > a:last-child > .admin-integrations-new-item {
- border-bottom: none;
- }
-}
-
-.message-example {
- & li {
- list-style: none;
- }
-}
-
-.integrate-other-ways {
- & p {
- font-size: 1rem;
- line-height: 1.5rem;
-
- & a {
- color: #175cc4 !important;
- }
- }
-}
-
-.content.zapier {
- display: flex;
- flex-direction: column;
- align-items: center;
-
- #zapier-goes-here {
- width: 95%;
- }
-}
diff --git a/app/integrations/client/views/additional/zapier.html b/app/integrations/client/views/additional/zapier.html
deleted file mode 100644
index d95f0ac50691..000000000000
--- a/app/integrations/client/views/additional/zapier.html
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- {{> header sectionName="Zapier" hideHelp=true fixedHeight=true}}
- {{_ "Back_to_integrations"}}
-
-
-
-
diff --git a/app/integrations/client/views/index.js b/app/integrations/client/views/index.js
deleted file mode 100644
index f7c6282cbd35..000000000000
--- a/app/integrations/client/views/index.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import './integrations.html';
-import './integrations';
-import './integrationsNew.html';
-import './integrationsNew';
-import './integrationsIncoming.html';
-import './integrationsIncoming';
-import './integrationsOutgoing.html';
-import './integrationsOutgoing';
-import './integrationsOutgoingHistory.html';
-import './integrationsOutgoingHistory';
-import './additional/zapier.html';
diff --git a/app/integrations/client/views/integrations.html b/app/integrations/client/views/integrations.html
deleted file mode 100644
index 0a39da5ae278..000000000000
--- a/app/integrations/client/views/integrations.html
+++ /dev/null
@@ -1,74 +0,0 @@
-
-
- {{#header sectionName=pageTitle buttons=true}}
- {{#if hasPermission}}
-
- {{/if}}
- {{/header}}
-
-
- {{#unless hasPermission}}
-
{{_ "You_are_not_authorized_to_view_this_page"}}
- {{else}}
-
- {{/unless}}
-
-
-
diff --git a/app/integrations/client/views/integrations.js b/app/integrations/client/views/integrations.js
deleted file mode 100644
index 0506b21e1697..000000000000
--- a/app/integrations/client/views/integrations.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import { Template } from 'meteor/templating';
-import { ReactiveVar } from 'meteor/reactive-var';
-import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
-import { Tracker } from 'meteor/tracker';
-import moment from 'moment';
-import _ from 'underscore';
-
-import { hasAtLeastOnePermission } from '../../../authorization';
-import { integrations } from '../../lib/rocketchat';
-import { SideNav } from '../../../ui-utils/client';
-import { APIClient } from '../../../utils/client';
-
-const ITEMS_COUNT = 50;
-
-Template.integrations.helpers({
- hasPermission() {
- return hasAtLeastOnePermission([
- 'manage-outgoing-integrations',
- 'manage-own-outgoing-integrations',
- 'manage-incoming-integrations',
- 'manage-own-incoming-integrations',
- ]);
- },
- integrations() {
- return Template.instance().integrations.get();
- },
- dateFormated(date) {
- return moment(date).format('L LT');
- },
- eventTypeI18n(event) {
- return TAPi18n.__(integrations.outgoingEvents[event].label);
- },
-});
-
-Template.integrations.onRendered(() => {
- Tracker.afterFlush(() => {
- SideNav.setFlex('adminFlex');
- SideNav.openFlex();
- });
-});
-
-Template.integrations.onCreated(async function() {
- this.integrations = new ReactiveVar([]);
- this.offset = new ReactiveVar(0);
- this.total = new ReactiveVar(0);
-
- this.autorun(async () => {
- const offset = this.offset.get();
- const { integrations, total } = await APIClient.v1.get(`integrations.list?sort={"type":1}&count=${ ITEMS_COUNT }&offset=${ offset }`);
- this.total.set(total);
- this.integrations.set(this.integrations.get().concat(integrations));
- });
-});
-
-Template.integrations.events({
- 'scroll .content': _.throttle(function(e, instance) {
- if (e.target.scrollTop >= (e.target.scrollHeight - e.target.clientHeight)) {
- const integrations = instance.integrations.get();
- if (instance.total.get() <= integrations.length) {
- return;
- }
- return instance.offset.set(instance.offset.get() + ITEMS_COUNT);
- }
- }, 200),
-});
diff --git a/app/integrations/client/views/integrationsIncoming.html b/app/integrations/client/views/integrationsIncoming.html
deleted file mode 100644
index 7a98d4ac9241..000000000000
--- a/app/integrations/client/views/integrationsIncoming.html
+++ /dev/null
@@ -1,138 +0,0 @@
-
-
- {{#header sectionName=pageTitle buttons=true}}
-
- {{/header}}
-
-
{{_ "Back_to_integrations"}}
-
- {{#if hasPermission}}
-
- {{else}}
- {{_ "Not_authorized"}}
- {{/if}}
-
-
-
-
diff --git a/app/integrations/client/views/integrationsIncoming.js b/app/integrations/client/views/integrationsIncoming.js
deleted file mode 100644
index 222205d64fa7..000000000000
--- a/app/integrations/client/views/integrationsIncoming.js
+++ /dev/null
@@ -1,251 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-import { ReactiveVar } from 'meteor/reactive-var';
-import { FlowRouter } from 'meteor/kadira:flow-router';
-import { Template } from 'meteor/templating';
-import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
-import { Tracker } from 'meteor/tracker';
-import hljs from 'highlight.js';
-import toastr from 'toastr';
-
-import { exampleMsg, exampleSettings, exampleUser } from './messageExample';
-import { hasAtLeastOnePermission } from '../../../authorization';
-import { modal, SideNav } from '../../../ui-utils/client';
-import { t, handleError } from '../../../utils';
-import { getIntegration } from '../getIntegration';
-
-Template.integrationsIncoming.onCreated(async function _incomingIntegrationsOnCreated() {
- const params = Template.instance().data.params ? Template.instance().data.params() : undefined;
- this.integration = new ReactiveVar({});
- this.record = new ReactiveVar({
- username: 'rocket.cat',
- });
- if (params && params.id) {
- const integration = await getIntegration(params.id, Meteor.userId());
- if (integration) {
- this.integration.set(integration);
- }
- }
-});
-
-Template.integrationsIncoming.helpers({
- exampleMsg,
- exampleUser,
- exampleSettings,
- hasPermission() {
- return hasAtLeastOnePermission([
- 'manage-incoming-integrations',
- 'manage-own-incoming-integrations',
- ]);
- },
-
- canDelete() {
- return this.params && this.params() && typeof this.params().id !== 'undefined';
- },
-
- data() {
- const data = Template.instance().integration.get();
- if (data) {
- const completeToken = `${ data._id }/${ data.token }`;
- data.url = Meteor.absoluteUrl(`hooks/${ completeToken }`);
- data.completeToken = completeToken;
- data.hasScriptError = data.scriptEnabled && data.scriptError;
- Template.instance().record.set(data);
- return data;
- }
-
- return Template.instance().record.curValue;
- },
- exampleJson() {
- const record = Template.instance().record.get();
- const data = {
- username: record.alias,
- icon_emoji: record.emoji,
- icon_url: record.avatar,
- text: 'Example message',
- attachments: [{
- title: 'Rocket.Chat',
- title_link: 'https://rocket.chat',
- text: 'Rocket.Chat, the best open source chat',
- image_url: '/images/integration-attachment-example.png',
- color: '#764FA5',
- }],
- };
-
- const invalidData = [null, ''];
- Object.keys(data).forEach((key) => {
- if (invalidData.includes(data[key])) {
- delete data[key];
- }
- });
-
- return hljs.highlight('json', JSON.stringify(data, null, 2)).value;
- },
-
- curl() {
- const record = Template.instance().record.get();
-
- if (!record.url) {
- return;
- }
-
- const data = {
- username: record.alias,
- icon_emoji: record.emoji,
- icon_url: record.avatar,
- text: 'Example message',
- attachments: [{
- title: 'Rocket.Chat',
- title_link: 'https://rocket.chat',
- text: 'Rocket.Chat, the best open source chat',
- image_url: '/images/integration-attachment-example.png',
- color: '#764FA5',
- }],
- };
-
- const invalidData = [null, ''];
- Object.keys(data).forEach((key) => {
- if (invalidData.includes(data[key])) {
- delete data[key];
- }
- });
-
- return `curl -X POST -H 'Content-Type: application/json' --data '${ JSON.stringify(data) }' ${ record.url }`;
- },
-
- editorOptions() {
- return {
- lineNumbers: true,
- mode: 'javascript',
- gutters: [
- // 'CodeMirror-lint-markers'
- 'CodeMirror-linenumbers',
- 'CodeMirror-foldgutter',
- ],
- // lint: true,
- foldGutter: true,
- // lineWrapping: true,
- matchBrackets: true,
- autoCloseBrackets: true,
- matchTags: true,
- showTrailingSpace: true,
- highlightSelectionMatches: true,
- };
- },
-});
-
-Template.integrationsIncoming.events({
- 'blur input': (e, t) => {
- const value = t.record.curValue || {};
-
- value.name = $('[name=name]').val().trim();
- value.alias = $('[name=alias]').val().trim();
- value.emoji = $('[name=emoji]').val().trim();
- value.avatar = $('[name=avatar]').val().trim();
- value.channel = $('[name=channel]').val().trim();
- value.username = $('[name=username]').val().trim();
-
- t.record.set(value);
- },
-
- 'click .rc-header__section-button > .delete': () => {
- const params = Template.instance().data.params();
-
- modal.open({
- title: t('Are_you_sure'),
- text: t('You_will_not_be_able_to_recover'),
- type: 'warning',
- showCancelButton: true,
- confirmButtonColor: '#DD6B55',
- confirmButtonText: t('Yes_delete_it'),
- cancelButtonText: t('Cancel'),
- closeOnConfirm: false,
- html: false,
- }, () => {
- Meteor.call('deleteIncomingIntegration', params.id, (err) => {
- if (err) {
- return handleError(err);
- }
- modal.open({
- title: t('Deleted'),
- text: t('Your_entry_has_been_deleted'),
- type: 'success',
- timer: 1000,
- showConfirmButton: false,
- });
-
- FlowRouter.go('admin-integrations');
- });
- });
- },
-
- 'click .button-fullscreen': () => {
- const codeMirrorBox = $('.code-mirror-box');
- codeMirrorBox.addClass('code-mirror-box-fullscreen content-background-color');
- codeMirrorBox.find('.CodeMirror')[0].CodeMirror.refresh();
- },
-
- 'click .button-restore': () => {
- const codeMirrorBox = $('.code-mirror-box');
- codeMirrorBox.removeClass('code-mirror-box-fullscreen content-background-color');
- codeMirrorBox.find('.CodeMirror')[0].CodeMirror.refresh();
- },
-
- 'click .rc-header__section-button > .save': () => {
- const enabled = $('[name=enabled]:checked').val().trim();
- const name = $('[name=name]').val().trim();
- const alias = $('[name=alias]').val().trim();
- const emoji = $('[name=emoji]').val().trim();
- const avatar = $('[name=avatar]').val().trim();
- const channel = $('[name=channel]').val().trim();
- const username = $('[name=username]').val().trim();
- const scriptEnabled = $('[name=scriptEnabled]:checked').val().trim();
- const script = $('[name=script]').val().trim();
-
- if (channel === '') {
- return toastr.error(TAPi18n.__('The_channel_name_is_required'));
- }
-
- if (username === '') {
- return toastr.error(TAPi18n.__('The_username_is_required'));
- }
-
- const integration = {
- enabled: enabled === '1',
- channel,
- username,
- alias: alias !== '' ? alias : undefined,
- emoji: emoji !== '' ? emoji : undefined,
- avatar: avatar !== '' ? avatar : undefined,
- name: name !== '' ? name : undefined,
- script: script !== '' ? script : undefined,
- scriptEnabled: scriptEnabled === '1',
- };
-
- const params = Template.instance().data.params ? Template.instance().data.params() : undefined;
- if (params && params.id) {
- Meteor.call('updateIncomingIntegration', params.id, integration, (err) => {
- if (err) {
- return handleError(err);
- }
-
- toastr.success(TAPi18n.__('Integration_updated'));
- });
- } else {
- Meteor.call('addIncomingIntegration', integration, (err, data) => {
- if (err) {
- return handleError(err);
- }
-
- toastr.success(TAPi18n.__('Integration_added'));
- FlowRouter.go('admin-integrations-incoming', { id: data._id });
- });
- }
- },
-});
-
-Template.integrationsIncoming.onRendered(() => {
- Tracker.afterFlush(() => {
- SideNav.setFlex('adminFlex');
- SideNav.openFlex();
- });
-});
diff --git a/app/integrations/client/views/integrationsNew.html b/app/integrations/client/views/integrationsNew.html
deleted file mode 100644
index 9c3ab5ee9ec3..000000000000
--- a/app/integrations/client/views/integrationsNew.html
+++ /dev/null
@@ -1,51 +0,0 @@
-
-
- {{> header sectionName=pageTitle}}
-
-
{{_ "Back_to_integrations"}}
- {{#if hasPermission}}
-
-
- {{else}}
- {{_ "Not_authorized"}}
- {{/if}}
-
-
-
diff --git a/app/integrations/client/views/integrationsNew.js b/app/integrations/client/views/integrationsNew.js
deleted file mode 100644
index f5bd29c08e6f..000000000000
--- a/app/integrations/client/views/integrationsNew.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import { Template } from 'meteor/templating';
-import { Tracker } from 'meteor/tracker';
-
-import { hasAtLeastOnePermission } from '../../../authorization';
-import { SideNav } from '../../../ui-utils/client';
-
-Template.integrationsNew.helpers({
- hasPermission() {
- return hasAtLeastOnePermission([
- 'manage-outgoing-integrations',
- 'manage-own-outgoing-integrations',
- 'manage-incoming-integrations',
- 'manage-own-incoming-integrations',
- ]);
- },
- canAddIncomingIntegration() {
- return hasAtLeastOnePermission([
- 'manage-incoming-integrations',
- 'manage-own-incoming-integrations',
- ]);
- },
- canAddOutgoingIntegration() {
- return hasAtLeastOnePermission([
- 'manage-outgoing-integrations',
- 'manage-own-outgoing-integrations',
- ]);
- },
-});
-
-Template.integrationsNew.onRendered(() => {
- Tracker.afterFlush(() => {
- SideNav.setFlex('adminFlex');
- SideNav.openFlex();
- });
-});
diff --git a/app/integrations/client/views/integrationsOutgoing.html b/app/integrations/client/views/integrationsOutgoing.html
deleted file mode 100644
index d9dc03ae209e..000000000000
--- a/app/integrations/client/views/integrationsOutgoing.html
+++ /dev/null
@@ -1,241 +0,0 @@
-
-
- {{#header sectionName=pageTitle buttons=true}}
-
- {{/header}}
-
-
-
{{_ "Back_to_integrations"}}
- {{#unless hasPermission}}
-
{{_ "You_are_not_authorized_to_view_this_page"}}
- {{else}}
-
- {{/unless}}
-
-
-
diff --git a/app/integrations/client/views/integrationsOutgoing.js b/app/integrations/client/views/integrationsOutgoing.js
deleted file mode 100644
index dba874aca11b..000000000000
--- a/app/integrations/client/views/integrationsOutgoing.js
+++ /dev/null
@@ -1,353 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-import { ReactiveVar } from 'meteor/reactive-var';
-import { Random } from 'meteor/random';
-import { FlowRouter } from 'meteor/kadira:flow-router';
-import { Template } from 'meteor/templating';
-import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
-import { Tracker } from 'meteor/tracker';
-import hljs from 'highlight.js';
-import toastr from 'toastr';
-
-import { exampleMsg, exampleSettings, exampleUser } from './messageExample';
-import { hasAtLeastOnePermission } from '../../../authorization';
-import { modal, SideNav } from '../../../ui-utils';
-import { t, handleError } from '../../../utils/client';
-import { integrations } from '../../lib/rocketchat';
-import { getIntegration } from '../getIntegration';
-
-Template.integrationsOutgoing.onCreated(async function _integrationsOutgoingOnCreated() {
- const params = Template.instance().data.params ? Template.instance().data.params() : undefined;
- this.record = new ReactiveVar({
- username: 'rocket.cat',
- token: Random.id(24),
- retryFailedCalls: true,
- retryCount: 6,
- retryDelay: 'powers-of-ten',
- runOnEdits: true,
- });
-
- this.updateRecord = () => {
- this.record.set({
- enabled: $('[name=enabled]:checked').val().trim() === '1',
- event: $('[name=event]').val().trim(),
- name: $('[name=name]').val().trim(),
- alias: $('[name=alias]').val().trim(),
- emoji: $('[name=emoji]').val().trim(),
- avatar: $('[name=avatar]').val().trim(),
- channel: $('[name=channel]').val() ? $('[name=channel]').val().trim() : undefined,
- username: $('[name=username]').val().trim(),
- triggerWords: $('[name=triggerWords]').val() ? $('[name=triggerWords]').val().trim() : undefined,
- urls: $('[name=urls]').val().trim(),
- token: $('[name=token]').val().trim(),
- scriptEnabled: $('[name=scriptEnabled]:checked').val().trim() === '1',
- script: $('[name=script]').val().trim(),
- targetRoom: $('[name=targetRoom]').val() ? $('[name=targetRoom]').val().trim() : undefined,
- triggerWordAnywhere: $('[name=triggerWordAnywhere]:checked').val().trim() === '1',
- retryFailedCalls: $('[name=retryFailedCalls]:checked').val().trim() === '1',
- retryCount: $('[name=retryCount]').val() ? $('[name=retryCount]').val().trim() : 6,
- retryDelay: $('[name=retryDelay]').val() ? $('[name=retryDelay]').val().trim() : 'powers-of-ten',
- runOnEdits: $('[name=runOnEdits]:checked').val().trim() === '1',
- });
- };
-
- const integration = await getIntegration(params.id, Meteor.userId());
- if (params.id && !integration) {
- toastr.error(TAPi18n.__('No_integration_found'));
- FlowRouter.go('admin-integrations');
- return;
- }
-
- integration.hasScriptError = integration.scriptEnabled && integration.scriptError;
- this.record.set(integration);
-});
-
-Template.integrationsOutgoing.helpers({
- exampleMsg,
- exampleUser,
- exampleSettings,
- join(arr, sep) {
- if (!arr || !arr.join) {
- return arr;
- }
-
- return arr.join(sep);
- },
-
- showHistoryButton() {
- return this.params && this.params() && typeof this.params().id !== 'undefined';
- },
-
- hasPermission() {
- return hasAtLeastOnePermission([
- 'manage-outgoing-integrations',
- 'manage-own-outgoing-integrations',
- ]);
- },
-
- data() {
- return Template.instance().record.get();
- },
-
- canDelete() {
- return this.params && this.params() && typeof this.params().id !== 'undefined';
- },
-
- eventTypes() {
- return Object.values(integrations.outgoingEvents);
- },
-
- hasTypeSelected() {
- const record = Template.instance().record.get();
-
- return typeof record.event === 'string' && record.event !== '';
- },
-
- shouldDisplayChannel() {
- const record = Template.instance().record.get();
-
- return typeof record.event === 'string' && integrations.outgoingEvents[record.event].use.channel;
- },
-
- shouldDisplayTriggerWords() {
- const record = Template.instance().record.get();
-
- return typeof record.event === 'string' && integrations.outgoingEvents[record.event].use.triggerWords;
- },
-
- shouldDisplayTargetRoom() {
- const record = Template.instance().record.get();
-
- return typeof record.event === 'string' && integrations.outgoingEvents[record.event].use.targetRoom;
- },
-
- exampleJson() {
- const record = Template.instance().record.get();
- const data = {
- username: record.alias,
- icon_emoji: record.emoji,
- icon_url: record.avatar,
- text: 'Response text',
- attachments: [{
- title: 'Rocket.Chat',
- title_link: 'https://rocket.chat',
- text: 'Rocket.Chat, the best open source chat',
- image_url: '/images/integration-attachment-example.png',
- color: '#764FA5',
- }],
- };
-
- const invalidData = [null, ''];
- Object.keys(data).forEach((key) => {
- if (invalidData.includes(data[key])) {
- delete data[key];
- }
- });
-
- return hljs.highlight('json', JSON.stringify(data, null, 2)).value;
- },
-
- editorOptions() {
- return {
- lineNumbers: true,
- mode: 'javascript',
- gutters: [
- // "CodeMirror-lint-markers",
- 'CodeMirror-linenumbers',
- 'CodeMirror-foldgutter',
- ],
- // lint: true,
- foldGutter: true,
- // lineWrapping: true,
- matchBrackets: true,
- autoCloseBrackets: true,
- matchTags: true,
- showTrailingSpace: true,
- highlightSelectionMatches: true,
- };
- },
-});
-
-Template.integrationsOutgoing.events({
- 'blur input': (e, t) => {
- t.updateRecord();
- },
-
- 'click input[type=radio]': (e, t) => {
- t.updateRecord();
- },
-
- 'change select[name=event]': (e, t) => {
- const record = t.record.get();
- record.event = $('[name=event]').val().trim();
-
- t.record.set(record);
- },
-
- 'click .rc-button.history': () => {
- FlowRouter.go(`/admin/integrations/outgoing/${ FlowRouter.getParam('id') }/history`);
- },
-
- 'click .expand': (e) => {
- $(e.currentTarget).closest('.section').removeClass('section-collapsed');
- $(e.currentTarget).closest('button').removeClass('expand').addClass('collapse').find('span').text(TAPi18n.__('Collapse'));
- $('.CodeMirror').each((index, codeMirror) => codeMirror.CodeMirror.refresh());
- },
-
- 'click .collapse': (e) => {
- $(e.currentTarget).closest('.section').addClass('section-collapsed');
- $(e.currentTarget).closest('button').addClass('expand').removeClass('collapse').find('span').text(TAPi18n.__('Expand'));
- },
-
- 'click .rc-header__section-button > .delete': () => {
- const params = Template.instance().data.params();
-
- modal.open({
- title: t('Are_you_sure'),
- text: t('You_will_not_be_able_to_recover'),
- type: 'warning',
- showCancelButton: true,
- confirmButtonColor: '#DD6B55',
- confirmButtonText: t('Yes_delete_it'),
- cancelButtonText: t('Cancel'),
- closeOnConfirm: false,
- html: false,
- }, () => {
- Meteor.call('deleteOutgoingIntegration', params.id, (err) => {
- if (err) {
- handleError(err);
- } else {
- modal.open({
- title: t('Deleted'),
- text: t('Your_entry_has_been_deleted'),
- type: 'success',
- timer: 1000,
- showConfirmButton: false,
- });
-
- FlowRouter.go('admin-integrations');
- }
- });
- });
- },
-
- 'click .button-fullscreen': () => {
- $('.code-mirror-box').addClass('code-mirror-box-fullscreen content-background-color');
- $('.CodeMirror')[0].CodeMirror.refresh();
- },
-
- 'click .button-restore': () => {
- $('.code-mirror-box').removeClass('code-mirror-box-fullscreen content-background-color');
- $('.CodeMirror')[0].CodeMirror.refresh();
- },
-
- 'click .rc-header__section-button > .save': () => {
- const event = $('[name=event]').val().trim();
- const enabled = $('[name=enabled]:checked').val().trim();
- const name = $('[name=name]').val().trim();
- const impersonateUser = $('[name=impersonateUser]:checked').val().trim();
- const alias = $('[name=alias]').val().trim();
- const emoji = $('[name=emoji]').val().trim();
- const avatar = $('[name=avatar]').val().trim();
- const username = $('[name=username]').val().trim();
- const token = $('[name=token]').val().trim();
- const scriptEnabled = $('[name=scriptEnabled]:checked').val().trim();
- const script = $('[name=script]').val().trim();
- const retryFailedCalls = $('[name=retryFailedCalls]:checked').val().trim();
- let urls = $('[name=urls]').val().trim();
-
- if (username === '' && impersonateUser === '0') {
- return toastr.error(TAPi18n.__('The_username_is_required'));
- }
-
- urls = urls.split('\n').filter((url) => url.trim() !== '');
- if (urls.length === 0) {
- return toastr.error(TAPi18n.__('You_should_inform_one_url_at_least'));
- }
-
- let triggerWords;
- let triggerWordAnywhere;
- let runOnEdits;
- if (integrations.outgoingEvents[event].use.triggerWords) {
- triggerWords = $('[name=triggerWords]').val().trim();
- triggerWords = triggerWords.split(',').filter((word) => word.trim() !== '');
-
- triggerWordAnywhere = $('[name=triggerWordAnywhere]:checked').val().trim();
- runOnEdits = $('[name=runOnEdits]:checked').val().trim();
- }
-
- let channel;
- if (integrations.outgoingEvents[event].use.channel) {
- channel = $('[name=channel]').val().trim();
-
- if (!channel || channel.trim() === '') {
- return toastr.error(TAPi18n.__('error-the-field-is-required', { field: TAPi18n.__('Channel') }));
- }
- }
-
- let targetRoom;
- if (integrations.outgoingEvents[event].use.targetRoom) {
- targetRoom = $('[name=targetRoom]').val().trim();
-
- if (!targetRoom || targetRoom.trim() === '') {
- return toastr.error(TAPi18n.__('error-the-field-is-required', { field: TAPi18n.__('TargetRoom') }));
- }
- }
-
- let retryCount;
- let retryDelay;
- if (retryFailedCalls === '1') {
- retryCount = parseInt($('[name=retryCount]').val().trim());
- retryDelay = $('[name=retryDelay]').val().trim();
- }
-
- const integration = {
- event: event !== '' ? event : undefined,
- enabled: enabled === '1',
- username,
- channel: channel !== '' ? channel : undefined,
- targetRoom: targetRoom !== '' ? targetRoom : undefined,
- alias: alias !== '' ? alias : undefined,
- emoji: emoji !== '' ? emoji : undefined,
- avatar: avatar !== '' ? avatar : undefined,
- name: name !== '' ? name : undefined,
- triggerWords: triggerWords !== '' ? triggerWords : undefined,
- urls: urls !== '' ? urls : undefined,
- token: token !== '' ? token : undefined,
- script: script !== '' ? script : undefined,
- scriptEnabled: scriptEnabled === '1',
- impersonateUser: impersonateUser === '1',
- retryFailedCalls: retryFailedCalls === '1',
- retryCount: retryCount || 6,
- retryDelay: retryDelay || 'powers-of-ten',
- triggerWordAnywhere: triggerWordAnywhere === '1',
- runOnEdits: runOnEdits === '1',
- };
-
- const params = Template.instance().data.params ? Template.instance().data.params() : undefined;
- if (params && params.id) {
- Meteor.call('updateOutgoingIntegration', params.id, integration, (err) => {
- if (err) {
- return handleError(err);
- }
-
- toastr.success(TAPi18n.__('Integration_updated'));
- });
- } else {
- Meteor.call('addOutgoingIntegration', integration, (err, data) => {
- if (err) {
- return handleError(err);
- }
-
- toastr.success(TAPi18n.__('Integration_added'));
- FlowRouter.go('admin-integrations-outgoing', { id: data._id });
- });
- }
- },
-});
-
-Template.integrationsOutgoing.onRendered(() => {
- Tracker.afterFlush(() => {
- SideNav.setFlex('adminFlex');
- SideNav.openFlex();
- });
-});
diff --git a/app/integrations/client/views/integrationsOutgoingHistory.html b/app/integrations/client/views/integrationsOutgoingHistory.html
deleted file mode 100644
index b453924e1f9d..000000000000
--- a/app/integrations/client/views/integrationsOutgoingHistory.html
+++ /dev/null
@@ -1,143 +0,0 @@
-
-
- {{#header sectionName=pageTitle buttons=true}}
-
- {{/header}}
-
-
- {{#unless hasPermission}}
-
{{_ "You_are_not_authorized_to_view_this_page"}}
- {{else}}
-
{{_ "Back_to_integration_detail"}}
-
-
- {{/unless}}
-
-
-
diff --git a/app/integrations/client/views/integrationsOutgoingHistory.js b/app/integrations/client/views/integrationsOutgoingHistory.js
deleted file mode 100644
index 66384848d6a4..000000000000
--- a/app/integrations/client/views/integrationsOutgoingHistory.js
+++ /dev/null
@@ -1,191 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-import { ReactiveVar } from 'meteor/reactive-var';
-import { FlowRouter } from 'meteor/kadira:flow-router';
-import { Template } from 'meteor/templating';
-import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
-import { Tracker } from 'meteor/tracker';
-import _ from 'underscore';
-import hljs from 'highlight.js';
-import moment from 'moment';
-import toastr from 'toastr';
-
-import { handleError } from '../../../utils';
-import { hasAtLeastOnePermission } from '../../../authorization';
-import { integrations } from '../../lib/rocketchat';
-import { SideNav } from '../../../ui-utils/client';
-import { APIClient } from '../../../utils/client';
-import { getIntegration } from '../getIntegration';
-import { integrationHistoryStreamer } from '../streamer';
-
-const HISTORY_COUNT = 25;
-
-Template.integrationsOutgoingHistory.onCreated(async function _integrationsOutgoingHistoryOnCreated() {
- const params = Template.instance().data.params ? Template.instance().data.params() : undefined;
- this.isLoading = new ReactiveVar(false);
- this.history = new ReactiveVar([]);
- this.offset = new ReactiveVar(0);
- this.total = new ReactiveVar(0);
-
- if (params && params.id) {
- integrationHistoryStreamer.on(params.id, ({ type, id, diff, data }) => {
- const histories = this.history.get();
-
- if (type === 'inserted') {
- this.history.set([{ ...data }].concat(histories));
- return;
- }
-
- if (type === 'updated') {
- const history = histories.find(({ _id }) => _id === id);
- Object.assign(history, diff);
- this.history.set(histories);
- return;
- }
-
- if (type === 'removed') {
- this.history.set([]);
- }
- });
-
- const integration = await getIntegration(params.id, Meteor.userId());
-
- if (!integration) {
- toastr.error(TAPi18n.__('No_integration_found'));
- return FlowRouter.go('admin-integrations');
- }
- this.autorun(async () => {
- this.isLoading.set(true);
- const { history, total } = await APIClient.v1.get(`integrations.history?id=${ integration._id }&count=${ HISTORY_COUNT }&offset=${ this.offset.get() }`);
- this.history.set(this.history.get().concat(history));
- this.total.set(total);
- this.isLoading.set(false);
- });
- } else {
- toastr.error(TAPi18n.__('No_integration_found'));
- FlowRouter.go('admin-integrations');
- }
-});
-
-Template.integrationsOutgoingHistory.helpers({
- hasPermission() {
- return hasAtLeastOnePermission(['manage-outgoing-integrations', 'manage-own-outgoing-integrations']);
- },
-
- isLoading() {
- return Template.instance().isLoading.get();
- },
-
- histories() {
- return Template.instance().history.get().sort((a, b) => {
- if (+a._updatedAt < +b._updatedAt) {
- return 1;
- }
-
- if (+a._updatedAt > +b._updatedAt) {
- return -1;
- }
-
- return 0;
- });
- },
-
- hasProperty(history, property) {
- return typeof history[property] !== 'undefined' || history[property] != null;
- },
-
- iconClass(history) {
- if (typeof history.error !== 'undefined' && history.error) {
- return 'icon-cancel-circled error-color';
- } if (history.finished) {
- return 'icon-ok-circled success-color';
- }
- return 'icon-help-circled';
- },
-
- statusI18n(error) {
- return typeof error !== 'undefined' && error ? TAPi18n.__('Failure') : TAPi18n.__('Success');
- },
-
- formatDate(date) {
- return moment(date).format('L LTS');
- },
-
- formatDateDetail(date) {
- return moment(date).format('L HH:mm:ss:SSSS');
- },
-
- eventTypei18n(event) {
- return TAPi18n.__(integrations.outgoingEvents[event].label);
- },
-
- jsonStringify(data) {
- if (!data) {
- return '';
- } if (typeof data === 'object') {
- return hljs.highlight('json', JSON.stringify(data, null, 2)).value;
- }
- return hljs.highlight('json', data).value;
- },
-
- integrationId() {
- return this.params && this.params() && this.params().id;
- },
-});
-
-Template.integrationsOutgoingHistory.events({
- 'click .expand': (e) => {
- $(e.currentTarget).closest('.section').removeClass('section-collapsed');
- $(e.currentTarget).closest('button').removeClass('expand').addClass('collapse').find('span').text(TAPi18n.__('Collapse'));
- $('.CodeMirror').each((index, codeMirror) => codeMirror.CodeMirror.refresh());
- },
-
- 'click .collapse': (e) => {
- $(e.currentTarget).closest('.section').addClass('section-collapsed');
- $(e.currentTarget).closest('button').addClass('expand').removeClass('collapse').find('span').text(TAPi18n.__('Expand'));
- },
-
- 'click .replay': (e, t) => {
- if (!t || !t.data || !t.data.params || !t.data.params().id) {
- return;
- }
-
- const historyId = $(e.currentTarget).attr('data-history-id');
-
- Meteor.call('replayOutgoingIntegration', { integrationId: t.data.params().id, historyId }, (e) => {
- if (e) {
- handleError(e);
- }
- });
- },
-
- 'click .clear-history': (e, t) => {
- if (!t || !t.data || !t.data.params || !t.data.params().id) {
- return;
- }
-
- Meteor.call('clearIntegrationHistory', t.data.params().id, (e) => {
- if (e) {
- handleError(e);
- return;
- }
-
- toastr.success(TAPi18n.__('Integration_History_Cleared'));
-
- t.history.set([]);
- });
- },
-
- 'scroll .content': _.throttle((e, instance) => {
- const history = instance.history.get();
- if ((e.target.scrollTop >= e.target.scrollHeight - e.target.clientHeight) && instance.total.get() > history.length) {
- instance.offset.set(instance.offset.get() + HISTORY_COUNT);
- }
- }, 200),
-});
-
-Template.integrationsOutgoingHistory.onRendered(() => {
- Tracker.afterFlush(() => {
- SideNav.setFlex('adminFlex');
- SideNav.openFlex();
- });
-});
diff --git a/app/integrations/client/views/messageExample.js b/app/integrations/client/views/messageExample.js
deleted file mode 100644
index 2e553dae32b6..000000000000
--- a/app/integrations/client/views/messageExample.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import { Template } from 'meteor/templating';
-import { Random } from 'meteor/random';
-
-export const exampleMsg = () => {
- const record = Template.instance().record.get();
- return {
- _id: Random.id(),
- alias: record.alias,
- emoji: record.emoji,
- avatar: record.avatar,
- msg: 'Example message',
- bot: {
- i: Random.id(),
- },
- groupable: false,
- attachments: [{
- title: 'Rocket.Chat',
- title_link: 'https://rocket.chat',
- text: 'Rocket.Chat, the best open source chat',
- image_url: '/images/integration-attachment-example.png',
- color: '#764FA5',
- }],
- ts: new Date(),
- u: {
- _id: Random.id(),
- username: record.username,
- },
- };
-};
-
-export const exampleUser = () => ({
- u: {
- _id: Random.id(),
- },
-});
-
-export const exampleSettings = () => ({
- settings: {},
-});
diff --git a/app/oauth2-server-config/client/admin/route.js b/app/oauth2-server-config/client/admin/route.js
deleted file mode 100644
index c274dbaa6087..000000000000
--- a/app/oauth2-server-config/client/admin/route.js
+++ /dev/null
@@ -1,28 +0,0 @@
-import { BlazeLayout } from 'meteor/kadira:blaze-layout';
-
-import { registerAdminRoute } from '../../../../client/admin';
-import { t } from '../../../utils';
-
-registerAdminRoute('/oauth-apps', {
- name: 'admin-oauth-apps',
- async action() {
- await import('./views');
- return BlazeLayout.render('main', {
- center: 'oauthApps',
- pageTitle: t('OAuth_Applications'),
- });
- },
-});
-
-registerAdminRoute('/oauth-app/:id?', {
- name: 'admin-oauth-app',
- async action(params) {
- await import('./views');
- return BlazeLayout.render('main', {
- center: 'pageSettingsContainer',
- pageTitle: t('OAuth_Application'),
- pageTemplate: 'oauthApp',
- params,
- });
- },
-});
diff --git a/app/oauth2-server-config/client/admin/views/index.js b/app/oauth2-server-config/client/admin/views/index.js
deleted file mode 100644
index 071605a56b89..000000000000
--- a/app/oauth2-server-config/client/admin/views/index.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import './oauthApp.html';
-import './oauthApp';
-import './oauthApps.html';
-import './oauthApps';
diff --git a/app/oauth2-server-config/client/admin/views/oauthApp.html b/app/oauth2-server-config/client/admin/views/oauthApp.html
deleted file mode 100644
index 360a4ff57c99..000000000000
--- a/app/oauth2-server-config/client/admin/views/oauthApp.html
+++ /dev/null
@@ -1,72 +0,0 @@
-
-
-
diff --git a/app/oauth2-server-config/client/admin/views/oauthApp.js b/app/oauth2-server-config/client/admin/views/oauthApp.js
deleted file mode 100644
index fa866ba86a81..000000000000
--- a/app/oauth2-server-config/client/admin/views/oauthApp.js
+++ /dev/null
@@ -1,120 +0,0 @@
-import { Meteor } from 'meteor/meteor';
-import { ReactiveVar } from 'meteor/reactive-var';
-import { FlowRouter } from 'meteor/kadira:flow-router';
-import { Template } from 'meteor/templating';
-import { TAPi18n } from 'meteor/rocketchat:tap-i18n';
-import { Tracker } from 'meteor/tracker';
-import toastr from 'toastr';
-
-import { hasAllPermission } from '../../../../authorization';
-import { modal, SideNav } from '../../../../ui-utils/client';
-import { t, handleError } from '../../../../utils';
-import { APIClient } from '../../../../utils/client';
-
-Template.oauthApp.onCreated(async function() {
- const params = this.data.params();
- this.oauthApp = new ReactiveVar({});
- this.record = new ReactiveVar({
- active: true,
- });
- if (params && params.id) {
- const { oauthApp } = await APIClient.v1.get(`oauth-apps.get?appId=${ params.id }`);
- this.oauthApp.set(oauthApp);
- }
-});
-
-Template.oauthApp.helpers({
- hasPermission() {
- return hasAllPermission('manage-oauth-apps');
- },
- data() {
- const instance = Template.instance();
- if (typeof instance.data.params === 'function') {
- const params = instance.data.params();
- if (params && params.id) {
- const data = Template.instance().oauthApp.get();
- if (data) {
- data.authorization_url = Meteor.absoluteUrl('oauth/authorize');
- data.access_token_url = Meteor.absoluteUrl('oauth/token');
- if (Array.isArray(data.redirectUri)) {
- data.redirectUri = data.redirectUri.join('\n');
- }
-
- Template.instance().record.set(data);
- return data;
- }
- }
- }
- return Template.instance().record.curValue;
- },
-});
-
-Template.oauthApp.events({
- 'click .submit > .delete'() {
- const params = Template.instance().data.params();
- modal.open({
- title: t('Are_you_sure'),
- text: t('You_will_not_be_able_to_recover'),
- type: 'warning',
- showCancelButton: true,
- confirmButtonColor: '#DD6B55',
- confirmButtonText: t('Yes_delete_it'),
- cancelButtonText: t('Cancel'),
- closeOnConfirm: false,
- html: false,
- }, function() {
- Meteor.call('deleteOAuthApp', params.id, function() {
- modal.open({
- title: t('Deleted'),
- text: t('Your_entry_has_been_deleted'),
- type: 'success',
- timer: 1000,
- showConfirmButton: false,
- });
- FlowRouter.go('admin-oauth-apps');
- });
- });
- },
- 'click .submit > .save'() {
- const instance = Template.instance();
- const name = $('[name=name]').val().trim();
- const active = $('[name=active]:checked').val().trim() === '1';
- const redirectUri = $('[name=redirectUri]').val().trim();
- if (name === '') {
- return toastr.error(TAPi18n.__('The_application_name_is_required'));
- }
- if (redirectUri === '') {
- return toastr.error(TAPi18n.__('The_redirectUri_is_required'));
- }
- const app = {
- name,
- active,
- redirectUri,
- };
- if (typeof instance.data.params === 'function') {
- const params = instance.data.params();
- if (params && params.id) {
- return Meteor.call('updateOAuthApp', params.id, app, function(err) {
- if (err != null) {
- return handleError(err);
- }
- toastr.success(TAPi18n.__('Application_updated'));
- });
- }
- }
- Meteor.call('addOAuthApp', app, function(err, data) {
- if (err != null) {
- return handleError(err);
- }
- toastr.success(TAPi18n.__('Application_added'));
- FlowRouter.go('admin-oauth-app', { id: data._id });
- });
- },
-});
-
-Template.oauthApp.onRendered(() => {
- Tracker.afterFlush(() => {
- SideNav.setFlex('adminFlex');
- SideNav.openFlex();
- });
-});
diff --git a/app/oauth2-server-config/client/admin/views/oauthApps.html b/app/oauth2-server-config/client/admin/views/oauthApps.html
deleted file mode 100644
index 99a142127802..000000000000
--- a/app/oauth2-server-config/client/admin/views/oauthApps.html
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
- {{# header sectionName=pageTitle}}
-
- {{/header}}
-
- {{#if hasPermission}}
-
- {{else}}
- {{_ "Not_authorized"}}
- {{/if}}
-
-
-
diff --git a/app/oauth2-server-config/client/admin/views/oauthApps.js b/app/oauth2-server-config/client/admin/views/oauthApps.js
deleted file mode 100644
index 595af845a769..000000000000
--- a/app/oauth2-server-config/client/admin/views/oauthApps.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import { Template } from 'meteor/templating';
-import { Tracker } from 'meteor/tracker';
-import { ReactiveVar } from 'meteor/reactive-var';
-import moment from 'moment';
-
-import { hasAllPermission } from '../../../../authorization';
-import { SideNav } from '../../../../ui-utils/client';
-import { APIClient } from '../../../../utils/client';
-
-Template.oauthApps.onCreated(async function() {
- this.oauthApps = new ReactiveVar([]);
- const { oauthApps } = await APIClient.v1.get('oauth-apps.list');
- this.oauthApps.set(oauthApps);
-});
-
-Template.oauthApps.helpers({
- hasPermission() {
- return hasAllPermission('manage-oauth-apps');
- },
- applications() {
- return Template.instance().oauthApps.get();
- },
- dateFormated(date) {
- return moment(date).format('L LT');
- },
-});
-
-Template.oauthApps.onRendered(() => {
- Tracker.afterFlush(() => {
- SideNav.setFlex('adminFlex');
- SideNav.openFlex();
- });
-});
diff --git a/app/oauth2-server-config/client/index.js b/app/oauth2-server-config/client/index.js
index 917437a0b53a..5911484bf1c4 100644
--- a/app/oauth2-server-config/client/index.js
+++ b/app/oauth2-server-config/client/index.js
@@ -1,5 +1,4 @@
import './oauth/oauth2-client.html';
import './oauth/oauth2-client';
import './admin/startup';
-import './admin/route';
import './oauth/stylesheets/oauth2.css';
diff --git a/app/ui/client/components/GenericTable.js b/app/ui/client/components/GenericTable.js
index 9d50a456aff5..c3a9d0cf5161 100644
--- a/app/ui/client/components/GenericTable.js
+++ b/app/ui/client/components/GenericTable.js
@@ -1,4 +1,4 @@
-import React, { useMemo, useState, useEffect, useCallback } from 'react';
+import React, { useMemo, useState, useEffect, useCallback, forwardRef } from 'react';
import { Box, Pagination, Skeleton, Table, Flex, Tile, Scrollable } from '@rocket.chat/fuselage';
import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
@@ -35,15 +35,15 @@ const LoadingRow = ({ cols }) =>
)}
;
-export function GenericTable({
+export const GenericTable = forwardRef(function GenericTable({
results,
total,
- renderRow,
+ renderRow: RenderRow,
header,
setParams = () => { },
params: paramsDefault = '',
FilterComponent = () => null,
-}) {
+}, ref) {
const t = useTranslation();
const [filter, setFilter] = useState(paramsDefault);
@@ -65,40 +65,38 @@ export function GenericTable({
const itemsPerPageLabel = useCallback(() => t('Items_per_page:'), []);
return <>
- <>
-
- {results && !results.length
- ?
- {t('No_data_found')}
-
- : <>
-
-
-
- { header &&
-
- {header}
-
- }
-
- {results
- ? results.map(renderRow)
- : }
-
-
-
-
-
- >
- }
- >
+
+ {results && !results.length
+ ?
+ {t('No_data_found')}
+
+ : <>
+
+
+
+ { header &&
+
+ {header}
+
+ }
+
+ {results
+ ? results.map((props, index) => )
+ : }
+
+
+
+
+
+ >
+ }
>;
-}
+});
diff --git a/app/utils/client/lib/handleError.js b/app/utils/client/lib/handleError.js
index 6e2fb92bb2e9..92117a95e216 100644
--- a/app/utils/client/lib/handleError.js
+++ b/app/utils/client/lib/handleError.js
@@ -22,11 +22,11 @@ export const handleError = function(error, useToastr = true) {
}
const details = Object.entries(error.details || {})
.reduce((obj, [key, value]) => ({ ...obj, [key]: s.escapeHTML(value) }), {});
- const message = TAPi18n.__(error.error, details);
+ const message = TAPi18n.__(error.error || error.message, details);
const title = details.errorTitle && TAPi18n.__(details.errorTitle);
return toastr.error(message, title);
}
- return s.escapeHTML(TAPi18n.__(error.error, error.details));
+ return s.escapeHTML(TAPi18n.__(error.error || error.message, error.details));
};
diff --git a/client/admin/cloud/CloudPage.js b/client/admin/cloud/CloudPage.js
new file mode 100644
index 000000000000..e4e285da741a
--- /dev/null
+++ b/client/admin/cloud/CloudPage.js
@@ -0,0 +1,146 @@
+import { Box, Button, ButtonGroup, Margins } from '@rocket.chat/fuselage';
+import { useMutableCallback, useSafely } from '@rocket.chat/fuselage-hooks';
+import React, { useState, useEffect } from 'react';
+
+import Page from '../../components/basic/Page';
+import { useTranslation } from '../../contexts/TranslationContext';
+import { useMethod } from '../../contexts/ServerContext';
+import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext';
+import { useQueryStringParameter, useRoute, useRouteParameter } from '../../contexts/RouterContext';
+import WhatIsItSection from './WhatIsItSection';
+import ConnectToCloudSection from './ConnectToCloudSection';
+import TroubleshootingSection from './TroubleshootingSection';
+import WorkspaceRegistrationSection from './WorkspaceRegistrationSection';
+import WorkspaceLoginSection from './WorkspaceLoginSection';
+import ManualWorkspaceRegistrationModal from './ManualWorkspaceRegistrationModal';
+import { cloudConsoleUrl } from './constants';
+
+function CloudPage() {
+ const t = useTranslation();
+ const dispatchToastMessage = useToastMessageDispatch();
+
+ const cloudRoute = useRoute('cloud');
+
+ const page = useRouteParameter('page');
+
+ const errorCode = useQueryStringParameter('error_code');
+ const code = useQueryStringParameter('code');
+ const state = useQueryStringParameter('state');
+ const token = useQueryStringParameter('token');
+
+ const finishOAuthAuthorization = useMethod('cloud:finishOAuthAuthorization');
+ const checkRegisterStatus = useMethod('cloud:checkRegisterStatus');
+ const connectWorkspace = useMethod('cloud:connectWorkspace');
+
+ useEffect(() => {
+ const acceptOAuthAuthorization = async () => {
+ if (page !== 'oauth-callback') {
+ return;
+ }
+
+ if (errorCode) {
+ dispatchToastMessage({
+ type: 'error',
+ title: t('Cloud_error_in_authenticating'),
+ message: t('Cloud_error_code', { errorCode }),
+ });
+ cloudRoute.push();
+ return;
+ }
+
+ try {
+ await finishOAuthAuthorization(code, state);
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ } finally {
+ cloudRoute.push();
+ }
+ };
+
+ acceptOAuthAuthorization();
+ }, [errorCode, code, state]);
+
+ const [registerStatus, setRegisterStatus] = useSafely(useState());
+ const [modal, setModal] = useState(null);
+
+ const fetchRegisterStatus = useMutableCallback(async () => {
+ try {
+ const registerStatus = await checkRegisterStatus();
+ setRegisterStatus(registerStatus);
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ }
+ });
+
+ useEffect(() => {
+ const acceptWorkspaceToken = async () => {
+ try {
+ if (token) {
+ const isConnected = await connectWorkspace(token);
+
+ if (!isConnected) {
+ throw Error(t('An error occured connecting'));
+ }
+
+ dispatchToastMessage({ type: 'success', message: t('Connected') });
+ }
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ } finally {
+ await fetchRegisterStatus();
+ }
+ };
+
+ acceptWorkspaceToken();
+ }, [token]);
+
+ const handleManualWorkspaceRegistrationButtonClick = () => {
+ const handleModalClose = () => {
+ setModal(null);
+ fetchRegisterStatus();
+ };
+ setModal( );
+ };
+
+ const isConnectedToCloud = registerStatus?.connectToCloud;
+ const isWorkspaceRegistered = registerStatus?.workspaceRegistered;
+
+ return
+
+
+ {!isWorkspaceRegistered &&
+ {t('Cloud_Register_manually')}
+ }
+
+ {t('Cloud_console')}
+
+
+
+
+ {modal}
+
+
+
+
+ {isConnectedToCloud && <>
+ {isWorkspaceRegistered
+ ?
+ : }
+
+
+ >}
+
+ {!isConnectedToCloud && }
+
+
+
+ ;
+}
+
+export default CloudPage;
diff --git a/client/admin/cloud/CloudRoute.js b/client/admin/cloud/CloudRoute.js
new file mode 100644
index 000000000000..53c71affae7f
--- /dev/null
+++ b/client/admin/cloud/CloudRoute.js
@@ -0,0 +1,17 @@
+import React from 'react';
+
+import { usePermission } from '../../contexts/AuthorizationContext';
+import NotAuthorizedPage from '../NotAuthorizedPage';
+import CloudPage from './CloudPage';
+
+function CloudRoute() {
+ const canManageCloud = usePermission('manage-cloud');
+
+ if (!canManageCloud) {
+ return ;
+ }
+
+ return ;
+}
+
+export default CloudRoute;
diff --git a/client/admin/cloud/ConnectToCloudSection.js b/client/admin/cloud/ConnectToCloudSection.js
new file mode 100644
index 000000000000..2b1009d35ccc
--- /dev/null
+++ b/client/admin/cloud/ConnectToCloudSection.js
@@ -0,0 +1,61 @@
+import { Box, Button, ButtonGroup, Throbber } from '@rocket.chat/fuselage';
+import { useSafely } from '@rocket.chat/fuselage-hooks';
+import React, { useState } from 'react';
+
+import Subtitle from '../../components/basic/Subtitle';
+import { useTranslation } from '../../contexts/TranslationContext';
+import { useMethod } from '../../contexts/ServerContext';
+import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext';
+
+function ConnectToCloudSection({
+ onRegisterStatusChange,
+ ...props
+}) {
+ const t = useTranslation();
+ const dispatchToastMessage = useToastMessageDispatch();
+
+ const [isConnecting, setConnecting] = useSafely(useState(false));
+
+ const registerWorkspace = useMethod('cloud:registerWorkspace');
+ const syncWorkspace = useMethod('cloud:syncWorkspace');
+
+ const handleRegisterButtonClick = async () => {
+ setConnecting(true);
+
+ try {
+ const isRegistered = await registerWorkspace();
+
+ if (!isRegistered) {
+ throw Error(t('An error occured'));
+ }
+
+ // TODO: sync on register?
+ const isSynced = await syncWorkspace();
+
+ if (!isSynced) {
+ throw Error(t('An error occured syncing'));
+ }
+
+ dispatchToastMessage({ type: 'success', message: t('Sync Complete') });
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ } finally {
+ await (onRegisterStatusChange && onRegisterStatusChange());
+ setConnecting(false);
+ }
+ };
+
+ return
+ {t('Cloud_registration_required')}
+
+ {t('Cloud_registration_required_description')}
+
+
+
+ {isConnecting ? : t('Cloud_registration_required_link_text')}
+
+
+ ;
+}
+
+export default ConnectToCloudSection;
diff --git a/client/admin/cloud/ManualWorkspaceRegistrationModal.js b/client/admin/cloud/ManualWorkspaceRegistrationModal.js
new file mode 100644
index 000000000000..7952a3aa7053
--- /dev/null
+++ b/client/admin/cloud/ManualWorkspaceRegistrationModal.js
@@ -0,0 +1,183 @@
+import { Box, Button, ButtonGroup, Icon, Scrollable, Throbber } from '@rocket.chat/fuselage';
+import Clipboard from 'clipboard';
+import React, { useEffect, useState, useRef } from 'react';
+
+import { Modal } from '../../components/basic/Modal';
+import { useTranslation } from '../../contexts/TranslationContext';
+import { useMethod, useEndpoint } from '../../contexts/ServerContext';
+import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext';
+import MarkdownText from '../../components/basic/MarkdownText';
+import { cloudConsoleUrl } from './constants';
+
+function CopyStep({ onNextButtonClick }) {
+ const t = useTranslation();
+ const dispatchToastMessage = useToastMessageDispatch();
+
+ const [clientKey, setClientKey] = useState('');
+
+ const getWorkspaceRegisterData = useMethod('cloud:getWorkspaceRegisterData');
+
+ useEffect(() => {
+ const loadWorkspaceRegisterData = async () => {
+ const clientKey = await getWorkspaceRegisterData();
+ setClientKey(clientKey);
+ };
+
+ loadWorkspaceRegisterData();
+ }, []);
+
+ const copyRef = useRef();
+
+ useEffect(function() {
+ const clipboard = new Clipboard(copyRef.current);
+ clipboard.on('success', () => {
+ dispatchToastMessage({ type: 'success', message: t('Copied') });
+ });
+
+ return () => {
+ clipboard.destroy();
+ };
+ }, []);
+
+ return <>
+
+
+ {t('Cloud_register_offline_helper')}
+
+
+
+
+ {clientKey}
+
+
+
+ {t('Copy')}
+
+
+
+
+ {t('Cloud_click_here', { cloudConsoleUrl })}
+
+
+
+
+
+ {t('Next')}
+
+
+ >;
+}
+
+function PasteStep({ onBackButtonClick, onFinish }) {
+ const t = useTranslation();
+ const dispatchToastMessage = useToastMessageDispatch();
+
+ const [isLoading, setLoading] = useState(false);
+ const [cloudKey, setCloudKey] = useState('');
+
+ const handleCloudKeyChange = (e) => {
+ setCloudKey(e.currentTarget.value);
+ };
+
+ const registerManually = useEndpoint('POST', 'cloud.manualRegister');
+
+ const handleFinishButtonClick = async () => {
+ setLoading(true);
+
+ try {
+ await registerManually({}, { cloudBlob: cloudKey });
+ dispatchToastMessage({ type: 'success', message: t('Cloud_register_success') });
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: t('Cloud_register_error') });
+ } finally {
+ setLoading(false);
+ onFinish && onFinish();
+ }
+ };
+
+ return <>
+
+
+ {t('Cloud_register_offline_finish_helper')}
+
+
+
+
+
+
+
+
+
+ {t('Back')}
+
+ {isLoading ? : t('Finish Registration')}
+
+
+
+ >;
+}
+
+const Steps = {
+ COPY: 'copy',
+ PASTE: 'paste',
+};
+
+function ManualWorkspaceRegistrationModal({ onClose, props }) {
+ const t = useTranslation();
+
+ const [step, setStep] = useState(Steps.COPY);
+
+ const handleNextButtonClick = () => {
+ setStep(Steps.PASTE);
+ };
+
+ const handleBackButtonClick = () => {
+ setStep(Steps.COPY);
+ };
+
+ return
+
+ {t('Cloud_Register_manually')}
+
+
+ {(step === Steps.COPY && )
+ || (step === Steps.PASTE && )}
+ ;
+}
+
+export default ManualWorkspaceRegistrationModal;
diff --git a/client/admin/cloud/TroubleshootingSection.js b/client/admin/cloud/TroubleshootingSection.js
new file mode 100644
index 000000000000..4d1c8e7238d7
--- /dev/null
+++ b/client/admin/cloud/TroubleshootingSection.js
@@ -0,0 +1,63 @@
+import { Box, Button, ButtonGroup, Throbber } from '@rocket.chat/fuselage';
+import { useSafely } from '@rocket.chat/fuselage-hooks';
+import React, { useState } from 'react';
+
+import { useTranslation } from '../../contexts/TranslationContext';
+import Subtitle from '../../components/basic/Subtitle';
+import { useMethod } from '../../contexts/ServerContext';
+import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext';
+import { statusPageUrl } from './constants';
+
+function TroubleshootingSection({
+ onRegisterStatusChange,
+ ...props
+}) {
+ const t = useTranslation();
+ const dispatchToastMessage = useToastMessageDispatch();
+
+ const [isSyncing, setSyncing] = useSafely(useState(false));
+
+ const syncWorkspace = useMethod('cloud:syncWorkspace');
+
+ const handleSyncButtonClick = async () => {
+ setSyncing(true);
+
+ try {
+ const isSynced = await syncWorkspace();
+
+ if (!isSynced) {
+ throw Error(t('An error occured syncing'));
+ }
+
+ dispatchToastMessage({ type: 'success', message: t('Sync Complete') });
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ } finally {
+ await (onRegisterStatusChange && onRegisterStatusChange());
+ setSyncing(false);
+ }
+ };
+
+ return
+ {t('Cloud_troubleshooting')}
+
+
+ {t('Cloud_workspace_support')}
+
+
+
+
+ {isSyncing ? : t('Sync')}
+
+
+
+
+
+ {t('Cloud_status_page_description')}:{' '}
+ {statusPageUrl}
+
+
+ ;
+}
+
+export default TroubleshootingSection;
diff --git a/client/admin/cloud/WhatIsItSection.js b/client/admin/cloud/WhatIsItSection.js
new file mode 100644
index 000000000000..3bafb0e65f57
--- /dev/null
+++ b/client/admin/cloud/WhatIsItSection.js
@@ -0,0 +1,32 @@
+import { Box } from '@rocket.chat/fuselage';
+import React from 'react';
+
+import { useTranslation } from '../../contexts/TranslationContext';
+import Subtitle from '../../components/basic/Subtitle';
+
+function WhatIsItSection(props) {
+ const t = useTranslation();
+
+ return
+ {t('Cloud_what_is_it')}
+
+
+ {t('Cloud_what_is_it_description')}
+
+
+ {t('Cloud_what_is_it_services_like')}
+
+
+ {t('Register_Server_Registered_Push_Notifications')}
+ {t('Register_Server_Registered_Livechat')}
+ {t('Register_Server_Registered_OAuth')}
+ {t('Register_Server_Registered_Marketplace')}
+
+
+ {t('Cloud_what_is_it_additional')}
+
+
+ ;
+}
+
+export default WhatIsItSection;
diff --git a/client/admin/cloud/WorkspaceLoginSection.js b/client/admin/cloud/WorkspaceLoginSection.js
new file mode 100644
index 000000000000..7e2bad6cb0dd
--- /dev/null
+++ b/client/admin/cloud/WorkspaceLoginSection.js
@@ -0,0 +1,108 @@
+import { Box, Button, ButtonGroup } from '@rocket.chat/fuselage';
+import { useSafely } from '@rocket.chat/fuselage-hooks';
+import React, { useState, useEffect } from 'react';
+
+import { useTranslation } from '../../contexts/TranslationContext';
+import { useMethod } from '../../contexts/ServerContext';
+import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext';
+
+function WorkspaceLoginSection({
+ onRegisterStatusChange,
+ ...props
+}) {
+ const t = useTranslation();
+ const dispatchToastMessage = useToastMessageDispatch();
+
+ const checkUserLoggedIn = useMethod('cloud:checkUserLoggedIn');
+ const getOAuthAuthorizationUrl = useMethod('cloud:getOAuthAuthorizationUrl');
+ const logout = useMethod('cloud:logout');
+ const disconnectWorkspace = useMethod('cloud:disconnectWorkspace');
+
+ const [isLoggedIn, setLoggedIn] = useSafely(useState(false));
+ const [isLoading, setLoading] = useSafely(useState(true));
+
+ const handleLoginButtonClick = async () => {
+ setLoading(true);
+
+ try {
+ const url = await getOAuthAuthorizationUrl();
+ window.location.href = url;
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleLogoutButtonClick = async () => {
+ setLoading(true);
+
+ try {
+ await logout();
+ const isLoggedIn = await checkUserLoggedIn();
+ setLoggedIn(isLoggedIn);
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleDisconnectButtonClick = async () => {
+ setLoading(true);
+
+ try {
+ const success = await disconnectWorkspace();
+
+ if (!success) {
+ throw Error(t('An error occured disconnecting'));
+ }
+
+ dispatchToastMessage({ type: 'success', message: t('Disconnected') });
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ } finally {
+ await (onRegisterStatusChange && onRegisterStatusChange());
+ setLoading(false);
+ }
+ };
+
+ useEffect(() => {
+ const checkLoginState = async () => {
+ setLoading(true);
+
+ try {
+ const isLoggedIn = await checkUserLoggedIn();
+ setLoggedIn(isLoggedIn);
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ checkLoginState();
+ }, []);
+
+ return
+
+ {t('Cloud_workspace_connected')}
+
+
+
+ {isLoggedIn
+ ? {t('Cloud_logout')}
+ : {t('Cloud_login_to_cloud')} }
+
+
+
+ {t('Cloud_workspace_disconnect')}
+
+
+
+ {t('Disconnect')}
+
+ ;
+}
+
+export default WorkspaceLoginSection;
diff --git a/client/admin/cloud/WorkspaceRegistrationSection.js b/client/admin/cloud/WorkspaceRegistrationSection.js
new file mode 100644
index 000000000000..8f2884afa63a
--- /dev/null
+++ b/client/admin/cloud/WorkspaceRegistrationSection.js
@@ -0,0 +1,128 @@
+import { Box, Button, ButtonGroup, EmailInput, Field, Margins, TextInput } from '@rocket.chat/fuselage';
+import { useSafely, useUniqueId } from '@rocket.chat/fuselage-hooks';
+import React, { useState, useMemo } from 'react';
+
+import { useTranslation } from '../../contexts/TranslationContext';
+import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext';
+import { useMethod } from '../../contexts/ServerContext';
+import { supportEmailAddress } from './constants';
+
+function WorkspaceRegistrationSection({
+ email: initialEmail,
+ token: initialToken,
+ workspaceId,
+ uniqueId,
+ onRegisterStatusChange,
+ ...props
+}) {
+ const t = useTranslation();
+ const dispatchToastMessage = useToastMessageDispatch();
+
+ const updateEmail = useMethod('cloud:updateEmail');
+ const connectWorkspace = useMethod('cloud:connectWorkspace');
+
+ const [isProcessing, setProcessing] = useSafely(useState(false));
+ const [email, setEmail] = useState(initialEmail);
+ const [token, setToken] = useState(initialToken);
+
+ const supportMailtoUrl = useMemo(() => {
+ const subject = encodeURIComponent('Self Hosted Registration');
+ const body = encodeURIComponent([
+ `WorkspaceId: ${ workspaceId }`,
+ `Deployment Id: ${ uniqueId }`,
+ 'Issue: ',
+ ].join('\r\n'));
+ return `mailto:${ supportEmailAddress }?subject=${ subject }&body=${ body }`;
+ }, [workspaceId, uniqueId]);
+
+ const handleEmailChange = ({ currentTarget: { value } }) => {
+ setEmail(value);
+ };
+
+ const handleTokenChange = ({ currentTarget: { value } }) => {
+ setToken(value);
+ };
+
+ const handleUpdateEmailButtonClick = async () => {
+ setProcessing(true);
+
+ try {
+ await updateEmail(email, false);
+ dispatchToastMessage({ type: 'success', message: t('Saved') });
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ } finally {
+ setProcessing(false);
+ }
+ };
+
+ const handleResendEmailButtonClick = async () => {
+ setProcessing(true);
+
+ try {
+ await updateEmail(email, true);
+ dispatchToastMessage({ type: 'success', message: t('Requested') });
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ } finally {
+ setProcessing(false);
+ }
+ };
+
+ const handleConnectButtonClick = async () => {
+ setProcessing(true);
+
+ try {
+ const isConnected = await connectWorkspace(token);
+
+ if (!isConnected) {
+ throw Error(t('An error occured connecting'));
+ }
+
+ dispatchToastMessage({ type: 'success', message: t('Connected') });
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ } finally {
+ await (onRegisterStatusChange && onRegisterStatusChange());
+ setProcessing(false);
+ }
+ };
+
+ const emailInputId = useUniqueId();
+ const tokenInputId = useUniqueId();
+
+ return
+
+
+ {t('Email')}
+
+
+
+ {t('Cloud_address_to_send_registration_to')}
+
+
+
+ {t('Cloud_update_email')}
+ {t('Cloud_resend_email')}
+
+
+
+ {t('Token')}
+
+
+
+ {t('Cloud_manually_input_token')}
+
+
+
+ {t('Connect')}
+
+
+
+ {t('Cloud_connect_support')}: {supportEmailAddress}
+
+
+ ;
+}
+
+export default WorkspaceRegistrationSection;
diff --git a/client/admin/cloud/constants.js b/client/admin/cloud/constants.js
new file mode 100644
index 000000000000..c5ed79a1477e
--- /dev/null
+++ b/client/admin/cloud/constants.js
@@ -0,0 +1,3 @@
+export const cloudConsoleUrl = 'https://cloud.rocket.chat';
+export const supportEmailAddress = 'support@rocket.chat';
+export const statusPageUrl = 'https://status.rocket.chat';
diff --git a/client/admin/customSounds/EditSound.js b/client/admin/customSounds/EditSound.js
new file mode 100644
index 000000000000..0496a617fb0e
--- /dev/null
+++ b/client/admin/customSounds/EditSound.js
@@ -0,0 +1,202 @@
+import React, { useCallback, useState, useMemo, useEffect } from 'react';
+import { Box, Button, ButtonGroup, Margins, TextInput, Field, Icon, Skeleton, Throbber, InputBox } from '@rocket.chat/fuselage';
+
+import { useTranslation } from '../../contexts/TranslationContext';
+import { useMethod } from '../../contexts/ServerContext';
+import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext';
+import { Modal } from '../../components/basic/Modal';
+import { useFileInput } from '../../hooks/useFileInput';
+import { useEndpointDataExperimental, ENDPOINT_STATES } from '../../hooks/useEndpointDataExperimental';
+import { validate, createSoundData } from './lib';
+
+const DeleteWarningModal = ({ onDelete, onCancel, ...props }) => {
+ const t = useTranslation();
+ return
+
+
+ {t('Are_you_sure')}
+
+
+
+ {t('Custom_Sound_Status_Delete_Warning')}
+
+
+
+ {t('Cancel')}
+ {t('Delete')}
+
+
+ ;
+};
+
+const SuccessModal = ({ onClose, ...props }) => {
+ const t = useTranslation();
+ return
+
+
+ {t('Deleted')}
+
+
+
+ {t('Custom_Sound_Has_Been_Deleted')}
+
+
+
+ {t('Ok')}
+
+
+ ;
+};
+
+export function EditSound({ _id, cache, ...props }) {
+ const t = useTranslation();
+ const query = useMemo(() => ({
+ query: JSON.stringify({ _id }),
+ }), [_id]);
+
+ const { data, state, error } = useEndpointDataExperimental('custom-sounds.list', query);
+
+
+ if (state === ENDPOINT_STATES.LOADING) {
+ return
+
+
+
+
+
+
+
+
+
+
+
+ ;
+ }
+
+ if (error || !data || data.sounds.length < 1) {
+ return {t('Custom_User_Status_Error_Invalid_User_Status')} ;
+ }
+
+ return ;
+}
+
+export function EditCustomSound({ close, onChange, data, ...props }) {
+ const t = useTranslation();
+ const dispatchToastMessage = useToastMessageDispatch();
+
+ const { _id, name: previousName } = data || {};
+ const previousSound = data || {};
+
+ const [name, setName] = useState('');
+ const [sound, setSound] = useState();
+ const [modal, setModal] = useState();
+
+ useEffect(() => {
+ setName(previousName || '');
+ setSound(previousSound || '');
+ }, [previousName, previousSound, _id]);
+
+ const deleteCustomSound = useMethod('deleteCustomSound');
+ const uploadCustomSound = useMethod('uploadCustomSound');
+ const insertOrUpdateSound = useMethod('insertOrUpdateSound');
+
+ const handleChangeFile = (soundFile) => {
+ setSound(soundFile);
+ };
+
+ const hasUnsavedChanges = useMemo(() => previousName !== name || previousSound !== sound, [name, sound]);
+
+ const saveAction = async (sound) => {
+ const soundData = createSoundData(sound, name, { previousName, previousSound, _id });
+ const validation = validate(soundData, sound);
+ if (validation.length === 0) {
+ let soundId;
+ try {
+ soundId = await insertOrUpdateSound(soundData);
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ }
+
+ soundData._id = soundId;
+ soundData.random = Math.round(Math.random() * 1000);
+
+ if (sound && sound !== previousSound) {
+ dispatchToastMessage({ type: 'success', message: t('Uploading_file') });
+
+ const reader = new FileReader();
+ reader.readAsBinaryString(sound);
+ reader.onloadend = () => {
+ try {
+ uploadCustomSound(reader.result, sound.type, soundData);
+ return dispatchToastMessage({ type: 'success', message: t('File_uploaded') });
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ }
+ };
+ }
+ }
+
+ validation.forEach((error) => dispatchToastMessage({ type: 'error', message: t('error-the-field-is-required', { field: t(error) }) }));
+ };
+
+ const handleSave = useCallback(async () => {
+ saveAction(sound);
+ onChange();
+ }, [name, _id, sound]);
+
+ const onDeleteConfirm = useCallback(async () => {
+ try {
+ await deleteCustomSound(_id);
+ setModal(() => { setModal(undefined); close(); onChange(); }}/>);
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ onChange();
+ }
+ }, [_id]);
+
+ const openConfirmDelete = () => setModal(() => setModal(undefined)}/>);
+
+
+ const clickUpload = useFileInput(handleChangeFile, 'audio/mp3');
+
+
+ return <>
+
+
+
+ {t('Name')}
+
+ setName(e.currentTarget.value)} placeholder={t('Name')} />
+
+
+
+
+ {t('Sound_File_mp3')}
+
+
+
+ {(sound && sound.name) || 'none'}
+
+
+
+
+
+
+
+ {t('Cancel')}
+ {t('Save')}
+
+
+
+
+
+
+ {t('Delete')}
+
+
+
+
+
+ { modal }
+ >;
+}
diff --git a/client/admin/customSounds/NewSound.js b/client/admin/customSounds/NewSound.js
new file mode 100644
index 000000000000..b77671f1b07a
--- /dev/null
+++ b/client/admin/customSounds/NewSound.js
@@ -0,0 +1,101 @@
+import React, { useState, useCallback } from 'react';
+import { Field, TextInput, Box, Icon, Margins, Button, ButtonGroup } from '@rocket.chat/fuselage';
+
+import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext';
+import { useTranslation } from '../../contexts/TranslationContext';
+import { useMethod } from '../../contexts/ServerContext';
+import { useFileInput } from '../../hooks/useFileInput';
+import { validate, createSoundData } from './lib';
+
+export function NewSound({ goToNew, close, onChange, ...props }) {
+ const t = useTranslation();
+ const dispatchToastMessage = useToastMessageDispatch();
+
+ const [name, setName] = useState('');
+ const [sound, setSound] = useState();
+
+ const uploadCustomSound = useMethod('uploadCustomSound');
+
+ const insertOrUpdateSound = useMethod('insertOrUpdateSound');
+
+ const handleChangeFile = (soundFile) => {
+ setSound(soundFile);
+ };
+
+ const clickUpload = useFileInput(handleChangeFile, 'audio/mp3');
+
+ const saveAction = async (name, soundFile) => {
+ const soundData = createSoundData(soundFile, name);
+ const validation = validate(soundData, sound);
+ if (validation.length === 0) {
+ let soundId;
+ try {
+ soundId = await insertOrUpdateSound(soundData);
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ }
+
+ soundData._id = soundId;
+ soundData.random = Math.round(Math.random() * 1000);
+
+ if (soundId) {
+ dispatchToastMessage({ type: 'success', message: t('Uploading_file') });
+
+ const reader = new FileReader();
+ reader.readAsBinaryString(soundFile);
+ reader.onloadend = () => {
+ try {
+ uploadCustomSound(reader.result, soundFile.type, soundData);
+ dispatchToastMessage({ type: 'success', message: t('File_uploaded') });
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ }
+ };
+ }
+ return soundId;
+ }
+ validation.forEach((error) => { throw new Error({ type: 'error', message: t('error-the-field-is-required', { field: t(error) }) }); });
+ };
+
+ const handleSave = useCallback(async () => {
+ try {
+ const result = await saveAction(
+ name,
+ sound,
+ );
+ dispatchToastMessage({ type: 'success', message: t('Custom_Sound_Updated_Successfully') });
+ goToNew(result)();
+ onChange();
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ }
+ }, [name, sound]);
+
+ return
+
+
+ {t('Name')}
+
+ setName(e.currentTarget.value)} placeholder={t('Name')} />
+
+
+
+ {t('Sound_File_mp3')}
+
+
+
+ {(sound && sound.name) || 'none'}
+
+
+
+
+
+
+ {t('Cancel')}
+ {t('Save')}
+
+
+
+
+ ;
+}
diff --git a/client/admin/integrations/IncomingWebhookForm.js b/client/admin/integrations/IncomingWebhookForm.js
new file mode 100644
index 000000000000..f25ba8b304f0
--- /dev/null
+++ b/client/admin/integrations/IncomingWebhookForm.js
@@ -0,0 +1,165 @@
+import React, { useMemo } from 'react';
+import { Field, TextInput, Box, ToggleSwitch, Icon, TextAreaInput, FieldGroup, Margins } from '@rocket.chat/fuselage';
+
+import { useTranslation } from '../../contexts/TranslationContext';
+import { useAbsoluteUrl } from '../../contexts/ServerContext';
+import { useHilightCode } from '../../hooks/useHilightCode';
+import { useExampleData } from './exampleIncomingData';
+import Page from '../../components/basic/Page';
+
+export default function IncomingWebhookForm({ formValues, formHandlers, extraData = {}, append, ...props }) {
+ const t = useTranslation();
+
+ const hilightCode = useHilightCode();
+
+ const absoluteUrl = useAbsoluteUrl();
+
+ const {
+ enabled,
+ channel,
+ username,
+ name,
+ alias,
+ avatarUrl,
+ emoji,
+ scriptEnabled,
+ script,
+ } = formValues;
+
+ const {
+ handleEnabled,
+ handleChannel,
+ handleUsername,
+ handleName,
+ handleAlias,
+ handleAvatarUrl,
+ handleEmoji,
+ handleScriptEnabled,
+ handleScript,
+ } = formHandlers;
+
+ const url = absoluteUrl(`hooks/${ extraData._id }/${ extraData.token }`);
+
+ const [exampleData, curlData] = useExampleData({
+ aditionalFields: {
+ ...alias && { alias },
+ ...emoji && { emoji },
+ ...avatarUrl && { avatar: avatarUrl },
+ },
+ url,
+ }, [alias, emoji, avatarUrl]);
+
+ const hilightedExampleJson = hilightCode('json', JSON.stringify(exampleData, null, 2));
+
+ return
+
+
+ {useMemo(() =>
+
+ {t('Enabled')}
+
+
+ , [enabled, handleEnabled])}
+ {useMemo(() =>
+ {t('Name_optional')}
+
+
+
+ {t('You_should_name_it_to_easily_manage_your_integrations')}
+ , [name, handleName])}
+ {useMemo(() =>
+ {t('Post_to_Channel')}
+
+ }/>
+
+ {t('Messages_that_are_sent_to_the_Incoming_WebHook_will_be_posted_here')}
+
+ , [channel, handleChannel])}
+ {useMemo(() =>
+ {t('Post_as')}
+
+ }/>
+
+ {t('Choose_the_username_that_this_integration_will_post_as')}
+ {t('Should_exists_a_user_with_this_username')}
+ , [username, handleUsername])}
+ {useMemo(() =>
+ {`${ t('Alias') } (${ t('optional') })`}
+
+ }/>
+
+ {t('Choose_the_alias_that_will_appear_before_the_username_in_messages')}
+ , [alias, handleAlias])}
+ {useMemo(() =>
+ {`${ t('Avatar_URL') } (${ t('optional') })`}
+
+ }/>
+
+ {t('You_can_change_a_different_avatar_too')}
+ {t('Should_be_a_URL_of_an_image')}
+ , [avatarUrl, handleAvatarUrl])}
+ {useMemo(() =>
+ {`${ t('Emoji') } (${ t('optional') })`}
+
+ }/>
+
+ {t('You_can_use_an_emoji_as_avatar')}
+
+ , [emoji, handleEmoji])}
+ {useMemo(() =>
+
+ {t('Script_Enabled')}
+
+
+ , [scriptEnabled, handleScriptEnabled])}
+ {useMemo(() =>
+ {t('Script')}
+
+ }/>
+
+ , [script, handleScript])}
+ {useMemo(() => !extraData._id && <>
+ {t('Webhook_URL')}
+
+ } disabled/>
+
+ {t('Send_your_JSON_payloads_to_this_URL')}
+
+
+ {t('Token')}
+
+ } disabled/>
+
+ >, [extraData._id])}
+ {useMemo(() => extraData._id && <>
+ {t('Webhook_URL')}
+
+ }/>
+
+ {t('Send_your_JSON_payloads_to_this_URL')}
+
+
+ {t('Token')}
+
+ }/>
+
+ >, [url, extraData._id, extraData.token])}
+ {useMemo(() =>
+ {t('Example_payload')}
+
+
+
+
+
+ , [hilightedExampleJson])}
+ {useMemo(() => extraData._id &&
+ {t('Curl')}
+
+ }/>
+
+ , [curlData])}
+ { append }
+
+
+ ;
+}
diff --git a/client/admin/integrations/IntegrationsPage.js b/client/admin/integrations/IntegrationsPage.js
new file mode 100644
index 000000000000..ec9dc1adb688
--- /dev/null
+++ b/client/admin/integrations/IntegrationsPage.js
@@ -0,0 +1,56 @@
+import { Button, ButtonGroup, Icon, Tabs } from '@rocket.chat/fuselage';
+import React, { useEffect, useCallback } from 'react';
+
+import Page from '../../components/basic/Page';
+import { useTranslation } from '../../contexts/TranslationContext';
+import { useRoute, useRouteParameter } from '../../contexts/RouterContext';
+import IntegrationsTable from './IntegrationsTable';
+import NewZapier from './new/NewZapier';
+import NewBot from './new/NewBot';
+
+function IntegrationsPage() {
+ const t = useTranslation();
+
+ const router = useRoute('admin-integrations');
+
+ const handleNewButtonClick = useCallback(() => {
+ router.push({ context: 'new', type: 'incoming' });
+ }, []);
+
+ const context = useRouteParameter('context');
+ useEffect(() => {
+ if (!context) {
+ router.push({ context: 'webhook-incoming' });
+ }
+ }, [context]);
+
+ const showTable = !['zapier', 'bots'].includes(context);
+
+ const goToIncoming = useCallback(() => router.push({ context: 'webhook-incoming' }), []);
+ const goToOutgoing = useCallback(() => router.push({ context: 'webhook-outgoing' }), []);
+ const goToZapier = useCallback(() => router.push({ context: 'zapier' }), []);
+ const goToBots = useCallback(() => router.push({ context: 'bots' }), []);
+
+ return
+
+
+
+ {t('New')}
+
+
+
+
+ {t('Incoming')}
+ {t('Outgoing')}
+ {t('Zapier')}
+ {t('Bots')}
+
+
+ {context === 'zapier' && }
+ {context === 'bots' && }
+ {showTable && }
+
+ ;
+}
+
+export default IntegrationsPage;
diff --git a/client/admin/integrations/IntegrationsRoute.js b/client/admin/integrations/IntegrationsRoute.js
new file mode 100644
index 000000000000..e1af75ed11e0
--- /dev/null
+++ b/client/admin/integrations/IntegrationsRoute.js
@@ -0,0 +1,40 @@
+import React from 'react';
+
+import { useAtLeastOnePermission } from '../../contexts/AuthorizationContext';
+import { useRouteParameter } from '../../contexts/RouterContext';
+import NotAuthorizedPage from '../NotAuthorizedPage';
+import IntegrationsPage from './IntegrationsPage';
+import NewIntegrationsPage from './new/NewIntegrationsPage';
+import EditIntegrationsPage from './edit/EditIntegrationsPage';
+import OutgoingWebhookHistoryPage from './edit/OutgoingWebhookHistoryPage';
+
+function IntegrationsRoute() {
+ const canViewIntegrationsPage = useAtLeastOnePermission([
+ 'manage-incoming-integrations',
+ 'manage-outgoing-integrations',
+ 'manage-own-incoming-integrations',
+ 'manage-own-outgoing-integrations',
+ ]);
+
+ const context = useRouteParameter('context');
+
+ if (!canViewIntegrationsPage) {
+ return ;
+ }
+
+ if (context === 'new') {
+ return ;
+ }
+
+ if (context === 'edit') {
+ return ;
+ }
+
+ if (context === 'history') {
+ return ;
+ }
+
+ return ;
+}
+
+export default IntegrationsRoute;
diff --git a/client/admin/integrations/IntegrationsTable.js b/client/admin/integrations/IntegrationsTable.js
new file mode 100644
index 000000000000..582485154019
--- /dev/null
+++ b/client/admin/integrations/IntegrationsTable.js
@@ -0,0 +1,97 @@
+import { Box, Table, TextInput, Icon } from '@rocket.chat/fuselage';
+import { useDebouncedValue, useResizeObserver } from '@rocket.chat/fuselage-hooks';
+import React, { useMemo, useCallback, useState, useEffect } from 'react';
+
+import { GenericTable, Th } from '../../../app/ui/client/components/GenericTable';
+import { useTranslation } from '../../contexts/TranslationContext';
+import { useRoute } from '../../contexts/RouterContext';
+import { useEndpointDataExperimental } from '../../hooks/useEndpointDataExperimental';
+import { useFormatDateAndTime } from '../../hooks/useFormatDateAndTime';
+
+const style = { whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' };
+
+const FilterByTypeAndText = React.memo(({ setFilter, ...props }) => {
+ const t = useTranslation();
+
+ const [text, setText] = useState('');
+
+ const handleChange = useCallback((event) => setText(event.currentTarget.value), []);
+
+ useEffect(() => {
+ setFilter({ text });
+ }, [text]);
+
+ return
+ } onChange={handleChange} value={text} />
+ ;
+});
+
+const useQuery = (params, sort) => useMemo(() => ({
+ query: JSON.stringify({ name: { $regex: params.text || '', $options: 'i' }, type: params.type }),
+ sort: JSON.stringify({ [sort[0]]: sort[1] === 'asc' ? 1 : -1 }),
+ ...params.itemsPerPage && { count: params.itemsPerPage },
+ ...params.current && { offset: params.current },
+}), [JSON.stringify(params), JSON.stringify(sort)]);
+
+const useResizeInlineBreakpoint = (sizes = [], debounceDelay = 0) => {
+ const { ref, borderBoxSize } = useResizeObserver({ debounceDelay });
+ const inlineSize = borderBoxSize ? borderBoxSize.inlineSize : 0;
+ sizes = useMemo(() => sizes.map((current) => (inlineSize ? inlineSize > current : true)), [inlineSize]);
+ return [ref, ...sizes];
+};
+
+export function IntegrationsTable({ type }) {
+ const t = useTranslation();
+ const formatDateAndTime = useFormatDateAndTime();
+ const [ref, isBig] = useResizeInlineBreakpoint([700], 200);
+
+ const [params, setParams] = useState({ text: '', current: 0, itemsPerPage: 25 });
+ const [sort, setSort] = useState(['name', 'asc']);
+
+ const debouncedText = useDebouncedValue(params.text, 500);
+ const debouncedSort = useDebouncedValue(sort, 500);
+ const query = useQuery({ ...params, text: debouncedText, type }, debouncedSort);
+
+ const { data } = useEndpointDataExperimental('integrations.list', query);
+
+ const router = useRoute('admin-integrations');
+
+ const onClick = (_id, type) => () => router.push({
+ context: 'edit',
+ type: type === 'webhook-incoming' ? 'incoming' : 'outgoing',
+ id: _id,
+ });
+
+ const onHeaderClick = useCallback((id) => {
+ const [sortBy, sortDirection] = sort;
+
+ if (sortBy === id) {
+ setSort([id, sortDirection === 'asc' ? 'desc' : 'asc']);
+ return;
+ }
+ setSort([id, 'asc']);
+ }, [sort]);
+
+ const header = useMemo(() => [
+ {t('Name')} ,
+ {t('Post_to')} ,
+ {t('Created_by')} ,
+ isBig && {t('Created_at')} ,
+ {t('Post_as')} ,
+ ].filter(Boolean), [sort, isBig]);
+
+ const renderRow = useCallback(({ name, _id, type, username, _createdAt, _createdBy: { username: createdBy }, channel }) => {
+ const handler = useMemo(() => onClick(_id, type), []);
+ return
+ {name}
+ {channel.join(', ')}
+ {createdBy}
+ {isBig && {formatDateAndTime(_createdAt)} }
+ {username}
+ ;
+ }, []);
+
+ return ;
+}
+
+export default IntegrationsTable;
diff --git a/client/admin/integrations/OutgoiongWebhookForm.js b/client/admin/integrations/OutgoiongWebhookForm.js
new file mode 100644
index 000000000000..2f6fe9fa4900
--- /dev/null
+++ b/client/admin/integrations/OutgoiongWebhookForm.js
@@ -0,0 +1,264 @@
+import {
+ Field,
+ TextInput,
+ Box,
+ ToggleSwitch,
+ Icon,
+ TextAreaInput,
+ FieldGroup,
+ Margins,
+ Select,
+ Accordion,
+} from '@rocket.chat/fuselage';
+import React, { useMemo } from 'react';
+
+import { useHilightCode } from '../../hooks/useHilightCode';
+import { useExampleData } from './exampleIncomingData';
+import { useTranslation } from '../../contexts/TranslationContext';
+import Page from '../../components/basic/Page';
+import { integrations as eventList } from '../../../app/integrations/lib/rocketchat';
+
+
+export default function OutgoingWebhookForm({ formValues, formHandlers, append, ...props }) {
+ const t = useTranslation();
+
+ const {
+ enabled,
+ impersonateUser,
+ event,
+ urls,
+ triggerWords,
+ targetRoom,
+ channel,
+ username,
+ name,
+ alias,
+ avatar: avatarUrl,
+ emoji,
+ token,
+ scriptEnabled,
+ script,
+ retryFailedCalls,
+ retryCount,
+ retryDelay,
+ triggerWordAnywhere,
+ } = formValues;
+
+ const {
+ runOnEdits,
+ handleEvent,
+ handleEnabled,
+ handleName,
+ handleChannel,
+ handleTriggerWords,
+ handleTargetRoom,
+ handleUrls,
+ handleImpersonateUser,
+ handleUsername,
+ handleAlias,
+ handleAvatar,
+ handleEmoji,
+ handleToken,
+ handleScriptEnabled,
+ handleScript,
+ handleRetryFailedCalls,
+ handleRetryCount,
+ handleRetryDelay,
+ handleTriggerWordAnywhere,
+ handleRunOnEdits,
+ } = formHandlers;
+
+ const retryDelayOptions = useMemo(() => [
+ ['powers-of-ten', t('powers-of-ten')],
+ ['powers-of-two', t('powers-of-two')],
+ ['increments-of-two', t('increments-of-two')],
+ ], []);
+
+ const { outgoingEvents } = eventList;
+
+ const eventOptions = useMemo(() => Object.entries(outgoingEvents).map(([key, val]) => [key, t(val.label)]), []);
+
+ const hilightCode = useHilightCode();
+
+ const showChannel = useMemo(() => outgoingEvents[event].use.channel, [event]);
+ const showTriggerWords = useMemo(() => outgoingEvents[event].use.triggerWords, [event]);
+ const showTargetRoom = useMemo(() => outgoingEvents[event].use.targetRoom, [event]);
+
+ const [exampleData] = useExampleData({
+ aditionalFields: {
+ ...alias && { alias },
+ ...emoji && { emoji },
+ ...avatarUrl && { avatar: avatarUrl },
+ },
+ url: null,
+ }, [alias, emoji, avatarUrl]);
+
+ const hilightedExampleJson = hilightCode('json', JSON.stringify(exampleData, null, 2));
+
+ return
+
+
+
+ { useMemo(() =>
+ {t('Event_Trigger')}
+
+
+
+ {t('Event_Trigger_Description')}
+ , [event]) }
+ { useMemo(() =>
+
+ {t('Enabled')}
+
+
+ , [enabled]) }
+ { useMemo(() =>
+ {t('Name_optional')}
+
+
+
+ {t('You_should_name_it_to_easily_manage_your_integrations')}
+ , [name])}
+ { useMemo(() => showChannel &&
+ {t('Channel')}
+
+ }/>
+
+ {t('Channel_to_listen_on')}
+
+
+ , [showChannel, channel])}
+ { useMemo(() => showTriggerWords &&
+ {t('Trigger_Words')}
+
+
+
+ {t('When_a_line_starts_with_one_of_there_words_post_to_the_URLs_below')}
+ {t('Separate_multiple_words_with_commas')}
+ , [triggerWords])}
+ { useMemo(() => showTargetRoom &&
+ {t('TargetRoom')}
+
+
+
+ {t('TargetRoom_Description')}
+
+ , [showTargetRoom, targetRoom])}
+ { useMemo(() =>
+ {t('URLs')}
+
+ }/>
+
+ , [urls])}
+ { useMemo(() =>
+
+ {t('Impersonate_user')}
+
+
+ , [impersonateUser])}
+ { useMemo(() =>
+ {t('Post_as')}
+
+ }/>
+
+ {t('Choose_the_username_that_this_integration_will_post_as')}
+ {t('Should_exists_a_user_with_this_username')}
+ , [username])}
+ { useMemo(() =>
+ {`${ t('Alias') } (${ t('optional') })`}
+
+ }/>
+
+ {t('Choose_the_alias_that_will_appear_before_the_username_in_messages')}
+ , [alias])}
+ { useMemo(() =>
+ {`${ t('Avatar_URL') } (${ t('optional') })`}
+
+ }/>
+
+ {t('You_can_change_a_different_avatar_too')}
+ {t('Should_be_a_URL_of_an_image')}
+ , [avatarUrl])}
+ { useMemo(() =>
+ {`${ t('Emoji') } (${ t('optional') })`}
+
+ }/>
+
+ {t('You_can_use_an_emoji_as_avatar')}
+
+ , [emoji])}
+ { useMemo(() =>
+ {`${ t('Token') } (${ t('Optional') })`}
+
+ }/>
+
+ , [token])}
+ { useMemo(() =>
+
+ {t('Script_Enabled')}
+
+
+ , [scriptEnabled])}
+ { useMemo(() =>
+ {t('Script')}
+
+ }/>
+
+ , [script])}
+ { useMemo(() =>
+ {t('Responding')}
+ {t('Response_description_pre')}
+
+
+
+
+
+ {t('Response_description_post')}
+ , [hilightedExampleJson])}
+
+
+
+ { useMemo(() =>
+
+ {t('Integration_Retry_Failed_Url_Calls')}
+
+
+ {t('Integration_Retry_Failed_Url_Calls_Description')}
+ , [retryFailedCalls])}
+ { useMemo(() =>
+ {t('Retry_Count')}
+
+
+
+ {t('Integration_Retry_Count_Description')}
+ , [retryCount])}
+ { useMemo(() =>
+ {t('Integration_Retry_Delay')}
+
+
+
+
+ , [retryDelay])}
+ { useMemo(() => event === 'sendMessage' &&
+
+
+ {t('Integration_Word_Trigger_Placement')}
+
+
+ {t('Integration_Word_Trigger_Placement_Description')}
+
+
+
+ {t('Integration_Word_Trigger_Placement')}
+
+
+ {t('Integration_Run_When_Message_Is_Edited_Description')}
+
+ , [triggerWordAnywhere, runOnEdits])}
+
+
+ { append }
+
+
+ ;
+}
diff --git a/client/admin/integrations/edit/EditIncomingWebhook.js b/client/admin/integrations/edit/EditIncomingWebhook.js
new file mode 100644
index 000000000000..2a6363d9f224
--- /dev/null
+++ b/client/admin/integrations/edit/EditIncomingWebhook.js
@@ -0,0 +1,105 @@
+import React, { useMemo, useState } from 'react';
+import { Field, Box, Headline, Skeleton, Margins, Button } from '@rocket.chat/fuselage';
+
+import { SuccessModal, DeleteWarningModal } from './EditIntegrationsPage';
+import { useTranslation } from '../../../contexts/TranslationContext';
+import { useEndpointDataExperimental, ENDPOINT_STATES } from '../../../hooks/useEndpointDataExperimental';
+import { useMethod } from '../../../contexts/ServerContext';
+import { useEndpointAction } from '../../../hooks/useEndpointAction';
+import { useRoute } from '../../../contexts/RouterContext';
+import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext';
+import { useForm } from '../../../hooks/useForm';
+import IncomingWebhookForm from '../IncomingWebhookForm';
+
+export default function EditIncomingWebhookWithData({ integrationId, ...props }) {
+ const t = useTranslation();
+ const [cache, setCache] = useState();
+
+ const { data, state, error } = useEndpointDataExperimental('integrations.get', useMemo(() => ({ integrationId }), [integrationId, cache]));
+
+ const onChange = () => setCache(new Date());
+
+ if (state === ENDPOINT_STATES.LOADING) {
+ return
+
+
+
+
+
+
+ ;
+ }
+
+ if (error) {
+ return {t('Oops_page_not_found')} ;
+ }
+
+ return ;
+}
+
+const getInitialValue = (data) => {
+ const initialValue = {
+ enabled: data.enabled,
+ channel: data.channel.join(', ') ?? '',
+ username: data.username ?? '',
+ name: data.name ?? '',
+ alias: data.alias ?? '',
+ avatarUrl: data.avatarUrl ?? '',
+ emoji: data.emoji ?? '',
+ scriptEnabled: data.scriptEnabled,
+ script: data.script,
+ };
+ return initialValue;
+};
+
+function EditIncomingWebhook({ data, onChange, ...props }) {
+ const t = useTranslation();
+ const dispatchToastMessage = useToastMessageDispatch();
+
+ const { values: formValues, handlers: formHandlers, reset } = useForm(getInitialValue(data));
+ const [modal, setModal] = useState();
+
+ const deleteQuery = useMemo(() => ({ type: 'webhook-incoming', integrationId: data._id }), [data._id]);
+ const deleteIntegration = useEndpointAction('POST', 'integrations.remove', deleteQuery);
+ const saveIntegration = useMethod('updateIncomingIntegration');
+
+ const router = useRoute('admin-integrations');
+
+ const handleDeleteIntegration = () => {
+ const closeModal = () => setModal();
+ const onDelete = async () => {
+ const result = await deleteIntegration();
+ if (result.success) { setModal( { closeModal(); router.push({}); }}/>); }
+ };
+
+ setModal( );
+ };
+
+ const handleSave = async () => {
+ try {
+ await saveIntegration(data._id, { ...formValues });
+ dispatchToastMessage({ type: 'success', message: t('Integration_updated') });
+ onChange();
+ } catch (e) {
+ dispatchToastMessage({ type: 'error', message: e });
+ }
+ };
+
+ const actionButtons = useMemo(() =>
+
+
+
+ {t('Reset')}
+ {t('Save')}
+
+
+ {t('Delete')}
+
+ );
+
+
+ return <>
+
+ { modal }
+ >;
+}
diff --git a/client/admin/integrations/edit/EditIntegrationsPage.js b/client/admin/integrations/edit/EditIntegrationsPage.js
new file mode 100644
index 000000000000..3ffdb8168c79
--- /dev/null
+++ b/client/admin/integrations/edit/EditIntegrationsPage.js
@@ -0,0 +1,82 @@
+import { Button, ButtonGroup, Icon } from '@rocket.chat/fuselage';
+import React, { useCallback } from 'react';
+
+import Page from '../../../components/basic/Page';
+import EditIncomingWebhookWithData from './EditIncomingWebhook';
+import EditOutgoingWebhookWithData from './EditOutgoingWebhook';
+import { Modal } from '../../../components/basic/Modal';
+import { useTranslation } from '../../../contexts/TranslationContext';
+import { useRouteParameter, useRoute } from '../../../contexts/RouterContext';
+
+export const DeleteWarningModal = ({ onDelete, onCancel, ...props }) => {
+ const t = useTranslation();
+ return
+
+
+ {t('Are_you_sure')}
+
+
+
+ {t('Integration_Delete_Warning')}
+
+
+
+ {t('Cancel')}
+ {t('Delete')}
+
+
+ ;
+};
+
+export const SuccessModal = ({ onClose, ...props }) => {
+ const t = useTranslation();
+ return
+
+
+ {t('Deleted')}
+
+
+
+ {t('Your_entry_has_been_deleted')}
+
+
+
+ {t('Ok')}
+
+
+ ;
+};
+
+export default function NewIntegrationsPage({ ...props }) {
+ const t = useTranslation();
+
+ const router = useRoute('admin-integrations');
+
+ const type = useRouteParameter('type');
+ const integrationId = useRouteParameter('id');
+
+ const handleClickReturn = useCallback(() => {
+ router.push({ });
+ }, []);
+
+ const handleClickHistory = useCallback(() => {
+ router.push({ context: 'history', type: 'outgoing', id: integrationId });
+ }, [integrationId]);
+
+ return
+
+
+
+ {t('Back')}
+
+ {type === 'outgoing' && {t('History')} }
+
+
+
+ {
+ (type === 'outgoing' && )
+ || (type === 'incoming' && )
+ }
+
+ ;
+}
diff --git a/client/admin/integrations/edit/EditOutgoingWebhook.js b/client/admin/integrations/edit/EditOutgoingWebhook.js
new file mode 100644
index 000000000000..7c20820b2a82
--- /dev/null
+++ b/client/admin/integrations/edit/EditOutgoingWebhook.js
@@ -0,0 +1,134 @@
+import React, { useMemo, useState } from 'react';
+import {
+ Field,
+ Box,
+ Headline,
+ Skeleton,
+ Margins,
+ Button,
+} from '@rocket.chat/fuselage';
+
+import { SuccessModal, DeleteWarningModal } from './EditIntegrationsPage';
+import { useTranslation } from '../../../contexts/TranslationContext';
+import { useEndpointDataExperimental, ENDPOINT_STATES } from '../../../hooks/useEndpointDataExperimental';
+import { useEndpointAction } from '../../../hooks/useEndpointAction';
+import { useRoute } from '../../../contexts/RouterContext';
+import { useMethod } from '../../../contexts/ServerContext';
+import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext';
+import OutgoingWebhookForm from '../OutgoiongWebhookForm';
+import { useForm } from '../../../hooks/useForm';
+
+export default function EditOutgoingWebhookWithData({ integrationId, ...props }) {
+ const t = useTranslation();
+ const [cache, setCache] = useState();
+
+ const { data, state, error } = useEndpointDataExperimental('integrations.get', useMemo(() => ({ integrationId }), [integrationId, cache]));
+
+ const onChange = () => setCache(new Date());
+
+ if (state === ENDPOINT_STATES.LOADING) {
+ return
+
+
+
+
+
+
+ ;
+ }
+
+ if (error) {
+ return {t('Oops_page_not_found')} ;
+ }
+
+ return ;
+}
+
+const getInitialValue = (data) => {
+ const initialValue = {
+ enabled: data.enabled ?? true,
+ impersonateUser: data.impersonateUser,
+ event: data.event,
+ token: data.token,
+ urls: data.urls.join('\n') ?? '',
+ triggerWords: data.triggerWords?.join('; ') ?? '',
+ targetRoom: data.targetRoom ?? '',
+ channel: data.channel.join(', ') ?? '',
+ username: data.username ?? '',
+ name: data.name ?? '',
+ alias: data.alias ?? '',
+ avatarUrl: data.avatarUrl ?? '',
+ emoji: data.emoji ?? '',
+ scriptEnabled: data.scriptEnabled ?? false,
+ script: data.script ?? '',
+ retryFailedCalls: data.retryFailedCalls ?? true,
+ retryCount: data.retryCount ?? 5,
+ retryDelay: data.retryDelay ?? 'power-of-ten',
+ triggerrWordAnywhere: data.triggerrWordAnywhere ?? false,
+ runOnEdits: data.runOnEdits ?? true,
+ };
+ return initialValue;
+};
+
+function EditOutgoingWebhook({ data, onChange, setSaveAction, ...props }) {
+ const t = useTranslation();
+ const dispatchToastMessage = useToastMessageDispatch();
+
+ const { handlers: formHandlers, values: formValues, reset } = useForm(getInitialValue(data));
+ const [modal, setModal] = useState();
+
+ const saveIntegration = useMethod('updateOutgoingIntegration');
+
+ const router = useRoute('admin-integrations');
+
+ const deleteQuery = useMemo(() => ({ type: 'webhook-outgoing', integrationId: data._id }), [data._id]);
+ const deleteIntegration = useEndpointAction('POST', 'integrations.remove', deleteQuery);
+
+ const handleDeleteIntegration = () => {
+ const closeModal = () => setModal();
+ const onDelete = async () => {
+ const result = await deleteIntegration();
+ if (result.success) { setModal( { closeModal(); router.push({}); }}/>); }
+ };
+
+ setModal( );
+ };
+
+ const {
+ urls,
+ triggerWords,
+ } = formValues;
+
+ const handleSave = async () => {
+ try {
+ await saveIntegration(data._id, {
+ ...formValues,
+ triggerWords: triggerWords.split(';'),
+ urls: urls.split('\n'),
+ });
+
+ dispatchToastMessage({ type: 'success', message: t('Integration_updated') });
+ onChange();
+ } catch (e) {
+ dispatchToastMessage({ type: 'error', message: e });
+ }
+ };
+
+ const actionButtons = useMemo(() =>
+
+
+
+ {t('Reset')}
+ {t('Save')}
+
+
+ {t('Delete')}
+
+ );
+
+
+ return <>
+
+ { modal }
+ >;
+}
diff --git a/client/admin/integrations/edit/OutgoingWebhookHistoryPage.js b/client/admin/integrations/edit/OutgoingWebhookHistoryPage.js
new file mode 100644
index 000000000000..e931ade0b90d
--- /dev/null
+++ b/client/admin/integrations/edit/OutgoingWebhookHistoryPage.js
@@ -0,0 +1,268 @@
+import { Button, ButtonGroup, Icon, Headline, Skeleton, Box, Accordion, Field, FieldGroup, Pagination } from '@rocket.chat/fuselage';
+import React, { useMemo, useCallback, useState, useEffect } from 'react';
+
+import Page from '../../../components/basic/Page';
+import { useTranslation } from '../../../contexts/TranslationContext';
+import { useHilightCode } from '../../../hooks/useHilightCode';
+import { integrations as eventList } from '../../../../app/integrations/lib/rocketchat';
+import { useMethod } from '../../../contexts/ServerContext';
+import { useEndpointDataExperimental, ENDPOINT_STATES } from '../../../hooks/useEndpointDataExperimental';
+import { useRoute, useRouteParameter } from '../../../contexts/RouterContext';
+import { useFormatDateAndTime } from '../../../hooks/useFormatDateAndTime';
+import { useToastMessageDispatch } from '../../../contexts/ToastMessagesContext';
+
+function HistoryItem({ data, onChange, ...props }) {
+ const t = useTranslation();
+
+ const hilightCode = useHilightCode();
+
+ const replayOutgoingIntegration = useMethod('replayOutgoingIntegration');
+
+ const {
+ _id,
+ _createdAt,
+ _updatedAt,
+ httpResult,
+ event,
+ step,
+ httpCallData,
+ data: dataSentToTrigger,
+ prepareSentMessage,
+ processSentMessage,
+ url,
+ httpError,
+ errorStack,
+ error,
+ integration: { _id: integrationId },
+ } = data;
+
+ const handleClickReplay = useCallback((e) => {
+ e.stopPropagation();
+ replayOutgoingIntegration({ integrationId, historyId: _id });
+ onChange();
+ }, [_id]);
+
+ const formatDateAndTime = useFormatDateAndTime();
+
+ return
+
+ {formatDateAndTime(_createdAt)}
+
+ {t('Replay')}
+
+ }
+ {...props}
+ >
+
+
+ {t('Status')}
+
+
+ {error ? t('Failure') : t('Success')}
+
+
+
+
+ {t('Integration_Outgoing_WebHook_History_Time_Triggered')}
+
+
+ {_createdAt}
+
+
+
+
+ {t('Integration_Outgoing_WebHook_History_Time_Ended_Or_Error')}
+
+
+ {_updatedAt}
+
+
+
+
+ {t('Event_Trigger')}
+
+
+ {t(eventList.outgoingEvents[event].label)}
+
+
+
+
+ {t('Integration_Outgoing_WebHook_History_Trigger_Step')}
+
+
+ {step}
+
+
+
+
+ {t('Integration_Outgoing_WebHook_History_Data_Passed_To_Trigger')}
+
+
+
+
+
+
+ {prepareSentMessage &&
+ {t('Integration_Outgoing_WebHook_History_Messages_Sent_From_Prepare_Script')}
+
+
+
+
+
+ }
+ {processSentMessage &&
+ {t('Integration_Outgoing_WebHook_History_Messages_Sent_From_Process_Script')}
+
+
+
+
+
+ }
+ {url &&
+ {t('URL')}
+
+
+ {url}
+
+
+ }
+ {httpCallData &&
+ {t('Integration_Outgoing_WebHook_History_Data_Passed_To_URL')}
+
+
+
+
+
+ }
+ {httpError &&
+ {t('Integration_Outgoing_WebHook_History_Http_Response_Error')}
+
+
+
+
+
+ }
+ {httpResult &&
+ {t('Integration_Outgoing_WebHook_History_Http_Response')}
+
+
+
+
+
+ }
+ {errorStack &&
+ {t('Integration_Outgoing_WebHook_History_Error_Stacktrace')}
+
+
+
+
+
+ }
+
+ ;
+}
+
+function HistoryContent({ data, state, onChange, ...props }) {
+ const t = useTranslation();
+
+ const [loadedData, setLoadedData] = useState();
+ useEffect(() => {
+ if (state === ENDPOINT_STATES.DONE) { setLoadedData(data); }
+ }, [state]);
+
+ if (!loadedData || state === ENDPOINT_STATES.LOADING) {
+ return
+
+
+
+
+
+
+ ;
+ }
+
+ if (loadedData.history.length < 1) {
+ return {t('Integration_Outgoing_WebHook_No_History')} ;
+ }
+
+ return <>
+
+ {loadedData.history.map((current) => )}
+
+ >;
+}
+
+function OutgoingWebhookHistoryPage(props) {
+ const dispatchToastMessage = useToastMessageDispatch();
+ const t = useTranslation();
+
+ const [cache, setCache] = useState();
+ const [current, setCurrent] = useState();
+ const [itemsPerPage, setItemsPerPage] = useState();
+ const onChange = useCallback(() => {
+ setCache(new Date());
+ });
+
+ const router = useRoute('admin-integrations');
+
+ const clearHistory = useMethod('clearIntegrationHistory');
+
+ const handleClearHistory = async () => {
+ try {
+ await clearHistory();
+ dispatchToastMessage({ type: 'success', message: t('Integration_History_Cleared') });
+ onChange();
+ } catch (e) {
+ dispatchToastMessage({ type: 'error', message: e });
+ }
+ };
+
+ const handleClickReturn = () => {
+ router.push({ });
+ };
+
+ const id = useRouteParameter('id');
+
+ const query = useMemo(() => ({
+ id,
+ cout: itemsPerPage,
+ offset: current,
+ }), [id, itemsPerPage, current, cache]);
+
+ const { data, state } = useEndpointDataExperimental('integrations.history', query);
+
+ const showingResultsLabel = useCallback(({ count, current, itemsPerPage }) => t('Showing results %s - %s of %s', current + 1, Math.min(current + itemsPerPage, count), count), []);
+
+ return
+
+
+
+ {t('Back')}
+
+ 0)}>
+ {t('clear_history')}
+
+
+
+
+
+
+
+ ;
+}
+
+export default OutgoingWebhookHistoryPage;
diff --git a/client/admin/integrations/exampleIncomingData.js b/client/admin/integrations/exampleIncomingData.js
new file mode 100644
index 000000000000..0226156f69fc
--- /dev/null
+++ b/client/admin/integrations/exampleIncomingData.js
@@ -0,0 +1,20 @@
+import { useMemo } from 'react';
+
+export function useExampleData({ aditionalFields, url }, dep) {
+ const exampleData = {
+ ...aditionalFields,
+ text: 'Example message',
+ attachments: [{
+ title: 'Rocket.Chat',
+ title_link: 'https://rocket.chat',
+ text: 'Rocket.Chat, the best open source chat',
+ image_url: '/images/integration-attachment-example.png',
+ color: '#764FA5',
+ }],
+ };
+
+ return useMemo(() => [
+ exampleData,
+ `curl -X POST -H 'Content-Type: application/json' --data '${ JSON.stringify(exampleData) }' ${ url }`,
+ ], dep);
+}
diff --git a/client/admin/integrations/new/NewBot.js b/client/admin/integrations/new/NewBot.js
new file mode 100644
index 000000000000..54ff4615574e
--- /dev/null
+++ b/client/admin/integrations/new/NewBot.js
@@ -0,0 +1,9 @@
+import React from 'react';
+import { Box } from '@rocket.chat/fuselage';
+
+import { useTranslation } from '../../../contexts/TranslationContext';
+
+export default function NewBot() {
+ const t = useTranslation();
+ return ;
+}
diff --git a/client/admin/integrations/new/NewIncomingWebhook.js b/client/admin/integrations/new/NewIncomingWebhook.js
new file mode 100644
index 000000000000..99cdbc628276
--- /dev/null
+++ b/client/admin/integrations/new/NewIncomingWebhook.js
@@ -0,0 +1,50 @@
+import React, { useMemo } from 'react';
+import { Field, Box, Margins, Button } from '@rocket.chat/fuselage';
+
+import { useTranslation } from '../../../contexts/TranslationContext';
+import { useRoute } from '../../../contexts/RouterContext';
+import { useEndpointAction } from '../../../hooks/useEndpointAction';
+import { useForm } from '../../../hooks/useForm';
+import IncomingWebhookForm from '../IncomingWebhookForm';
+
+const initialState = {
+ enabled: false,
+ channel: '',
+ username: '',
+ name: '',
+ alias: '',
+ avatarUrl: '',
+ emoji: '',
+ scriptEnabled: false,
+ script: '',
+};
+
+export default function NewIncomingWebhook(props) {
+ const t = useTranslation();
+
+ const router = useRoute('admin-integrations');
+
+ const { values: formValues, handlers: formHandlers, reset } = useForm(initialState);
+
+ const saveAction = useEndpointAction('POST', 'integrations.create', useMemo(() => ({ ...formValues, type: 'webhook-incoming' }), [JSON.stringify(formValues)]), t('Integration_added'));
+
+ const handleSave = async () => {
+ const result = await saveAction();
+ if (result.success) {
+ router.push({ context: 'edit', type: 'incoming', id: result.integration._id });
+ }
+ };
+
+ const actionButtons = useMemo(() =>
+
+
+
+ {t('Reset')}
+ {t('Save')}
+
+
+
+ );
+
+ return ;
+}
diff --git a/client/admin/integrations/new/NewIntegrationsPage.js b/client/admin/integrations/new/NewIntegrationsPage.js
new file mode 100644
index 000000000000..70dfc441127d
--- /dev/null
+++ b/client/admin/integrations/new/NewIntegrationsPage.js
@@ -0,0 +1,58 @@
+import { Tabs, Button, ButtonGroup, Icon } from '@rocket.chat/fuselage';
+import React, { useCallback } from 'react';
+
+import Page from '../../../components/basic/Page';
+import NewIncomingWebhook from './NewIncomingWebhook';
+import NewOutgoingWebhook from './NewOutgoingWebhook';
+import { useTranslation } from '../../../contexts/TranslationContext';
+import { useRouteParameter, useRoute } from '../../../contexts/RouterContext';
+
+
+export default function NewIntegrationsPage({ ...props }) {
+ const t = useTranslation();
+
+ const router = useRoute('admin-integrations');
+
+ const handleClickTab = (type) => () => {
+ router.push({ context: 'new', type });
+ };
+
+ const handleClickReturn = useCallback(() => {
+ router.push({ });
+ }, []);
+
+ const tab = useRouteParameter('type');
+
+ const handleIncomingTab = useCallback(handleClickTab('incoming'), []);
+ const handleOutgoingTab = useCallback(handleClickTab('outgoing'), []);
+
+ return
+
+
+
+ {t('Back')}
+
+
+
+
+
+ {t('Incoming')}
+
+
+ {t('Outgoing')}
+
+
+
+ {
+ (tab === 'incoming' && )
+ || (tab === 'outgoing' && )
+ }
+
+ ;
+}
diff --git a/client/admin/integrations/new/NewOutgoingWebhook.js b/client/admin/integrations/new/NewOutgoingWebhook.js
new file mode 100644
index 000000000000..f211f07d5ba1
--- /dev/null
+++ b/client/admin/integrations/new/NewOutgoingWebhook.js
@@ -0,0 +1,68 @@
+import React, { useMemo, useCallback } from 'react';
+import { Field, Button } from '@rocket.chat/fuselage';
+import { useUniqueId } from '@rocket.chat/fuselage-hooks';
+
+import { useTranslation } from '../../../contexts/TranslationContext';
+import { useEndpointAction } from '../../../hooks/useEndpointAction';
+import { useRoute } from '../../../contexts/RouterContext';
+import { useForm } from '../../../hooks/useForm';
+import OutgoingWebhookForm from '../OutgoiongWebhookForm';
+
+const defaultData = {
+ type: 'webhook-outgoing',
+ enabled: true,
+ impersonateUser: false,
+ event: 'sendMessage',
+ urls: '',
+ triggerWords: '',
+ targetRoom: '',
+ channel: '',
+ username: '',
+ name: '',
+ alias: '',
+ avatar: '',
+ emoji: '',
+ scriptEnabled: false,
+ script: '',
+ retryFailedCalls: true,
+ retryCount: 6,
+ retryDelay: 'powers-of-ten',
+ triggerWordAnywhere: false,
+ runOnEdits: true,
+};
+
+export default function NewOutgoingWebhook({ data = defaultData, onChange, setSaveAction, ...props }) {
+ const t = useTranslation();
+ const router = useRoute('admin-integrations');
+
+ const { values: formValues, handlers: formHandlers } = useForm({ ...data, token: useUniqueId() });
+
+ const {
+ urls,
+ triggerWords,
+ } = formValues;
+
+ const query = useMemo(() => ({
+ ...formValues,
+ urls: urls.split('\n'),
+ triggerWords: triggerWords.split(';'),
+ }), [JSON.stringify(formValues)]);
+
+ const saveIntegration = useEndpointAction('POST', 'integrations.create', query, t('Integration_added'));
+
+ const handleSave = useCallback(async () => {
+ const result = await saveIntegration();
+ if (result.success) {
+ router.push({ id: result.integration._id, context: 'edit', type: 'outgoing' });
+ }
+ }, [saveIntegration, router]);
+
+ const saveButton = useMemo(() =>
+
+ {t('Save')}
+
+ );
+
+
+ return ;
+}
diff --git a/client/admin/integrations/new/NewZapier.js b/client/admin/integrations/new/NewZapier.js
new file mode 100644
index 000000000000..a9f9e1513cfc
--- /dev/null
+++ b/client/admin/integrations/new/NewZapier.js
@@ -0,0 +1,43 @@
+import React, { useEffect, useState } from 'react';
+import { Box, Skeleton, Margins } from '@rocket.chat/fuselage';
+
+import { useTranslation } from '../../../contexts/TranslationContext';
+
+const blogSpotStyleScriptImport = (src) => new Promise((resolve) => {
+ const script = document.createElement('script');
+ script.type = 'text/javascript';
+ document.body.appendChild(script);
+
+ const resolveFunc = (event) => resolve(event.currentTarget);
+
+ script.onreadystatechange = resolveFunc;
+ script.onload = resolveFunc;
+ script.src = src;
+});
+
+export default function NewZapier({ ...props }) {
+ const t = useTranslation();
+ const [script, setScript] = useState();
+ useEffect(() => {
+ const importZapier = async () => {
+ const scriptEl = await blogSpotStyleScriptImport('https://zapier.com/apps/embed/widget.js?services=rocketchat&html_id=zapier-goes-here');
+ setScript(scriptEl);
+ };
+ if (!script) { importZapier(); }
+ return () => script && script.parentNode.removeChild(script);
+ }, [script]);
+
+ return <>
+
+ {!script &&
+
+
+
+
+
+
+
+ }
+
+ >;
+}
diff --git a/client/admin/oauthApps/OAuthAddApp.js b/client/admin/oauthApps/OAuthAddApp.js
new file mode 100644
index 000000000000..a31a083288bd
--- /dev/null
+++ b/client/admin/oauthApps/OAuthAddApp.js
@@ -0,0 +1,87 @@
+import React, { useCallback, useState } from 'react';
+import {
+ Button,
+ ButtonGroup,
+ TextInput,
+ Field,
+ TextAreaInput,
+ ToggleSwitch,
+ FieldGroup,
+} from '@rocket.chat/fuselage';
+
+import { useTranslation } from '../../contexts/TranslationContext';
+import { useMethod } from '../../contexts/ServerContext';
+import { useRoute } from '../../contexts/RouterContext';
+import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext';
+import VerticalBar from '../../components/basic/VerticalBar';
+
+
+export default function OAuthAddApp(props) {
+ const t = useTranslation();
+ const dispatchToastMessage = useToastMessageDispatch();
+
+ const [newData, setNewData] = useState({
+ name: '',
+ active: false,
+ redirectUri: '',
+ });
+
+ const saveApp = useMethod('addOAuthApp');
+
+ const router = useRoute('admin-oauth-apps');
+
+ const close = useCallback(() => router.push({}), [router]);
+
+ const handleSave = useCallback(async () => {
+ try {
+ await saveApp(
+ newData,
+ );
+ close();
+ dispatchToastMessage({ type: 'success', message: t('Application_added') });
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ }
+ }, [JSON.stringify(newData)]);
+
+ const handleChange = (field, getValue = (e) => e.currentTarget.value) => (e) => setNewData({ ...newData, [field]: getValue(e) });
+
+ const {
+ active,
+ name,
+ redirectUri,
+ } = newData;
+
+ return
+
+
+
+ {t('Active')}
+ !active)}/>
+
+
+
+ {t('Application_Name')}
+
+
+
+ {t('Give_the_application_a_name_This_will_be_seen_by_your_users')}
+
+
+ {t('Redirect_URI')}
+
+
+
+ {t('After_OAuth2_authentication_users_will_be_redirected_to_this_URL')}
+
+
+
+
+ {t('Cancel')}
+ {t('Save')}
+
+
+
+
+ ;
+}
diff --git a/client/admin/oauthApps/OAuthAppsPage.js b/client/admin/oauthApps/OAuthAppsPage.js
new file mode 100644
index 000000000000..a42e5b863675
--- /dev/null
+++ b/client/admin/oauthApps/OAuthAppsPage.js
@@ -0,0 +1,39 @@
+import React from 'react';
+import { Button, Icon } from '@rocket.chat/fuselage';
+
+import Page from '../../components/basic/Page';
+// import VerticalBar from '../../components/basic/VerticalBar';
+import { useTranslation } from '../../contexts/TranslationContext';
+import { useRouteParameter, useRoute } from '../../contexts/RouterContext';
+import OAuthAppsTable from './OAuthAppsTable';
+import OAuthEditAppWithData from './OAuthEditApp';
+import OAuthAddApp from './OAuthAddApp';
+
+export function OAuthAppsPage() {
+ const t = useTranslation();
+
+ const router = useRoute('admin-oauth-apps');
+
+ const context = useRouteParameter('context');
+ const id = useRouteParameter('id');
+
+ return
+
+
+ {context && router.push({})}>
+ {t('Back')}
+ }
+ {!context && router.push({ context: 'new' })}>
+ {t('New_Application')}
+ }
+
+
+ {!context && }
+ {context === 'edit' && }
+ {context === 'new' && }
+
+
+ ;
+}
+
+export default OAuthAppsPage;
diff --git a/client/admin/oauthApps/OAuthAppsRoute.js b/client/admin/oauthApps/OAuthAppsRoute.js
new file mode 100644
index 000000000000..8f163d0cb483
--- /dev/null
+++ b/client/admin/oauthApps/OAuthAppsRoute.js
@@ -0,0 +1,15 @@
+import React from 'react';
+
+import { usePermission } from '../../contexts/AuthorizationContext';
+import NotAuthorizedPage from '../NotAuthorizedPage';
+import OAuthAppsPage from './OAuthAppsPage';
+
+export default function MailerRoute() {
+ const canAccessOAuthApps = usePermission('manage-oauth-apps');
+
+ if (!canAccessOAuthApps) {
+ return ;
+ }
+
+ return ;
+}
diff --git a/client/admin/oauthApps/OAuthAppsTable.js b/client/admin/oauthApps/OAuthAppsTable.js
new file mode 100644
index 000000000000..91b1ec301b06
--- /dev/null
+++ b/client/admin/oauthApps/OAuthAppsTable.js
@@ -0,0 +1,40 @@
+import { Table } from '@rocket.chat/fuselage';
+import React, { useMemo, useCallback } from 'react';
+
+import { GenericTable, Th } from '../../../app/ui/client/components/GenericTable';
+import { useTranslation } from '../../contexts/TranslationContext';
+import { useRoute } from '../../contexts/RouterContext';
+import { useEndpointDataExperimental } from '../../hooks/useEndpointDataExperimental';
+import { useFormatDateAndTime } from '../../hooks/useFormatDateAndTime';
+
+export function OAuthAppsTable() {
+ const t = useTranslation();
+ const formatDateAndTime = useFormatDateAndTime();
+
+ const { data } = useEndpointDataExperimental('oauth-apps.list', useMemo(() => ({}), []));
+
+ const router = useRoute('admin-oauth-apps');
+
+ const onClick = (_id) => () => router.push({
+ context: 'edit',
+ id: _id,
+ });
+
+ const header = useMemo(() => [
+ {t('Name')} ,
+ {t('Created_by')} ,
+ {t('Created_at')} ,
+ ]);
+
+ const renderRow = useCallback(({ _id, name, _createdAt, _createdBy: { username: createdBy } }) =>
+
+ {name}
+ {createdBy}
+ {formatDateAndTime(_createdAt)}
+ ,
+ );
+
+ return ;
+}
+
+export default OAuthAppsTable;
diff --git a/client/admin/oauthApps/OAuthEditApp.js b/client/admin/oauthApps/OAuthEditApp.js
new file mode 100644
index 000000000000..b525bbada583
--- /dev/null
+++ b/client/admin/oauthApps/OAuthEditApp.js
@@ -0,0 +1,222 @@
+import React, { useCallback, useState, useMemo } from 'react';
+import {
+ Box,
+ Button,
+ ButtonGroup,
+ TextInput,
+ Field,
+ Icon,
+ Skeleton,
+ Throbber,
+ InputBox,
+ TextAreaInput,
+ ToggleSwitch,
+ FieldGroup,
+} from '@rocket.chat/fuselage';
+
+import { useTranslation } from '../../contexts/TranslationContext';
+import { useMethod, useAbsoluteUrl } from '../../contexts/ServerContext';
+import { useRoute } from '../../contexts/RouterContext';
+import { useToastMessageDispatch } from '../../contexts/ToastMessagesContext';
+import { Modal } from '../../components/basic/Modal';
+import { useEndpointDataExperimental, ENDPOINT_STATES } from '../../hooks/useEndpointDataExperimental';
+import VerticalBar from '../../components/basic/VerticalBar';
+
+const DeleteWarningModal = ({ onDelete, onCancel, ...props }) => {
+ const t = useTranslation();
+ return
+
+
+ {t('Are_you_sure')}
+
+
+
+ {t('Application_delete_warning')}
+
+
+
+ {t('Cancel')}
+ {t('Delete')}
+
+
+ ;
+};
+
+const SuccessModal = ({ onClose, ...props }) => {
+ const t = useTranslation();
+ return
+
+
+ {t('Deleted')}
+
+
+
+ {t('Your_entry_has_been_deleted')}
+
+
+
+ {t('Ok')}
+
+
+ ;
+};
+
+export default function EditOauthAppWithData({ _id, ...props }) {
+ const t = useTranslation();
+
+ const [cache, setCache] = useState();
+
+ const onChange = useCallback(() => {
+ setCache(new Date());
+ }, []);
+
+ const query = useMemo(() => ({
+ appId: _id,
+ }), [_id, cache]);
+
+ const { data, state, error } = useEndpointDataExperimental('oauth-apps.get', query);
+
+ if (state === ENDPOINT_STATES.LOADING) {
+ return
+
+
+
+
+
+
+
+
+
+
+
+ ;
+ }
+
+ if (error || !data || !_id) {
+ return {t('error-application-not-found')} ;
+ }
+
+ return ;
+}
+
+function EditOauthApp({ onChange, data, ...props }) {
+ const t = useTranslation();
+ const dispatchToastMessage = useToastMessageDispatch();
+
+ const [newData, setNewData] = useState({
+ name: data.name,
+ active: data.active,
+ redirectUri: Array.isArray(data.redirectUri) ? data.redirectUri.join('\n') : data.redirectUri,
+ });
+ const [modal, setModal] = useState();
+
+ const router = useRoute('admin-oauth-apps');
+
+ const close = useCallback(() => router.push({}), [router]);
+
+ const absoluteUrl = useAbsoluteUrl();
+ const authUrl = useMemo(() => absoluteUrl('oauth/authorize'));
+ const tokenUrl = useMemo(() => absoluteUrl('oauth/token'));
+
+ const saveApp = useMethod('updateOAuthApp');
+ const deleteApp = useMethod('deleteOAuthApp');
+
+ const handleSave = useCallback(async () => {
+ try {
+ await saveApp(
+ data._id,
+ newData,
+ );
+ dispatchToastMessage({ type: 'success', message: t('Application_updated') });
+ onChange();
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ }
+ }, [JSON.stringify(newData)]);
+
+ const onDeleteConfirm = useCallback(async () => {
+ try {
+ await deleteApp(data._id);
+ setModal(() => { setModal(); close(); }}/>);
+ } catch (error) {
+ dispatchToastMessage({ type: 'error', message: error });
+ }
+ }, [data._id]);
+
+ const openConfirmDelete = () => setModal(() => setModal(undefined)}/>);
+
+ const handleChange = (field, getValue = (e) => e.currentTarget.value) => (e) => setNewData({ ...newData, [field]: getValue(e) });
+
+ const {
+ active,
+ name,
+ redirectUri,
+ } = newData;
+
+ return <>
+
+
+
+
+ {t('Active')}
+ !active)}/>
+
+
+
+ {t('Application_Name')}
+
+
+
+ {t('Give_the_application_a_name_This_will_be_seen_by_your_users')}
+
+
+ {t('Redirect_URI')}
+
+
+
+ {t('After_OAuth2_authentication_users_will_be_redirected_to_this_URL')}
+
+
+ {t('Client_ID')}
+
+
+
+
+
+ {t('Client_Secret')}
+
+
+
+
+
+ {t('Authorization_URL')}
+
+
+
+
+
+ {t('Access_Token_URL')}
+
+
+
+
+
+
+
+ {t('Cancel')}
+ {t('Save')}
+
+
+
+
+
+
+ {t('Delete')}
+
+
+
+
+
+ { modal }
+ >;
+}
diff --git a/client/admin/rooms/EditRoom.js b/client/admin/rooms/EditRoom.js
index d60ca438a58d..f77b06334afb 100644
--- a/client/admin/rooms/EditRoom.js
+++ b/client/admin/rooms/EditRoom.js
@@ -1,5 +1,5 @@
import React, { useCallback, useState, useMemo } from 'react';
-import { Box, Headline, Button, Margins, TextInput, Skeleton, Field, ToggleSwitch, Divider, Icon, Callout } from '@rocket.chat/fuselage';
+import { Box, Button, Margins, TextInput, Skeleton, Field, ToggleSwitch, Divider, Icon, Callout } from '@rocket.chat/fuselage';
import { useTranslation } from '../../contexts/TranslationContext';
import { useEndpointDataExperimental, ENDPOINT_STATES } from '../../hooks/useEndpointDataExperimental';
@@ -22,11 +22,11 @@ function EditRoomWithData({ rid }) {
if (state === ENDPOINT_STATES.LOADING) {
return
-
+
-
+
-
+
;
}
diff --git a/client/admin/rooms/edit/EditRoom.js b/client/admin/rooms/edit/EditRoom.js
new file mode 100644
index 000000000000..7638bebcc00c
--- /dev/null
+++ b/client/admin/rooms/edit/EditRoom.js
@@ -0,0 +1,194 @@
+import React, { useCallback, useState, useMemo } from 'react';
+import { Box, Headline, Button, Margins, TextInput, Skeleton, Field, ToggleSwitch, Divider, Icon, Callout } from '@rocket.chat/fuselage';
+
+import { useTranslation } from '../../../contexts/TranslationContext';
+import { useEndpointDataExperimental, ENDPOINT_STATES } from '../../../hooks/useEndpointDataExperimental';
+import { roomTypes } from '../../../../app/utils/client';
+import { useMethod } from '../../../contexts/ServerContext';
+import { usePermission } from '../../../contexts/AuthorizationContext';
+import NotAuthorizedPage from '../../NotAuthorizedPage';
+import { useEndpointAction } from '../../../hooks/useEndpointAction';
+import Page from '../../../components/basic/Page';
+
+export function EditRoomContextBar({ rid }) {
+ const canViewRoomAdministration = usePermission('view-room-administration');
+ return canViewRoomAdministration ? : ;
+}
+
+function EditRoomWithData({ rid }) {
+ const [cache, setState] = useState();
+
+ const { data = {}, state, error } = useEndpointDataExperimental('rooms.adminRooms.getRoom', useMemo(() => ({ rid }), [rid, cache]));
+
+ if (state === ENDPOINT_STATES.LOADING) {
+ return
+
+
+
+
+
+
+ ;
+ }
+
+ if (state === ENDPOINT_STATES.ERROR) {
+ return error.message;
+ }
+
+ return setState(new Date())}/>;
+}
+
+function EditRoom({ room, onChange }) {
+ const t = useTranslation();
+
+ const [deleted, setDeleted] = useState(false);
+ const [newData, setNewData] = useState({});
+ const [changeArchivation, setChangeArchivation] = useState(false);
+
+ const canDelete = usePermission(`delete-${ room.t }`);
+
+ const hasUnsavedChanges = useMemo(() => Object.values(newData).filter((current) => current === null).length < Object.keys(newData).length, [JSON.stringify(newData)]);
+ const saveQuery = useMemo(() => ({ rid: room._id, ...Object.fromEntries(Object.entries(newData).filter(([, value]) => value !== null)) }), [room._id, JSON.stringify(newData)]);
+
+ const archiveSelector = room.archived ? 'unarchive' : 'archive';
+ const archiveMessage = archiveSelector === 'archive' ? 'Room_has_been_archived' : 'Room_has_been_archived';
+ const archiveQuery = useMemo(() => ({ rid: room._id, action: room.archived ? 'unarchive' : 'archive' }), [room.rid, changeArchivation]);
+
+ const saveAction = useEndpointAction('POST', 'rooms.saveRoomSettings', saveQuery, t('Room_updated_successfully'));
+ const archiveAction = useEndpointAction('POST', 'rooms.changeArchivationState', archiveQuery, t(archiveMessage));
+
+ const updateType = (type) => () => (type === 'p' ? 'c' : 'p');
+ const areEqual = (a, b) => a === b || !(a || b);
+
+ const handleChange = (field, currentValue, getValue = (e) => e.currentTarget.value) => (e) => setNewData({ ...newData, [field]: areEqual(getValue(e), currentValue) ? null : getValue(e) });
+ const handleSave = async () => {
+ await Promise.all([hasUnsavedChanges && saveAction(), changeArchivation && archiveAction()].filter(Boolean));
+ onChange('update');
+ };
+
+ const deleteRoom = useMethod('eraseRoom');
+
+ const handleDelete = useCallback(async () => {
+ await deleteRoom(room._id);
+ setDeleted(true);
+ }, [room]);
+
+ const roomName = room.t === 'd' ? room.usernames.join(' x ') : roomTypes.getRoomName(room.t, { type: room.t, ...room });
+ const roomType = newData.roomType ?? room.t;
+ const readOnly = newData.readOnly ?? !!room.ro;
+ const isArchived = changeArchivation ? !room.archived : !!room.archived;
+ const isDefault = newData.default ?? !!room.default;
+ const isFavorite = newData.favorite ?? !!room.favorite;
+ const isFeatured = newData.featured ?? !!room.featured;
+
+ return
+
+
+ {deleted && }
+
+
+ {t('Name')}
+
+
+
+
+ { room.t !== 'd' && <>
+
+ {t('Owner')}
+
+ {room.u?.username}
+
+
+
+ {t('Topic')}
+
+
+
+
+
+
+
+
+ {t('Public')}
+ {t('All_users_in_the_channel_can_write_new_messages')}
+
+
+
+
+
+ {t('Private')}
+ {t('Just_invited_people_can_access_this_channel')}
+
+
+
+
+
+
+
+
+
+ {t('Collaborative')}
+ {t('All_users_in_the_channel_can_write_new_messages')}
+
+
+ !readOnly)}/>
+
+
+ {t('Read_only')}
+ {t('Only_authorized_users_can_write_new_messages')}
+
+
+
+
+
+
+
+
+ {t('Archived')}
+ setChangeArchivation(!changeArchivation)}/>
+
+
+
+
+
+
+ {t('Default')}
+ !isDefault)}/>
+
+
+
+
+
+
+ {t('Favorite')}
+ !isFavorite)}/>
+
+
+
+
+
+
+ {t('Featured')}
+ !isFeatured)}/>
+
+
+
+
+
+
+
+ setNewData({})}>{t('Reset')}
+ {t('Save')}
+
+
+
+
+ >}
+
+
+ {t('Delete')}
+
+
+
+ ;
+}
diff --git a/client/admin/routes.js b/client/admin/routes.js
index 4df152660524..6ede81d1ef1d 100644
--- a/client/admin/routes.js
+++ b/client/admin/routes.js
@@ -72,6 +72,16 @@ registerAdminRoute('/mailer', {
lazyRouteComponent: () => import('./mailer/MailerRoute'),
});
+registerAdminRoute('/oauth-apps/:context?/:id?', {
+ name: 'admin-oauth-apps',
+ lazyRouteComponent: () => import('./oauthApps/OAuthAppsRoute'),
+});
+
+registerAdminRoute('/integrations/:context?/:type?/:id?', {
+ name: 'admin-integrations',
+ lazyRouteComponent: () => import('./integrations/IntegrationsRoute'),
+});
+
registerAdminRoute('/custom-user-status/:context?/:id?', {
name: 'custom-user-status',
lazyRouteComponent: () => import('./customUserStatus/CustomUserStatusRoute'),
@@ -97,6 +107,11 @@ registerAdminRoute('/invites', {
lazyRouteComponent: () => import('./invites/InvitesRoute'),
});
+registerAdminRoute('/cloud/:page?', {
+ name: 'cloud',
+ lazyRouteComponent: () => import('./cloud/CloudRoute'),
+});
+
registerAdminRoute('/view-logs', {
name: 'admin-view-logs',
lazyRouteComponent: () => import('./viewLogs/ViewLogsRoute'),
diff --git a/client/admin/sidebarItems.js b/client/admin/sidebarItems.js
index a76b96d08dda..c5112049b571 100644
--- a/client/admin/sidebarItems.js
+++ b/client/admin/sidebarItems.js
@@ -42,6 +42,13 @@ registerAdminSidebarItem({
permissionGranted: () => hasPermission('create-invite-links'),
});
+registerAdminSidebarItem({
+ icon: 'cloud-plus',
+ href: 'cloud',
+ i18nLabel: 'Connectivity_Services',
+ permissionGranted: () => hasPermission('manage-cloud'),
+});
+
registerAdminSidebarItem({
href: 'admin-view-logs',
i18nLabel: 'View_Logs',
diff --git a/client/admin/users/EditUser.js b/client/admin/users/EditUser.js
index a7b287f234ac..1feb1eb82c34 100644
--- a/client/admin/users/EditUser.js
+++ b/client/admin/users/EditUser.js
@@ -1,5 +1,5 @@
import React, { useMemo, useState } from 'react';
-import { Field, TextInput, Box, Headline, Skeleton, ToggleSwitch, Icon, TextAreaInput, MultiSelectFiltered, Margins, Button } from '@rocket.chat/fuselage';
+import { Field, TextInput, Box, Skeleton, ToggleSwitch, Icon, TextAreaInput, MultiSelectFiltered, Margins, Button } from '@rocket.chat/fuselage';
import { useTranslation } from '../../contexts/TranslationContext';
import { useEndpointData } from '../../hooks/useEndpointData';
@@ -18,11 +18,11 @@ export function EditUserWithData({ userId, ...props }) {
if (state === ENDPOINT_STATES.LOADING) {
return
-
+
-
+
-
+
;
}
diff --git a/client/admin/users/InviteUsers.js b/client/admin/users/InviteUsers.js
index 5804f498c863..d47db76f7ff0 100644
--- a/client/admin/users/InviteUsers.js
+++ b/client/admin/users/InviteUsers.js
@@ -1,5 +1,5 @@
import React, { useCallback, useState } from 'react';
-import { Box, Headline, Button, Icon, TextAreaInput } from '@rocket.chat/fuselage';
+import { Box, Button, Icon, TextAreaInput } from '@rocket.chat/fuselage';
import { useTranslation } from '../../contexts/TranslationContext';
import { useMethod } from '../../contexts/ServerContext';
@@ -24,7 +24,7 @@ export function InviteUsers({ data, ...props }) {
});
};
return
- {t('Send_invitation_email')}
+ {t('Send_invitation_email')}
{t('Send_invitation_email_info')}
setText(e.currentTarget.value)}/>
diff --git a/client/admin/users/UserInfo.js b/client/admin/users/UserInfo.js
index 23831613eabc..93447ce3b669 100644
--- a/client/admin/users/UserInfo.js
+++ b/client/admin/users/UserInfo.js
@@ -1,5 +1,5 @@
import React, { useMemo, useState, useEffect } from 'react';
-import { Box, Avatar, Margins, Headline, Skeleton, Chip, Tag } from '@rocket.chat/fuselage';
+import { Box, Avatar, Margins, Skeleton, Chip, Tag } from '@rocket.chat/fuselage';
import moment from 'moment';
import { useEndpointDataExperimental, ENDPOINT_STATES } from '../../hooks/useEndpointDataExperimental';
@@ -38,11 +38,11 @@ export function UserInfoWithData({ userId, ...props }) {
if (state === ENDPOINT_STATES.LOADING) {
return
-
+
-
+
-
+
;
}
diff --git a/client/components/setupWizard/StepHeader.js b/client/components/setupWizard/StepHeader.js
index d46c86c01b76..aaf068dc976a 100644
--- a/client/components/setupWizard/StepHeader.js
+++ b/client/components/setupWizard/StepHeader.js
@@ -1,4 +1,4 @@
-import { Box, Headline, Margins } from '@rocket.chat/fuselage';
+import { Box } from '@rocket.chat/fuselage';
import React from 'react';
import { useTranslation } from '../../contexts/TranslationContext';
@@ -6,12 +6,8 @@ import { useTranslation } from '../../contexts/TranslationContext';
export function StepHeader({ number, title }) {
const t = useTranslation();
- return
-
-
- {t('Step')} {number}
-
- {title}
-
- ;
+ return
+ {t('Step')} {number}
+ {title}
+ ;
}
diff --git a/client/hooks/useForm.js b/client/hooks/useForm.js
new file mode 100644
index 000000000000..2d7f213b21a0
--- /dev/null
+++ b/client/hooks/useForm.js
@@ -0,0 +1,28 @@
+import { useState, useCallback } from 'react';
+
+const getValue = (e) => (e.currentTarget ? e.currentTarget.value : e);
+
+const capitalize = (s) => {
+ if (typeof s !== 'string') { return ''; }
+ return s.charAt(0).toUpperCase() + s.slice(1);
+};
+
+export const useForm = (obj) => {
+ const resetCallbacks = [];
+ const ret = Object.keys(obj).sort().reduce((ret, key) => {
+ const value = obj[key];
+ const [data, setData] = useState(value);
+
+ ret.values = { ...ret.values, [key]: data };
+ ret.handlers = { ...ret.handlers, [`handle${ capitalize(key) }`]: useCallback(typeof value !== 'boolean' ? (e) => setData(getValue(e)) : () => setData(!data), [data]) };
+ resetCallbacks.push(() => setData(value));
+
+ return ret;
+ }, {});
+
+ ret.reset = () => {
+ resetCallbacks.forEach((reset) => reset());
+ };
+
+ return ret;
+};
diff --git a/client/hooks/useHilightCode.js b/client/hooks/useHilightCode.js
new file mode 100644
index 000000000000..eb081ef36a13
--- /dev/null
+++ b/client/hooks/useHilightCode.js
@@ -0,0 +1,6 @@
+import hljs from 'highlight.js';
+import { useMemo } from 'react';
+
+export function useHilightCode() {
+ return (language, text) => useMemo(() => hljs.highlight(language, text).value, [language, text]);
+}
diff --git a/client/importPackages.js b/client/importPackages.js
index db055fd38e45..183ed71b41fc 100644
--- a/client/importPackages.js
+++ b/client/importPackages.js
@@ -7,7 +7,6 @@ import '../app/autotranslate/client';
import '../app/cas/client';
import '../app/channel-settings';
import '../app/channel-settings-mail-messages/client';
-import '../app/cloud/client';
import '../app/colors/client';
import '../app/crowd/client';
import '../app/custom-oauth';
@@ -31,7 +30,7 @@ import '../app/importer-hipchat/client';
import '../app/importer-hipchat-enterprise/client';
import '../app/importer-slack/client';
import '../app/importer-slack-users/client';
-import '../app/integrations/client';
+import '../app/integrations/client/startup';
import '../app/issuelinks/client';
import '../app/katex/client';
import '../app/ldap/client';
diff --git a/package-lock.json b/package-lock.json
index 715b28d31286..89edc6a2f1f5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2810,11 +2810,6 @@
"uuid": "^3.2.1"
},
"dependencies": {
- "adm-zip": {
- "version": "0.4.14",
- "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.14.tgz",
- "integrity": "sha512-/9aQCnQHF+0IiCl0qhXoK7qs//SwYE7zX8lsr/DNk1BRAHYxeLZPL4pguwK29gUEqasYQjqPtEpDRSWEkdHn9g=="
- },
"typescript": {
"version": "2.9.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz",
@@ -2879,9 +2874,9 @@
"integrity": "sha512-TVPEckSbzHr+Ix3h4OuEd5OyDVAVnviSQRqwYzAbfRvb4B9riZwqUlvdNKgRofy1og5ovRMZUL9CqduGbsdhsA=="
},
"@rocket.chat/fuselage-ui-kit": {
- "version": "0.6.3-dev.35",
- "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-ui-kit/-/fuselage-ui-kit-0.6.3-dev.35.tgz",
- "integrity": "sha512-E/C7voGjzfnp8WGqflOau/BtXSQa7Zaf2zc4cOXeudlagPLsG3K0uSU99ZiazKXZOR01h3QZM3Ikw5mEXTkeww==",
+ "version": "0.6.3-dev.39",
+ "resolved": "https://registry.npmjs.org/@rocket.chat/fuselage-ui-kit/-/fuselage-ui-kit-0.6.3-dev.39.tgz",
+ "integrity": "sha512-ll9lYOM2mEAsKtHiPdxDwVO2YGkarZMCLl7UK6yT71BFVuDY7hJXm14+aj+JBTKc9B2J0CFIo+MfO1cDuqYc+A==",
"requires": {
"@rocket.chat/ui-kit": "^0.8.0"
},
@@ -7788,9 +7783,9 @@
}
},
"caniuse-lite": {
- "version": "1.0.30001051",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001051.tgz",
- "integrity": "sha512-sw8UUnTlRevawTMZKN7vpfwSjCBVoiMPlYd8oT2VwNylyPCBdMAUmLGUApnYYTtIm5JXsQegUAY7GPHqgfDzjw==",
+ "version": "1.0.30001054",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001054.tgz",
+ "integrity": "sha512-jiKlTI6Ur8Kjfj8z0muGrV6FscpRvefcQVPSuMuXnvRCfExU7zlVLNjmOz1TnurWgUrAY7MMmjyy+uTgIl1XHw==",
"dev": true
},
"chalk": {
@@ -7820,9 +7815,9 @@
"dev": true
},
"electron-to-chromium": {
- "version": "1.3.428",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.428.tgz",
- "integrity": "sha512-u3+5jEfgLKq/hGO96YfAoOAM1tgFnRDTCD5mLuev44tttcXix+INtVegAkmGzUcfDsnzkPt51XXurXZLLwXt0w==",
+ "version": "1.3.431",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.431.tgz",
+ "integrity": "sha512-2okqkXCIda7qDwjYhUFxPcQdZDIZZ/zBLDzVOif7WW/TSNfEhdT6SO07O1x/sFteEHX189Z//UwjbZKKCOn2Fg==",
"dev": true
},
"node-releases": {
diff --git a/package.json b/package.json
index 8fcbf4e7db05..c5783cac5b30 100644
--- a/package.json
+++ b/package.json
@@ -130,7 +130,7 @@
"@rocket.chat/fuselage": "^0.6.3-dev.39",
"@rocket.chat/fuselage-hooks": "^0.6.3-dev.35",
"@rocket.chat/fuselage-polyfills": "^0.6.3-dev.23",
- "@rocket.chat/fuselage-ui-kit": "^0.6.3-dev.35",
+ "@rocket.chat/fuselage-ui-kit": "^0.6.3-dev.39",
"@rocket.chat/icons": "^0.6.3-dev.23",
"@rocket.chat/ui-kit": "^0.6.3-dev.23",
"@slack/client": "^4.8.0",
diff --git a/packages/rocketchat-i18n/i18n/ca.i18n.json b/packages/rocketchat-i18n/i18n/ca.i18n.json
index 5f5528d5c9bd..0bac88069909 100644
--- a/packages/rocketchat-i18n/i18n/ca.i18n.json
+++ b/packages/rocketchat-i18n/i18n/ca.i18n.json
@@ -634,7 +634,6 @@
"Closed_by_visitor": "Tancat pel visitant",
"Closing_chat": "Tancant xat",
"Cloud": "Cloud",
- "Cloud_connect": "Connexió Rocket.Chat Cloud ",
"Cloud_connect_support": "Si encara no heu rebut cap correu electrònic de registre, assegureu-vos que el vostre correu electrònic s’ha actualitzat anteriorment. Si encara teniu problemes, podeu posar-vos en contacte amb l’assistència a",
"Cloud_console": "Cloud Console",
"Cloud_what_is_it": "Què és això?",
@@ -652,9 +651,9 @@
"Cloud_manually_input_token": "Introduïu manualment la fitxa rebuda al correu electrònic de registre de Cloud.",
"Cloud_registration_required": "Cal registrar-se",
"Cloud_registration_required_description": "Sembla que durant la configuració no heu triat registrar l’espai de treball.",
- "Cloud_registration_requried_link_text": "Feu clic aquí per registrar el vostre espai de treball.",
+ "Cloud_registration_required_link_text": "Feu clic aquí per registrar el vostre espai de treball.",
"Cloud_error_in_authenticating": "S'ha rebut un error en autenticar-se",
- "Cloud_error_code": "Codi:",
+ "Cloud_error_code": "Codi: __errorCode__",
"Cloud_status_page_description": "Si un determinat servei Cloud té problemes, podeu comprovar els problemes coneguts a la nostra pàgina d’estat.",
"Cloud_troubleshooting": "Resolució de problemes",
"Collaborative": "Col·laboratiu",
@@ -2996,4 +2995,4 @@
"Your_push_was_sent_to_s_devices": "La notificació push s'ha enviat a %s dispositius",
"Your_server_link": "Enllaç del servidor",
"Your_workspace_is_ready": "El vostre espai de treball està a punt per utilitzar 🎉"
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/cs.i18n.json b/packages/rocketchat-i18n/i18n/cs.i18n.json
index 6d4c48da78ff..d6a2885c56f7 100644
--- a/packages/rocketchat-i18n/i18n/cs.i18n.json
+++ b/packages/rocketchat-i18n/i18n/cs.i18n.json
@@ -690,12 +690,10 @@
"Closing_chat": "Uzavření místnosti",
"Cloud": "Cloud",
"Cloud_Register_manually": "Registrovat ručně",
- "Cloud_click_here": "Po zkopírování textu přejděte do cloudové konzole. [Klikněte zde]()",
"Cloud_register_offline_finish_helper": "Po dokončení procesu registrace v cloudové konzoli byste měli dostat nějaký text. Pro dokončení registrace jej vložte sem.",
"Cloud_register_offline_helper": "Pracovní prostory lze ručně zaregistrovat, pokud je k nim omezen síťový přístup. Zkopírujte níže uvedený text a dokončete proces pomocí naší cloudové konzole.",
"Cloud_register_success": "Váš pracovní prostor byl úspěšně zaregistrován!",
"Cloud_register_error": "Při zpracování vašeho požadavku došlo k chybě. Prosím zkuste to znovu později.",
- "Cloud_connect": "Připojit k Rocket.Chat Cloud",
"Cloud_connect_support": "Pokud jste ještě neobdrželi registrační e-mail, ujistěte se, že váš e-mail je aktualizován výše. Pokud problémy přetrvávají, můžete kontaktovat podporu na adrese",
"Cloud_console": "Cloud přehled",
"Cloud_Info": "Informace o Cloudu",
@@ -714,9 +712,9 @@
"Cloud_manually_input_token": "Ručně zadejte token přijatý z e-mailu pro registraci do Cloudu.",
"Cloud_registration_required": "Povinná registrace",
"Cloud_registration_required_description": "Vypadá to, že jste se nevybral/a zaregistraci svého pracovního prostoru během instalace .",
- "Cloud_registration_requried_link_text": "Klikněte zde pro registraci svého pracovního prostoru.",
+ "Cloud_registration_required_link_text": "Klikněte zde pro registraci svého pracovního prostoru.",
"Cloud_error_in_authenticating": "Při ověřování došlo k chybě",
- "Cloud_error_code": "Kód:",
+ "Cloud_error_code": "Kód: __errorCode__",
"Cloud_status_page_description": "Pokud má konkrétní cloudová služba problémy, můžete zkontrolovat známé problémy na naší stavové stránce na adrese",
"Cloud_Service_Agree_PrivacyTerms": "Souhlas s podmínkami soukromí služby cloud",
"Cloud_troubleshooting": "Odstraňování chyb",
@@ -3694,4 +3692,4 @@
"Your_server_link": "Odkaz na Váš server",
"Your_temporary_password_is_password": "Vaše dočasné heslo je [password] .",
"Your_workspace_is_ready": "Váš prostředí je připraveno k použití 🎉"
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/da.i18n.json b/packages/rocketchat-i18n/i18n/da.i18n.json
index 8e2181aa273e..a89bc5c81dd4 100644
--- a/packages/rocketchat-i18n/i18n/da.i18n.json
+++ b/packages/rocketchat-i18n/i18n/da.i18n.json
@@ -681,7 +681,6 @@
"Closed_by_visitor": "Lukket af besøgende",
"Closing_chat": "Lukning af chat",
"Cloud": "Cloud",
- "Cloud_connect": "Rocket.Chat Cloud-forbindelse",
"Cloud_what_is_it": "Hvad er det her?",
"Cloud_what_is_it_description": "Rocket.Chat Cloud-forbindelse lader dig forbinde en Rocket.Chat-instans, som du selv er vært for, til vores sky. Hvis du gør det, kan du administrere dine licenser, regninger og support i Rocket.Chat Cloud.",
"Cloud_login_to_cloud": "Log ind i Rocket.Chat Cloud",
@@ -690,9 +689,9 @@
"Cloud_manually_input_token": "Indtast manuelt den token, der fulgte med Cloud-tilmeldingen.",
"Cloud_registration_required": "Tilmelding påkrævet",
"Cloud_registration_required_description": "Under konfigurationen valgte du tilsyneladende ikke at tilmelde din instans.",
- "Cloud_registration_requried_link_text": "Tryk her for at tilmelde din instans.",
+ "Cloud_registration_required_link_text": "Tryk her for at tilmelde din instans.",
"Cloud_error_in_authenticating": "Fejl i forbindelse med godkendelse",
- "Cloud_error_code": "Kode:",
+ "Cloud_error_code": "Kode: __errorCode__",
"Collaborative": "Samarbejde",
"Collapse_Embedded_Media_By_Default": "Skjul embedded media som standard",
"color": "Farve",
diff --git a/packages/rocketchat-i18n/i18n/de-IN.i18n.json b/packages/rocketchat-i18n/i18n/de-IN.i18n.json
index ee0713b989f0..c9c831324984 100644
--- a/packages/rocketchat-i18n/i18n/de-IN.i18n.json
+++ b/packages/rocketchat-i18n/i18n/de-IN.i18n.json
@@ -609,7 +609,6 @@
"Closed_by_visitor": "Durch Besucher geschlossen",
"Closing_chat": "Schließe Chat",
"Cloud": "Cloud",
- "Cloud_connect": "Rocket.Chat-Cloud-Connector",
"Cloud_connect_support": "Wenn Du noch immer keine Registrierungs-E-Mail erhalten haben, überprüfe bitte die o. g. Adresse. Wenn es dann immer noch Probleme gibt, erreichst Du unseren Support unter",
"Cloud_console": "Cloud Console",
"Cloud_what_is_it": "Was ist das?",
@@ -627,9 +626,9 @@
"Cloud_manually_input_token": "Gib das von der Cloud-Registrierungs-E-Mail erhaltene Token manuell ein.",
"Cloud_registration_required": "Registrierung erforderlich",
"Cloud_registration_required_description": "Sieht aus, als hättest Du Dich während des Setups nicht für die Registrierung Deines Arbeitsbereichs entschieden.",
- "Cloud_registration_requried_link_text": "Klicke hier, um Deinen Arbeitsbereich zu registrieren.",
+ "Cloud_registration_required_link_text": "Klicke hier, um Deinen Arbeitsbereich zu registrieren.",
"Cloud_error_in_authenticating": "Fehler beim Authentifizieren",
- "Cloud_error_code": "Code:",
+ "Cloud_error_code": "Code: __errorCode__",
"Cloud_status_page_description": "Wenn ein bestimmter Cloud-Dienste Probleme hat, lohnt sich ein Blick auf die Status-Seite der Cloud-Dienste unter",
"Cloud_troubleshooting": "Troubleshooting",
"Collaborative": "Kollaborativ",
diff --git a/packages/rocketchat-i18n/i18n/de.i18n.json b/packages/rocketchat-i18n/i18n/de.i18n.json
index 196da04d0b28..6de29f4bbc03 100644
--- a/packages/rocketchat-i18n/i18n/de.i18n.json
+++ b/packages/rocketchat-i18n/i18n/de.i18n.json
@@ -660,12 +660,10 @@
"Closing_chat": "Schließe Chat",
"Cloud": "Cloud",
"Cloud_Register_manually": "Manuell registrieren",
- "Cloud_click_here": "Wechseln Sie nach dem Kopieren des Texts zur Cloud-Konsole. [Klicke hier]()",
"Cloud_register_offline_finish_helper": "Nach Abschluss des Registrierungsvorgangs in der Cloud-Konsole, sollte ein Text angezeigt werden. Bitte hier einfügen, um die Registrierung abzuschließen.",
"Cloud_register_offline_helper": "Arbeitsbereiche können manuell registriert werden, wenn ein Airgap besteht oder der Netzwerkzugriff eingeschränkt ist. Kopieren Sie den folgenden Text und rufen Sie unsere Cloud-Konsole auf, um den Vorgang abzuschließen.",
"Cloud_register_success": "Ihr Arbeitsbereich wurde erfolgreich registriert!",
"Cloud_register_error": "Beim Verarbeiten Ihrer Anfrage ist ein Fehler aufgetreten. Bitte versuchen Sie es später noch einmal.",
- "Cloud_connect": "Rocket.Chat Cloud Connect",
"Cloud_connect_support": "Wenn Sie noch immer keine Registrierungs-E-Mail erhalten haben, überprüfen Sie bitte die o. g. Adresse. Wenn es dann immer noch Probleme gibt, erreichen Sie unseren Support unter",
"Cloud_console": "Cloud Console",
"Cloud_Info": "Cloud-Informationen",
@@ -684,9 +682,9 @@
"Cloud_manually_input_token": "Geben Sie das von der Cloud-Registrierungs-E-Mail erhaltene Token manuell ein.",
"Cloud_registration_required": "Registrierung erforderlich",
"Cloud_registration_required_description": "Sieht aus, als hätten Sie sich während des Setups nicht für die Registrierung Ihres Arbeitsbereichs entschieden.",
- "Cloud_registration_requried_link_text": "Klicken Sie hier, um Ihren Arbeitsbereich zu registrieren.",
+ "Cloud_registration_required_link_text": "Klicken Sie hier, um Ihren Arbeitsbereich zu registrieren.",
"Cloud_error_in_authenticating": "Fehler beim Authentifizieren",
- "Cloud_error_code": "Code:",
+ "Cloud_error_code": "Code: __errorCode__",
"Cloud_status_page_description": "Wenn ein bestimmter Cloud-Dienste Probleme hat, lohnt sich ein Blick auf die Status-Seite der Cloud-Dienste unter",
"Cloud_Service_Agree_PrivacyTerms": "Cloud-Dienst-Datenschutzbestimmungen zustimmen",
"Cloud_troubleshooting": "Troubleshooting",
@@ -3564,4 +3562,4 @@
"Your_server_link": "Ihre Serververbindung",
"Your_temporary_password_is_password": "Ihr temporäres Passwort lautet [password] .",
"Your_workspace_is_ready": "Ihr Arbeitsbereich ist einsatzbereit 🎉"
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/en.i18n.json b/packages/rocketchat-i18n/i18n/en.i18n.json
index 5253a9c83272..ffc3965f6d9c 100644
--- a/packages/rocketchat-i18n/i18n/en.i18n.json
+++ b/packages/rocketchat-i18n/i18n/en.i18n.json
@@ -388,6 +388,7 @@
"App_user_not_allowed_to_login": "App users are not allowed to log in directly.",
"Appearance": "Appearance",
"Application_added": "Application added",
+ "Application_delete_warning": "You will not be able to recover this Application!",
"Application_Name": "Application Name",
"Application_updated": "Application updated",
"Apply": "Apply",
@@ -705,12 +706,11 @@
"Closing_chat": "Closing chat",
"Cloud": "Cloud",
"Cloud_Register_manually": "Register Manually",
- "Cloud_click_here": "After copy the text, go to cloud console. [Click here]()",
+ "Cloud_click_here": "After copy the text, go to [cloud console (click here)](__cloudConsoleUrl__).",
"Cloud_register_offline_finish_helper": "After completing the registration process in the Cloud Console you should be presented with some text. Please paste it here to finish the registration.",
"Cloud_register_offline_helper": "Workspaces can be manually registered if airgapped or network access is restricted. Copy the text below and go to our Cloud Console to complete the process.",
"Cloud_register_success": "Your workspace has been successfully registered!",
"Cloud_register_error": "There has been an error trying to process your request. Please try again later.",
- "Cloud_connect": "Rocket.Chat Cloud Connect",
"Cloud_connect_support": "If you still haven't received a registration email please make sure your email is updated above. If you still have issues you can contact support at",
"Cloud_console": "Cloud Console",
"Cloud_Info": "Cloud Info",
@@ -729,9 +729,9 @@
"Cloud_manually_input_token": "Manually enter the token received from the Cloud Registration Email.",
"Cloud_registration_required": "Registration Required",
"Cloud_registration_required_description": "Looks like during setup you didn't chose to register your workspace.",
- "Cloud_registration_requried_link_text": "Click here to register your workspace.",
+ "Cloud_registration_required_link_text": "Click here to register your workspace.",
"Cloud_error_in_authenticating": "Error received while authenticating",
- "Cloud_error_code": "Code: ",
+ "Cloud_error_code": "Code: __errorCode__",
"Cloud_status_page_description": "If a particular Cloud Service is having issues you can check for known issues on our status page at",
"Cloud_Service_Agree_PrivacyTerms": "Cloud Service Agree PrivacyTerms",
"Cloud_troubleshooting": "Troubleshooting",
@@ -1038,6 +1038,8 @@
"Create_unique_rules_for_this_channel": "Create unique rules for this channel",
"Created": "Created",
"Created_at": "Created at",
+ "Created_by": "Created by",
+ "Created_as": "Created as",
"Created_at_s_by_s": "Created at %s by %s ",
"Created_at_s_by_s_triggered_by_s": "Created at %s by %s triggered by %s ",
"CRM_Integration": "CRM Integration",
@@ -1473,6 +1475,7 @@
"Everyone_can_access_this_channel": "Everyone can access this channel",
"Exact": "Exact",
"Example_s": "Example: %s
",
+ "Example_payload": "Example payload",
"Exclude_Botnames": "Exclude Bots",
"Exclude_Botnames_Description": "Do not propagate messages from bots whose name matches the regular expression above. If left empty, all messages from bots will be propagated.",
"Exclude_pinned": "Exclude pinned messages",
@@ -1845,6 +1848,8 @@
"Integration_Incoming_WebHook": "Incoming WebHook Integration",
"Integration_New": "New Integration",
"Integration_Outgoing_WebHook": "Outgoing WebHook Integration",
+ "Integration_Delete_Warning": "Deleting an Integrations cannot be undone.",
+ "Webhook_Details": "WebHook Details",
"Integration_Outgoing_WebHook_History": "Outgoing WebHook Integration History",
"Integration_Outgoing_WebHook_History_Data_Passed_To_Trigger": "Data Passed to Integration",
"Integration_Outgoing_WebHook_History_Data_Passed_To_URL": "Data Passed to URL",
@@ -2691,6 +2696,7 @@
"post-readonly": "Post ReadOnly",
"post-readonly_description": "Permission to post a message in a read-only channel",
"Post_as": "Post as",
+ "Post_to": "Post to",
"Post_to_Channel": "Post to Channel",
"Post_to_s_as_s": "Post to %s as %s ",
"Preferences": "Preferences",
@@ -2839,6 +2845,7 @@
"Removed_User": "Removed User",
"Replied_on": "Replied on",
"Reply": "Reply",
+ "Replay": "Replay",
"reply_counter": "__counter__ reply",
"reply_counter_plural": "__counter__ replies",
"Replies": "Replies",
@@ -2864,6 +2871,9 @@
"Reset_password": "Reset password",
"Reset_section_settings": "Reset Section Settings",
"Reset_Connection": "Reset Connection",
+ "Responding": "Responding",
+ "Response_description_pre": "If the handler wishes to post a response back into the channel, the following JSON should be returned as the body of the response:",
+ "Response_description_post": "Empty bodies or bodies with an empty text property will simply be ignored. Non-200 responses will be retried a reasonable number of times. A response will be posted using the alias and avatar specified above. You can override these informations as in the example above.",
"Restart": "Restart",
"Restart_the_server": "Restart the server",
"Retail": "Retail",
@@ -3009,6 +3019,7 @@
"Search_by_username": "Search by username",
"Search_Channels": "Search Channels",
"Search_current_provider_not_active": "Current Search Provider is not active",
+ "Search_Integrations": "Search Integrations",
"Search_message_search_failed": "Search request failed",
"Search_Messages": "Search Messages",
"Search_Page_Size": "Page Size",
@@ -3062,6 +3073,7 @@
"Sending": "Sending...",
"Sent_an_attachment": "Sent an attachment",
"Sent_from": "Sent from",
+ "Separate_multiple_words_with_commas": "Separate multiple words with commas",
"Served_By": "Served By",
"Server_File_Path": "Server File Path",
"Server_Folder_Path": "Server Folder Path",
@@ -3734,8 +3746,10 @@
"Welcome_to_the": "Welcome to the",
"Where_are_the_messages_being_sent?": "Where are the messages being sent?",
"When_is_the_chat_busier?": "When is the chat busier?",
+ "When_a_line_starts_with_one_of_there_words_post_to_the_URLs_below": "When a line starts with one of these words, post to the URL(s) below",
"Why_do_you_want_to_report_question_mark": "Why do you want to report?",
"will_be_able_to": "will be able to",
+ "Will_be_available_here_after_saving": "Will be available here after saving.",
"Worldwide": "Worldwide",
"Would_you_like_to_return_the_inquiry": "Would you like to return the inquiry?",
"Yes": "Yes",
diff --git a/packages/rocketchat-i18n/i18n/es.i18n.json b/packages/rocketchat-i18n/i18n/es.i18n.json
index 397df04e8260..ab29e482610c 100644
--- a/packages/rocketchat-i18n/i18n/es.i18n.json
+++ b/packages/rocketchat-i18n/i18n/es.i18n.json
@@ -685,7 +685,6 @@
"Closed_by_visitor": "Cerrado por el visitante",
"Closing_chat": "Cerrando chat",
"Cloud_Register_manually": "Registrar manualmente",
- "Cloud_click_here": "Después de copiar el texto, ve a la consola Cloud. [Haga clic aquí]()",
"Cloud_register_offline_finish_helper": "Después de completar el proceso de registro en la Cloud Console se le presentará un texto. Por favor, pégalo aquí para terminar el registro.",
"Cloud_register_offline_helper": "Los espacios de trabajo pueden registrarse manualmente si el acceso a la red está restringido. Copia el texto de abajo y ve a nuestra Cloud Console para completar el proceso.",
"Cloud_register_success": "¡Su espacio de trabajo ha sido registrado correctamente!",
@@ -706,9 +705,9 @@
"Cloud_manually_input_token": "Introduzca manualmente el token recibido en el correo electrónico de registro en la nube.",
"Cloud_registration_required": "Se requiere registro",
"Cloud_registration_required_description": "Parece que durante la configuración no eligió registrar su espacio de trabajo.",
- "Cloud_registration_requried_link_text": "Haga clic aquí para registrar su espacio de trabajo.",
+ "Cloud_registration_required_link_text": "Haga clic aquí para registrar su espacio de trabajo.",
"Cloud_error_in_authenticating": "Error durante la autentificación",
- "Cloud_error_code": "Código:",
+ "Cloud_error_code": "Código: __errorCode__",
"Cloud_status_page_description": "Si está teniendo problemas con un servicio en particular de la nube, puede comprobar los problemas conocidos en nuestra página de estado en",
"Cloud_Service_Agree_PrivacyTerms": "Acuerdos y términos de privacidad del servicio en la nube",
"Cloud_troubleshooting": "Solución de problemas",
@@ -3361,4 +3360,4 @@
"Your_push_was_sent_to_s_devices": "Su push fue enviado a los dispositivos %s",
"Your_server_link": "Su enlace de servidor",
"Your_workspace_is_ready": "Su espacio de trabajo está listo para usar 🎉"
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/fa.i18n.json b/packages/rocketchat-i18n/i18n/fa.i18n.json
index a1db2d28b55e..56e0c9601bfc 100644
--- a/packages/rocketchat-i18n/i18n/fa.i18n.json
+++ b/packages/rocketchat-i18n/i18n/fa.i18n.json
@@ -661,12 +661,10 @@
"Closing_chat": "در حال بستن چت",
"Cloud": "ابر",
"Cloud_Register_manually": "ثبت دستی",
- "Cloud_click_here": "پس از کپی کردن متن ، به کنسول ابری بروید. [اینجا کلیک کنید]()",
"Cloud_register_offline_finish_helper": "پس از اتمام مراحل ثبت نام در بسترCloud ، باید متنی برای شما ارائه شود. لطفاً برای پایان ثبت نام آن را در اینجا جایگذاری کنید.",
"Cloud_register_offline_helper": "در صورت محدود بودن دسترسی به شبکه ، می توان مکان های کاری را به صورت دستی ثبت کرد. متن را کپی کنید و برای تکمیل مراحل به کنسول Cloud ما بروید.",
"Cloud_register_success": "فضای کاری شما با موفقیت ثبت شده است!",
"Cloud_register_error": "هنگام پردازش درخواست شما خطایی رخ داده است. لطفا بعدا دوباره امتحان کنید.",
- "Cloud_connect": "اتصال Roket.Chat Cloud",
"Cloud_connect_support": "اگر هنوز ایمیل ثبت نام دریافت نکردید ، لطفاً اطمینان حاصل کنید که ایمیل شما در بالا به روز شده است. اگر هنوز مشکل دارید می توانید با پشتیبانی تماس بگیرید",
"Cloud_console": "کنسول Cloud",
"Cloud_Info": "اطلاعات Cloud",
@@ -684,9 +682,9 @@
"Cloud_manually_input_token": "نشانه دریافت شده از ایمیل ثبت نام Cloud را به صورت دستی وارد کنید.",
"Cloud_registration_required": "نیاز به ثبت نام",
"Cloud_registration_required_description": "به نظر می رسد در هنگام راه اندازی شما تصمیم به ثبت نام فضای کاری خود ندارید.",
- "Cloud_registration_requried_link_text": "برای ثبت فضای کاری خود اینجا را کلیک کنید.",
+ "Cloud_registration_required_link_text": "برای ثبت فضای کاری خود اینجا را کلیک کنید.",
"Cloud_error_in_authenticating": "هنگام تأیید اعتبار ، خطایی رخ داده",
- "Cloud_error_code": "کد:",
+ "Cloud_error_code": "کد: __errorCode__",
"Cloud_status_page_description": "اگر سرویس ویژه Cloud مشکلی دارد ، می توانید موارد شناخته شده را در صفحه وضعیت ما در اینجا بررسی کنید",
"Cloud_Service_Agree_PrivacyTerms": "سرویس ابر با حریم خصوصی موافقت می کند",
"Cloud_troubleshooting": "عیب یابی",
@@ -3057,4 +3055,4 @@
"Your_push_was_sent_to_s_devices": "فشار خود را به دستگاه %s را ارسال شد",
"Your_server_link": "لینک سرور شما",
"Your_workspace_is_ready": "فضای کاری شما آماده استفاده است"
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/fr.i18n.json b/packages/rocketchat-i18n/i18n/fr.i18n.json
index bd657ac1282a..a5e308554202 100644
--- a/packages/rocketchat-i18n/i18n/fr.i18n.json
+++ b/packages/rocketchat-i18n/i18n/fr.i18n.json
@@ -607,7 +607,7 @@
"Cloud_resend_email": "Envoyer à nouveau l'e-mail",
"Cloud_registration_required": "Inscription requise",
"Cloud_registration_required_description": "On dirait que, lors de l'installation, vous n'avez pas choisi d'enregistrer votre espace de travail.",
- "Cloud_registration_requried_link_text": "Cliquez ici pour enregistrer votre espace de travail.",
+ "Cloud_registration_required_link_text": "Cliquez ici pour enregistrer votre espace de travail.",
"Cloud_troubleshooting": "Dépannage",
"Collapse_Embedded_Media_By_Default": "Réduire tous les médias intégrés par défaut",
"color": "Couleur",
@@ -3094,4 +3094,4 @@
"Your_question": "Votre question",
"Your_server_link": "Le lien de votre serveur",
"Your_workspace_is_ready": "Votre espace de travail est prêt à l'emploi 🎉"
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/hr.i18n.json b/packages/rocketchat-i18n/i18n/hr.i18n.json
index 24b414b06202..34be45dec63a 100644
--- a/packages/rocketchat-i18n/i18n/hr.i18n.json
+++ b/packages/rocketchat-i18n/i18n/hr.i18n.json
@@ -600,7 +600,6 @@
"Closed_by_visitor": "Zatvorio posjetitelj",
"Closing_chat": "Zatvaranje chata",
"Cloud": "Cloud",
- "Cloud_connect": "Rocket.Chat Cloud Connect",
"Cloud_what_is_it": "Što je ovo?",
"Cloud_what_is_it_description": "Rocket.Chat Cloud Connect omogućuje vam povezivanje vašeg Rocket.Chat radnog prostora koje ste sami ugradili u naš Cloud. Na taj način možete upravljati licencama, naplatom i podrškom u Rocket.Chat Cloudu.",
"Cloud_login_to_cloud": "Prijava u Rocket.Chat Cloud",
@@ -609,9 +608,9 @@
"Cloud_manually_input_token": "Ručno unesite token primljen iz e-pošte za registraciju u Cloudu.",
"Cloud_registration_required": "Potrebna je registracija",
"Cloud_registration_required_description": "Izgleda da tijekom postavljanja niste odabrali registrirati svoj radni prostor.",
- "Cloud_registration_requried_link_text": "Kliknite ovdje da biste registrirali svoj radni prostor.",
+ "Cloud_registration_required_link_text": "Kliknite ovdje da biste registrirali svoj radni prostor.",
"Cloud_error_in_authenticating": "Pri provjeri autentičnosti primljena je pogreška",
- "Cloud_error_code": "Kod:",
+ "Cloud_error_code": "Kod: __errorCode__",
"Collaborative": "Kolaborativni",
"Collapse_Embedded_Media_By_Default": "Zadano sakrij sve ugrađene medije",
"color": "Boja",
@@ -2979,4 +2978,4 @@
"Your_push_was_sent_to_s_devices": "Push obavijest je poslana %s uređaje",
"Your_server_link": "Veza poslužitelja",
"Your_workspace_is_ready": "Radni je prostor spreman za upotrebu 🎉"
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/hu.i18n.json b/packages/rocketchat-i18n/i18n/hu.i18n.json
index 7d5173dd6067..974b45e77107 100644
--- a/packages/rocketchat-i18n/i18n/hu.i18n.json
+++ b/packages/rocketchat-i18n/i18n/hu.i18n.json
@@ -656,7 +656,6 @@
"Closed_by_visitor": "Látogató által bezárva",
"Closing_chat": "Beszélgetés bezáráse",
"Cloud": "Felhő",
- "Cloud_connect": "Rocket.Chat Felhő kapcsolódás",
"Cloud_console": "Felhő konzol",
"Cloud_what_is_it": "Mi ez?",
"Cloud_what_is_it_services_like": "Szolgáltatások, mint:",
@@ -670,9 +669,9 @@
"Cloud_manually_input_token": "Add meg kézzel a tokent, ami a Felhő regisztrációs e-mailben szerepel.",
"Cloud_registration_required": "Regisztráció szükséges",
"Cloud_registration_required_description": "Úgy tűnik, a beállítás során nem választottad a munkaterület regisztrációját.",
- "Cloud_registration_requried_link_text": "Kattints ide a munkahely regisztrálásához.",
+ "Cloud_registration_required_link_text": "Kattints ide a munkahely regisztrálásához.",
"Cloud_error_in_authenticating": "Hiba a hitelesítés során:",
- "Cloud_error_code": "Kód:",
+ "Cloud_error_code": "Kód: __errorCode__",
"Cloud_troubleshooting": "Hibakeresés",
"Collaborative": "Együttműködő",
"Collapse_Embedded_Media_By_Default": "Beágyazott média alapértelmezett összecsukása",
@@ -3355,4 +3354,4 @@
"Your_question": "Kérdésed",
"Your_server_link": "A szerver linkje",
"Your_workspace_is_ready": "A munkaterület készen áll a 🎉 használatára"
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/ja.i18n.json b/packages/rocketchat-i18n/i18n/ja.i18n.json
index 1073647cd010..0c7a6a5138a0 100644
--- a/packages/rocketchat-i18n/i18n/ja.i18n.json
+++ b/packages/rocketchat-i18n/i18n/ja.i18n.json
@@ -698,12 +698,10 @@
"Closing_chat": "閉じるチャット",
"Cloud": "クラウド",
"Cloud_Register_manually": "手動で登録する",
- "Cloud_click_here": "テキストをコピーしたら、クラウドコンソールに移動します。 [ここをクリック]()",
"Cloud_register_offline_finish_helper": "Cloud Consoleで登録プロセスを完了すると、テキストが表示されます。ここに貼り付けて登録を完了してください。",
"Cloud_register_offline_helper": "エアギャップまたはネットワークアクセスが制限されている場合、ワークスペースを手動で登録できます。以下のテキストをコピーし、クラウドコンソールに移動してプロセスを完了します。",
"Cloud_register_success": "ワークスペースが正常に登録されました!",
"Cloud_register_error": "リクエストの処理中にエラーが発生しました。後でもう一度やり直してください。",
- "Cloud_connect": "Rocket.Chat クラウド接続",
"Cloud_connect_support": "まだ登録メールが届いていない場合は、上記のメールアドレスを必ず更新してください。それでも問題が解決しない場合は、サポートに連絡することができます。",
"Cloud_console": "クラウドコンソール",
"Cloud_Info": "クラウド情報",
@@ -722,9 +720,9 @@
"Cloud_manually_input_token": "クラウド登録メールに記載されたトークンを手動で入力してください。",
"Cloud_registration_required": "登録が必要です",
"Cloud_registration_required_description": "セットアップ中にワークスペースを登録しない選択をしたようです。",
- "Cloud_registration_requried_link_text": "ワークスペースを登録するにはここをクリック。",
+ "Cloud_registration_required_link_text": "ワークスペースを登録するにはここをクリック。",
"Cloud_error_in_authenticating": "認証中にエラーが発生",
- "Cloud_error_code": "コード:",
+ "Cloud_error_code": "コード: __errorCode__",
"Cloud_status_page_description": "特定のクラウドサービスに問題がある場合は、Googleのステータスページで既知の問題を確認できます。",
"Cloud_Service_Agree_PrivacyTerms": "クラウドサービスに同意するプライバシー規約",
"Cloud_troubleshooting": "トラブルシューティング",
@@ -3761,4 +3759,4 @@
"Your_server_link": "サーバーのURLはこちら",
"Your_temporary_password_is_password": "一時的なパスワードは[password] です。",
"Your_workspace_is_ready": "ワークスペースの準備ができました🎉"
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/km.i18n.json b/packages/rocketchat-i18n/i18n/km.i18n.json
index 9a0586af194e..fa04232a815c 100644
--- a/packages/rocketchat-i18n/i18n/km.i18n.json
+++ b/packages/rocketchat-i18n/i18n/km.i18n.json
@@ -654,9 +654,7 @@
"Closing_chat": "ការជជែកបិទ",
"Cloud": "ពពក",
"Cloud_Register_manually": "ចុះឈ្មោះដោយដៃ",
- "Cloud_click_here": "បន្ទាប់ពីចម្លងអត្ថបទសូមចូលទៅកាន់កុងសូលក្លោដ។ [ចុចទីនេះ]()",
"Cloud_register_success": "កន្លែងការងាររបស់អ្នកត្រូវបានចុះឈ្មោះដោយជោគជ័យ!",
- "Cloud_connect": "ការតភ្ជាប់ពពក Rocket.Chat ",
"Cloud_connect_support": "ប្រសិនបើអ្នកនៅតែមិនទាន់បានទទួលអ៊ីមែលចុះឈ្មោះសូមប្រាកដថាអ៊ីមែលរបស់អ្នកត្រូវបានធ្វើបច្ចុប្បន្នភាពខាងលើ។ ប្រសិនបើអ្នកនៅតែមានបញ្ហាអ្នកអាចទាក់ទងការគាំទ្រ។",
"Cloud_console": "Cloud Console",
"Cloud_Info": "ព័ត៌មានពពក",
@@ -675,9 +673,9 @@
"Cloud_manually_input_token": "បញ្ចូលលេខសម្ងាត់ដែលបានទទួលដោយផ្ទាល់ពីអ៊ីម៉ែលចុះឈ្មោះពពក។",
"Cloud_registration_required": "ត្រូវការការចុះឈ្មោះ",
"Cloud_registration_required_description": "មើលទៅដូចក្នុងអំឡុងពេលរៀបចំដែលអ្នកមិនបានជ្រើសដើម្បីចុះឈ្មោះកន្លែងធ្វើការរបស់អ្នក។",
- "Cloud_registration_requried_link_text": "សូមចុចត្រង់នេះដើម្បីចុះឈ្មោះកន្លែងធ្វើការរបស់អ្នក។",
+ "Cloud_registration_required_link_text": "សូមចុចត្រង់នេះដើម្បីចុះឈ្មោះកន្លែងធ្វើការរបស់អ្នក។",
"Cloud_error_in_authenticating": "កំហុសបានទទួលខណៈពេលផ្ទៀងផ្ទាត់",
- "Cloud_error_code": "លេខកូដ:",
+ "Cloud_error_code": "លេខកូដ: __errorCode__",
"Cloud_status_page_description": "ប្រសិនបើសេវាកម្មពពកជាក់លាក់មួយមានបញ្ហាអ្នកអាចពិនិត្យមើលបញ្ហាដែលត្រូវបានគេស្គាល់នៅលើទំព័រស្ថានភាពរបស់យើង។",
"Cloud_troubleshooting": "ការដោះស្រាយបញ្ហា",
"Collaborative": "សហការ",
@@ -3174,4 +3172,4 @@
"Your_push_was_sent_to_s_devices": "ការជំរុញរបស់អ្នកត្រូវបានបញ្ជូនទៅកាន់ឧបករណ៍ %s បាន",
"Your_server_link": "តំណភ្ជាប់ម៉ាស៊ីនមេរបស់អ្នក",
"Your_workspace_is_ready": "កន្លែងធ្វើការរបស់អ្នករួចរាល់ដើម្បីប្រើ🎉"
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/ko.i18n.json b/packages/rocketchat-i18n/i18n/ko.i18n.json
index c7d4f94b65b5..ef5ec22f7827 100644
--- a/packages/rocketchat-i18n/i18n/ko.i18n.json
+++ b/packages/rocketchat-i18n/i18n/ko.i18n.json
@@ -599,15 +599,14 @@
"Closed_by_visitor": "방문자가 종료했습니다.",
"Closing_chat": "대화 종료 중..",
"Cloud": "클라우드",
- "Cloud_connect": "Rocket.Chat 클라우드 접속",
"Cloud_console": "클라우드 콘솔",
"Cloud_what_is_it": "이게 무엇입니까?",
"Cloud_login_to_cloud": "Rocket.Chat Cloud에 로그인",
"Cloud_update_email": "이메일 업데이트",
"Cloud_resend_email": "이메일 재전송",
"Cloud_registration_required": "등록이 필요합니다.",
- "Cloud_registration_requried_link_text": "작업 공간을 등록하려면 여기를 클릭하십시오.",
- "Cloud_error_code": "코드:",
+ "Cloud_registration_required_link_text": "작업 공간을 등록하려면 여기를 클릭하십시오.",
+ "Cloud_error_code": "코드: __errorCode__",
"Collaborative": "협업",
"Collapse_Embedded_Media_By_Default": "기본적으로 포함된 미디어 접기",
"color": "색",
@@ -3162,4 +3161,4 @@
"Your_question": "귀하의 질문",
"Your_server_link": "서버 링크",
"Your_workspace_is_ready": "Rocket.Chat을 사용할 준비가 되었습니다."
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/nl.i18n.json b/packages/rocketchat-i18n/i18n/nl.i18n.json
index 0915abcd6811..b63cd1f010db 100644
--- a/packages/rocketchat-i18n/i18n/nl.i18n.json
+++ b/packages/rocketchat-i18n/i18n/nl.i18n.json
@@ -609,7 +609,6 @@
"Closed_by_visitor": "Gesloten door bezoeker",
"Closing_chat": "closing-chat",
"Cloud": "Cloud",
- "Cloud_connect": "Rocket.Chat Cloud Connect",
"Cloud_connect_support": "Als u nog steeds geen registratie-e-mail heeft ontvangen, zorg er dan voor dat uw e-mail hierboven is bijgewerkt. Als u nog steeds problemen ondervindt, kunt u contact opnemen met ondersteuning op",
"Cloud_console": "Cloud Console",
"Cloud_what_is_it": "Wat is dit?",
@@ -3053,4 +3052,4 @@
"Your_push_was_sent_to_s_devices": "Je push werd verzonden naar %s apparaten",
"Your_server_link": "Uw serverlink",
"Your_workspace_is_ready": "Uw werkruimte is klaar voor gebruik 🎉"
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/pl.i18n.json b/packages/rocketchat-i18n/i18n/pl.i18n.json
index 7e2a9e7649cf..150a6cbd64d4 100644
--- a/packages/rocketchat-i18n/i18n/pl.i18n.json
+++ b/packages/rocketchat-i18n/i18n/pl.i18n.json
@@ -679,8 +679,6 @@
"Closed_by_visitor": "Zamknięte przez odwiedzającego",
"Closing_chat": "Zamykanie czatu",
"Cloud": "Chmura",
- "Cloud_click_here": "Po skopiowaniu tekstu, przejdź do konsoli chmury. [Kliknij tutaj]()",
- "Cloud_connect": "Połączenie z Chmurą Rocket.Chat",
"Cloud_console": "Konsola w chmurze",
"Cloud_Info": "Chmura",
"Cloud_what_is_it": "Co to jest?",
@@ -697,9 +695,9 @@
"Cloud_manually_input_token": "Wpisz ręcznie swój token otrzymany z wiadomości rejestrującej w Chmurze.",
"Cloud_registration_required": "Rejestracja wymagana",
"Cloud_registration_required_description": "Wygląda na to że podczas instalacji nie wybrałeś opcji rejestracji przestrzeni roboczej.",
- "Cloud_registration_requried_link_text": "Kliknij tutaj, aby zarejestrować swoją przestrzeń roboczą.",
+ "Cloud_registration_required_link_text": "Kliknij tutaj, aby zarejestrować swoją przestrzeń roboczą.",
"Cloud_error_in_authenticating": "Błąd podczas uwierzytelniania",
- "Cloud_error_code": "Kod:",
+ "Cloud_error_code": "Kod: __errorCode__",
"Cloud_troubleshooting": "Rozwiązywanie problemów",
"Collaborative": "Współpracujący",
"Collapse_Embedded_Media_By_Default": "Zwiń media (obrazki itp.) domyślnie",
@@ -3402,4 +3400,4 @@
"Your_question": "Twoje pytanie",
"Your_server_link": "Twój link do serwera",
"Your_workspace_is_ready": "Twój obszar roboczy jest gotowy do użycia 🎉"
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json
index bcbd42d61a08..fe136fe92dfd 100644
--- a/packages/rocketchat-i18n/i18n/pt-BR.i18n.json
+++ b/packages/rocketchat-i18n/i18n/pt-BR.i18n.json
@@ -679,7 +679,6 @@
"Cloud_Register_manually": "Registre manualmente",
"Cloud_register_success": "A sua área de trabalho foi registrada com sucesso!",
"Cloud_register_error": "Houve um erro ao processar sua solicitação. Por favor tente novamente mais tarde. ",
- "Cloud_connect": "Rocket.Chat Cloud Connect",
"Cloud_connect_support": "Se você ainda não recebeu um e-mail de registro, verifique se o seu e-mail está atualizado acima. Se você ainda tiver problemas, entre em contato com o suporte em",
"Cloud_what_is_it": "O que é isso?",
"Cloud_what_is_it_description": "O Rocket.Chat Cloud Connect permite que você conecte seu workspace Rocket.Chat auto-hospedado à nossa nuvem. Fazer isso permite que você gerencie suas licenças, Faturamento e Suporte no Rocket.Chat Cloud.",
@@ -691,9 +690,9 @@
"Cloud_manually_input_token": "Insira manualmente o token recebido do e-mail de registro na nuvem.",
"Cloud_registration_required": "Registro requerido",
"Cloud_registration_required_description": "Parece que durante a configuração você não escolheu registrar seu workspace.",
- "Cloud_registration_requried_link_text": "Clique aqui para registrar seu workspace.",
+ "Cloud_registration_required_link_text": "Clique aqui para registrar seu workspace.",
"Cloud_error_in_authenticating": "Erro recebido durante a autenticação",
- "Cloud_error_code": "Código:",
+ "Cloud_error_code": "Código: __errorCode__",
"Cloud_troubleshooting": "Resolução de problemas",
"Collaborative": "Colaborativo",
"Collapse_Embedded_Media_By_Default": "Esconder mídia por padrão",
diff --git a/packages/rocketchat-i18n/i18n/pt.i18n.json b/packages/rocketchat-i18n/i18n/pt.i18n.json
index 715088a58199..dc4826591c71 100644
--- a/packages/rocketchat-i18n/i18n/pt.i18n.json
+++ b/packages/rocketchat-i18n/i18n/pt.i18n.json
@@ -637,7 +637,6 @@
"Closed_by_visitor": "Encerrado pelo convidado",
"Closing_chat": "A encerrar chat",
"Cloud": "Nuvem",
- "Cloud_connect": "Rocket.Chat Cloud Connect",
"Cloud_connect_support": "Se ainda não recebeu um e-mail de registo, verifique se o seu e-mail está atualizado acima. Se ainda tiver problemas, entre em contacto com o suporte em",
"Cloud_console": "Cloud Console",
"Cloud_what_is_it": "O que é isto?",
@@ -650,9 +649,9 @@
"Cloud_manually_input_token": "Insira manualmente o token recebido do e-mail de registro na nuvem.",
"Cloud_registration_required": "Registro requerido",
"Cloud_registration_required_description": "Parece que durante a configuração você não escolheu registrar seu workspace.",
- "Cloud_registration_requried_link_text": "Clique aqui para registrar seu workspace.",
+ "Cloud_registration_required_link_text": "Clique aqui para registrar seu workspace.",
"Cloud_error_in_authenticating": "Erro recebido durante a autenticação",
- "Cloud_error_code": "Código:",
+ "Cloud_error_code": "Código: __errorCode__",
"Collaborative": "Colaborativo",
"Collapse_Embedded_Media_By_Default": "Esconder Leitor por padrão",
"color": "Côr",
@@ -3248,4 +3247,4 @@
"Your_question": "A sua pergunta",
"Your_server_link": "O link do seu servidor",
"Your_workspace_is_ready": "O seu espaço de trabalho está pronto a usar 🎉"
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/ru.i18n.json b/packages/rocketchat-i18n/i18n/ru.i18n.json
index f6fbbc1689b2..04e057628a07 100644
--- a/packages/rocketchat-i18n/i18n/ru.i18n.json
+++ b/packages/rocketchat-i18n/i18n/ru.i18n.json
@@ -662,12 +662,10 @@
"Closing_chat": "Закрыть чат",
"Cloud": "Облако",
"Cloud_Register_manually": "Зарегистрироваться вручную",
- "Cloud_click_here": "После копирования текста перейдите на Cloud Console. [Щелкните здесь]()",
"Cloud_register_offline_finish_helper": "После завершения процесса регистрации в Cloud Console вам должен быть представлен некоторый текст. Пожалуйста, вставьте его здесь, чтобы закончить регистрацию.",
"Cloud_register_offline_helper": "Рабочие места могут быть зарегистрированы вручную, если доступ в сеть ограничен. Скопируйте текст ниже и перейдите в нашу Cloud Console, чтобы завершить процесс.",
"Cloud_register_success": "Ваше рабочее место успешно зарегистрировано!",
"Cloud_register_error": "Произошла ошибка при попытке обработать ваш запрос. Пожалуйста, попробуйте позже.",
- "Cloud_connect": "Rocket.Chat Cloud Connect",
"Cloud_connect_support": "Если Вы все еще не получили регистрационное электронное письмо, пожалуйста, удостоверьтесь, что Ваша электронная почта обновлена выше. Если у Вас все еще есть проблемы, вы можете связаться с поддержкой по адресу",
"Cloud_console": "Облачная Консоль",
"Cloud_Info": "Информация об облаке",
@@ -686,9 +684,9 @@
"Cloud_manually_input_token": "Вручную введите токен, полученный из электронного письма регистрации в облаке.",
"Cloud_registration_required": "Требуется регистрация",
"Cloud_registration_required_description": "Похоже, что во время установки вы не решили регистрировать свое рабочее пространство.",
- "Cloud_registration_requried_link_text": "Нажмите здесь, чтобы зарегистрировать ваше рабочее пространство.",
+ "Cloud_registration_required_link_text": "Нажмите здесь, чтобы зарегистрировать ваше рабочее пространство.",
"Cloud_error_in_authenticating": "Ошибка при аутентификации",
- "Cloud_error_code": "Код:",
+ "Cloud_error_code": "Код: __errorCode__",
"Cloud_status_page_description": "Если у конкретной облачной службы возникают проблемы, вы можете проверить ее на нашей странице состояния по адресу",
"Cloud_Service_Agree_PrivacyTerms": "Согласие с условиями конфиденциальности Cloud Service",
"Cloud_troubleshooting": "Исправление проблем",
@@ -3554,4 +3552,4 @@
"Your_server_link": "Ссылка на ваш сервер",
"Your_temporary_password_is_password": "Ваш временный пароль [password] .",
"Your_workspace_is_ready": "Ваше рабочее пространство готово к работе 🎉"
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/sv.i18n.json b/packages/rocketchat-i18n/i18n/sv.i18n.json
index eb5398c34929..1cc153579053 100644
--- a/packages/rocketchat-i18n/i18n/sv.i18n.json
+++ b/packages/rocketchat-i18n/i18n/sv.i18n.json
@@ -573,7 +573,6 @@
"Closed_by_visitor": "Stängd av besökare",
"Closing_chat": "Stänger chat",
"Cloud": "Cloud",
- "Cloud_connect": "Rocket.Chat Cloud Connect",
"Collaborative": "Kollaborativ",
"Collapse_Embedded_Media_By_Default": "Kollapsa inbäddad media som standard",
"color": "Färg",
@@ -2945,4 +2944,4 @@
"Your_push_was_sent_to_s_devices": "Din push skickades till %s enheter",
"Your_server_link": "Din serverlänk",
"Your_workspace_is_ready": "Din arbetsyta är redo att använda 🎉"
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/tr.i18n.json b/packages/rocketchat-i18n/i18n/tr.i18n.json
index d463c1eaa310..cfe4dab5ba2c 100644
--- a/packages/rocketchat-i18n/i18n/tr.i18n.json
+++ b/packages/rocketchat-i18n/i18n/tr.i18n.json
@@ -634,7 +634,6 @@
"Cloud_Register_manually": "Manuel Kayıt",
"Cloud_register_success": "Çalışma alanınız başarıyla kaydedildi!",
"Cloud_register_error": "İsteğiniz işlenirken bir hata oluştu. Lütfen daha sonra tekrar deneyiniz.",
- "Cloud_connect": "Rocket.Chat Cloud Bağlantısı",
"Cloud_console": "Bulut Konsolu",
"Cloud_what_is_it": "Bu nedir?",
"Cloud_what_is_it_description": "Rocket.Chat Cloud Connect, kendi barındırdığınız Rocket.Chat Workspace'inizi Cloud'umuza bağlamanızı sağlar. Bunu yapmak, Rocket.Chat Cloud'daki lisanslarınızı, Fatura ve Desteği yönetmenizi etkinleştirir.",
@@ -650,9 +649,9 @@
"Cloud_manually_input_token": "Cloud Kayıt E-Postasından alınan belirteci elle girin.",
"Cloud_registration_required": "Kayıt Gerekli",
"Cloud_registration_required_description": "Görünen o ki kurulum sırasında çalışma alanınızı kaydetmeyi seçmediniz.",
- "Cloud_registration_requried_link_text": "Çalışma alanınızı kaydetmek için buraya tıklayın.",
+ "Cloud_registration_required_link_text": "Çalışma alanınızı kaydetmek için buraya tıklayın.",
"Cloud_error_in_authenticating": "Doğrulama sırasında hata alındı",
- "Cloud_error_code": "Kod:",
+ "Cloud_error_code": "Kod: __errorCode__",
"Cloud_troubleshooting": "Sorun Giderme",
"Collaborative": "Birlikte Çalışma",
"Collapse_Embedded_Media_By_Default": "Gömülü Ortam Varsayılan Olarak Daraltılsın",
@@ -3326,4 +3325,4 @@
"Your_question": "Sorunuz",
"Your_server_link": "Sunucu bağlantınız",
"Your_workspace_is_ready": "Çalışma alanınız kullanılmaya hazır 🎉"
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/uk.i18n.json b/packages/rocketchat-i18n/i18n/uk.i18n.json
index 7b4e77467295..bde08843bb17 100644
--- a/packages/rocketchat-i18n/i18n/uk.i18n.json
+++ b/packages/rocketchat-i18n/i18n/uk.i18n.json
@@ -674,12 +674,10 @@
"Closing_chat": "закриття чату",
"Cloud": "Хмара",
"Cloud_Register_manually": "Реєстрація вручну",
- "Cloud_click_here": "Після копіювання тексту перейдіть до хмарної консолі. [Натисніть тут]()",
"Cloud_register_offline_finish_helper": "ПІсля завершення процедури реєстрації у Cloud Console ви маєте побачити текст. Вставте цей текст сюди, щоб закінчити реєстрацію.",
"Cloud_register_offline_helper": "Якщо ваш доступ до мережі обмежено або застосовується фізична ізоляція мережі, робочі простори можна зареєструвати вручну. Скопіюйте текст, що його наведено нижче, та перейдіть до нашої консолі Cloud Console, щоб завершити процес.",
"Cloud_register_success": "Ваша робоча область успішно зареєстрована!",
"Cloud_register_error": "Під час обробки вашого запиту сталася помилка. Будь ласка, спробуйте пізніше.",
- "Cloud_connect": "З'єднання Rocket.Chat Cloud ",
"Cloud_connect_support": "Якщо ви все ще не отримали електронний лист, будь ласка, переконайтеся, що ваша електронна адреса була оновлена вище. Якщо у вас все ще виникають проблеми, ви можете зв’язатися із службою підтримки",
"Cloud_console": "Хмарна консоль",
"Cloud_Info": "Інформація про хмару",
@@ -698,9 +696,9 @@
"Cloud_manually_input_token": "Вучну ввести токен, отриманий з електонної пошти при реєстрації у хмарі.",
"Cloud_registration_required": "Потрібна реєстрація",
"Cloud_registration_required_description": "Схоже, що під час налаштування ви не вибрали реєстрацію робочої області.",
- "Cloud_registration_requried_link_text": "Натисніть тут, щоб зареєструвати робочу область.",
+ "Cloud_registration_required_link_text": "Натисніть тут, щоб зареєструвати робочу область.",
"Cloud_error_in_authenticating": "Отримана помилка під час авторизації",
- "Cloud_error_code": "Код:",
+ "Cloud_error_code": "Код: __errorCode__",
"Cloud_status_page_description": "Якщо у певної хмарної служби є проблеми, ви можете перевірити відомі проблеми на нашій сторінці статусу за адресою",
"Cloud_Service_Agree_PrivacyTerms": "Згода з умовами конфіденційності Cloud Service",
"Cloud_troubleshooting": "Вирішення проблем",
@@ -3389,4 +3387,4 @@
"Your_server_link": "Посилання на Ваш сервер",
"Your_temporary_password_is_password": "Ваш тимчасовий пароль [password] .",
"Your_workspace_is_ready": "Ваш робочий простір готовий до використання 🎉"
-}
\ No newline at end of file
+}
diff --git a/packages/rocketchat-i18n/i18n/zh-TW.i18n.json b/packages/rocketchat-i18n/i18n/zh-TW.i18n.json
index 263ebd612abe..6b28d5fc0789 100644
--- a/packages/rocketchat-i18n/i18n/zh-TW.i18n.json
+++ b/packages/rocketchat-i18n/i18n/zh-TW.i18n.json
@@ -700,12 +700,10 @@
"Closing_chat": "關閉聊天中",
"Cloud": "雲端",
"Cloud_Register_manually": "手動註冊",
- "Cloud_click_here": "在複製完文字後,到雲端控制介面。[點這裡]()",
"Cloud_register_offline_finish_helper": "在雲端控制介面完成註冊程序之後您應該會顯示一些文字。請貼到這裡來完成註冊。",
"Cloud_register_offline_helper": "如果存在間隙或網路存取受到限制,則可以手動註冊工作區。複製下面的文字,然後轉到我們的雲端控制介面以完成該過程。",
"Cloud_register_success": "您的工作區已成功註冊!",
"Cloud_register_error": "嘗試處理您的請求時出錯。請稍後再試。",
- "Cloud_connect": "Rocket.Chat 雲端連線",
"Cloud_connect_support": "假如您仍沒有收到註冊電子郵件請確認您的電子郵件已更新。假如您仍有問題您可以連絡支援在",
"Cloud_console": "雲端控制台",
"Cloud_Info": "雲端資訊",
@@ -724,9 +722,9 @@
"Cloud_manually_input_token": "手動輸入在雲端註冊的電子郵件來收 token 郵件。",
"Cloud_registration_required": "必須註冊",
"Cloud_registration_required_description": "看起來安裝過程中您沒有選擇您的工作區域。",
- "Cloud_registration_requried_link_text": "點擊這裡來註冊您的工作區域。",
+ "Cloud_registration_required_link_text": "點擊這裡來註冊您的工作區域。",
"Cloud_error_in_authenticating": "當驗證時收到錯誤",
- "Cloud_error_code": "代碼:",
+ "Cloud_error_code": "代碼: __errorCode__",
"Cloud_status_page_description": "假如特定的雲端服務有問題,您可以檢查在我們的狀態頁面找到已知問題",
"Cloud_Service_Agree_PrivacyTerms": "雲端服務同意隱私條款",
"Cloud_troubleshooting": "故障排除",
diff --git a/packages/rocketchat-i18n/i18n/zh.i18n.json b/packages/rocketchat-i18n/i18n/zh.i18n.json
index c583accc2e93..e797495f2c6b 100644
--- a/packages/rocketchat-i18n/i18n/zh.i18n.json
+++ b/packages/rocketchat-i18n/i18n/zh.i18n.json
@@ -660,12 +660,10 @@
"Closing_chat": "正在关闭聊天",
"Cloud": "Cloud ",
"Cloud_Register_manually": "手动注册",
- "Cloud_click_here": "复制文字后,访问云端控制界面。[单击此处]()",
"Cloud_register_offline_finish_helper": "在Cloud Console中完成注册过程后,应该显示出一些文本。请粘贴到此处以完成注册。",
"Cloud_register_offline_helper": "如果网络访问受到限制,可以手动注册工作区。复制下面的文字并转到Cloud Console以完成本次操作。",
"Cloud_register_success": "您的工作区已成功注册!",
"Cloud_register_error": "处理请求时出错。请稍后再试。",
- "Cloud_connect": "Rocket.Chat Cloud 连接",
"Cloud_connect_support": "如果你尚未收到注册邮件请检查上方邮件是否正确。仍有问题可获取支持于",
"Cloud_console": "云控制台",
"Cloud_Info": "云端资料",
@@ -684,9 +682,9 @@
"Cloud_manually_input_token": "手动输入在 Cloud 注册邮件中包含的令牌。",
"Cloud_registration_required": "要求注册",
"Cloud_registration_required_description": "在安装过程中,您似乎没有选择注册工作区。",
- "Cloud_registration_requried_link_text": "点击此处注册您的工作区。",
+ "Cloud_registration_required_link_text": "点击此处注册您的工作区。",
"Cloud_error_in_authenticating": "验证出错",
- "Cloud_error_code": "代码:",
+ "Cloud_error_code": "代码: __errorCode__",
"Cloud_status_page_description": "如果某项云服务有问题您可以查看我们的状态页面于",
"Cloud_troubleshooting": "故障排除",
"Collaborative": "协作",
@@ -3546,4 +3544,4 @@
"Your_server_link": "您的服务器链接",
"Your_temporary_password_is_password": "您的暂时密码为 [password] 。",
"Your_workspace_is_ready": "您的工作区已准备好使用🎉"
-}
\ No newline at end of file
+}