import {
	IExecuteFunctions,
} from 'n8n-core';

import {
	IDataObject,
	INodeExecutionData,
	INodeType,
	INodeTypeDescription,
} from 'n8n-workflow';

import {
	vonageApiRequest,
} from './GenericFunctions';

export class Vonage implements INodeType {
	description: INodeTypeDescription = {
		displayName: 'Vonage',
		name: 'vonage',
		icon: 'file:vonage.png',
		group: ['input'],
		version: 1,
		subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
		description: 'Consume Vonage API',
		defaults: {
			name: 'Vonage',
			color: '#000000',
		},
		inputs: ['main'],
		outputs: ['main'],
		credentials: [
			{
				name: 'vonageApi',
				required: true,
			},
		],
		properties: [
			{
				displayName: 'Resource',
				name: 'resource',
				type: 'options',
				options: [
					{
						name: 'SMS',
						value: 'sms',
					},
				],
				default: 'sms',
				description: 'The resource to operate on.',
			},
			{
				displayName: 'Operation',
				name: 'operation',
				type: 'options',
				options: [
					{
						name: 'Send',
						value: 'send',
					},
				],
				displayOptions: {
					show: {
						resource: [
							'sms',
						],
					},
				},
				default: 'send',
				description: 'The resource to operate on.',
			},
			{
				displayName: 'From',
				name: 'from',
				type: 'string',
				displayOptions: {
					show: {
						resource: [
							'sms',
						],
						operation: [
							'send',
						],
					},
				},
				default: '',
				description: `The name or number the message should be sent from`,
			},
			{
				displayName: 'To',
				name: 'to',
				type: 'string',
				displayOptions: {
					show: {
						resource: [
							'sms',
						],
						operation: [
							'send',
						],
					},
				},
				default: '',
				description: `The number that the message should be sent to. Numbers are specified in E.164 format.`,
			},
			// {
			// 	displayName: 'Type',
			// 	name: 'type',
			// 	type: 'options',
			// 	displayOptions: {
			// 		show: {
			// 			resource: [
			// 				'sms',
			// 			],
			// 			operation: [
			// 				'send',
			// 			],
			// 		},
			// 	},
			// 	options: [
			// 		{
			// 			name: 'Binary',
			// 			value: 'binary',
			// 		},
			// 		{
			// 			name: 'Text',
			// 			value: 'text',
			// 		},
			// 		{
			// 			name: 'Wappush',
			// 			value: 'wappush',
			// 		},
			// 		{
			// 			name: 'Unicode',
			// 			value: 'unicode',
			// 		},
			// 		{
			// 			name: 'VCAL',
			// 			value: 'vcal',
			// 		},
			// 		{
			// 			name: 'VCARD',
			// 			value: 'vcard',
			// 		},
			// 	],
			// 	default: 'text',
			// 	description: 'The format of the message body',
			// },
			// {
			// 	displayName: 'Binary Property',
			// 	name: 'binaryPropertyName',
			// 	displayOptions: {
			// 		show: {
			// 			resource: [
			// 				'sms',
			// 			],
			// 			operation: [
			// 				'send',
			// 			],
			// 			type: [
			// 				'binary',
			// 			],
			// 		},
			// 	},
			// 	type: 'string',
			// 	default: 'data',
			// 	description: 'Object property name which holds binary data.',
			// 	required: true,
			// },
			// {
			// 	displayName: 'Body',
			// 	name: 'body',
			// 	type: 'string',
			// 	displayOptions: {
			// 		show: {
			// 			resource: [
			// 				'sms',
			// 			],
			// 			operation: [
			// 				'send',
			// 			],
			// 			type: [
			// 				'binary',
			// 			],
			// 		},
			// 	},
			// 	default: '',
			// 	description: 'Hex encoded binary data',
			// },
			// {
			// 	displayName: 'UDH',
			// 	name: 'udh',
			// 	type: 'string',
			// 	displayOptions: {
			// 		show: {
			// 			resource: [
			// 				'sms',
			// 			],
			// 			operation: [
			// 				'send',
			// 			],
			// 			type: [
			// 				'binary',
			// 			],
			// 		},
			// 	},
			// 	default: '',
			// 	description: 'Your custom Hex encoded User Data Header',
			// },
			// {
			// 	displayName: 'Title',
			// 	name: 'title',
			// 	displayOptions: {
			// 		show: {
			// 			resource: [
			// 				'sms',
			// 			],
			// 			operation: [
			// 				'send',
			// 			],
			// 			type: [
			// 				'wappush',
			// 			],
			// 		},
			// 	},
			// 	type: 'string',
			// 	default: '',
			// 	description: 'The title for a wappush SMS',
			// },
			// {
			// 	displayName: 'URL',
			// 	name: 'url',
			// 	type: 'string',
			// 	displayOptions: {
			// 		show: {
			// 			resource: [
			// 				'sms',
			// 			],
			// 			operation: [
			// 				'send',
			// 			],
			// 			type: [
			// 				'wappush',
			// 			],
			// 		},
			// 	},
			// 	default: '',
			// 	description: 'The URL of your website',
			// },
			// {
			// 	displayName: 'Validity (in minutes)',
			// 	name: 'validity',
			// 	type: 'number',
			// 	default: 0,
			// 	displayOptions: {
			// 		show: {
			// 			resource: [
			// 				'sms',
			// 			],
			// 			operation: [
			// 				'send',
			// 			],
			// 			type: [
			// 				'wappush',
			// 			],
			// 		},
			// 	},
			// 	description: 'The availability for an SMS in minutes',
			// },
			{
				displayName: 'Message',
				name: 'message',
				type: 'string',
				displayOptions: {
					show: {
						resource: [
							'sms',
						],
						operation: [
							'send',
						],
						// type: [
						// 	'text',
						// 	'unicode',
						// ],
					},
				},
				default: '',
				description: `The body of the message being sent`,
			},
			// {
			// 	displayName: 'VCard',
			// 	name: 'vcard',
			// 	type: 'string',
			// 	displayOptions: {
			// 		show: {
			// 			resource: [
			// 				'sms',
			// 			],
			// 			operation: [
			// 				'send',
			// 			],
			// 			type: [
			// 				'vcard',
			// 			],
			// 		},
			// 	},
			// 	default: '',
			// 	description: 'A business card in vCard format',
			// },
			// {
			// 	displayName: 'VCal',
			// 	name: 'vcal',
			// 	type: 'string',
			// 	displayOptions: {
			// 		show: {
			// 			resource: [
			// 				'sms',
			// 			],
			// 			operation: [
			// 				'send',
			// 			],
			// 			type: [
			// 				'vcal',
			// 			],
			// 		},
			// 	},
			// 	default: '',
			// 	description: 'A calendar event in vCal format',
			// },
			{
				displayName: 'Additional Fields',
				name: 'additionalFields',
				type: 'collection',
				placeholder: 'Add Field',
				displayOptions: {
					show: {
						resource: [
							'sms',
						],
						operation: [
							'send',
						],
					},
				},
				default: {},
				options: [
					{
						displayName: 'Account Ref',
						name: 'account-ref',
						type: 'string',
						default: '',
						description: 'An optional string used to identify separate accounts using the SMS endpoint for billing purposes. To use this feature, please email support@nexmo.com',
					},
					{
						displayName: 'Callback',
						name: 'callback',
						type: 'string',
						default: '',
						description: 'The webhook endpoint the delivery receipt for this sms is sent to. This parameter overrides the webhook endpoint you set in Dashboard.',
					},
					{
						displayName: 'Client Ref',
						name: 'client-ref',
						type: 'string',
						default: '',
						description: 'You can optionally include your own reference of up to 40 characters.',
					},
					{
						displayName: 'Message Class',
						name: 'message-class',
						type: 'options',
						options: [
							{
								name: '0',
								value: 0,
							},
							{
								name: '1',
								value: 1,
							},
							{
								name: '2',
								value: 2,
							},
							{
								name: '3',
								value: 3,
							},
						],
						default: '',
						description: 'The Data Coding Scheme value of the message',
					},
					{
						displayName: 'Protocol ID',
						name: 'protocol-id',
						type: 'string',
						default: '',
						description: 'The value of the protocol identifier to use. Ensure that the value is aligned with udh.',
					},
					{
						displayName: 'Status Report Req',
						name: 'status-report-req',
						type: 'boolean',
						default: false,
						description: 'Boolean indicating if you like to receive a Delivery Receipt.',
					},
					{
						displayName: 'TTL (in minutes)',
						name: 'ttl',
						type: 'number',
						default: 4320,
						description: 'By default Nexmo attempt delivery for 72 hours',
					},
				],
			},
		],
	};

	async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
		const items = this.getInputData();
		const returnData: IDataObject[] = [];
		const length = (items.length as unknown) as number;
		const qs: IDataObject = {};
		let responseData;
		const resource = this.getNodeParameter('resource', 0) as string;
		const operation = this.getNodeParameter('operation', 0) as string;
		for (let i = 0; i < length; i++) {

			if (resource === 'sms') {

				if (operation === 'send') {

					const from = this.getNodeParameter('from', i) as string;

					const to = this.getNodeParameter('to', i) as string;

					const type = this.getNodeParameter('type', i, 'text') as string;

					const body: IDataObject = {
						from,
						to,
						type,
					};

					if (type === 'text' || type === 'unicode') {
						const message = this.getNodeParameter('message', i) as string;

						body.text = message;
					}

					if (type === 'binary') {
						const data = this.getNodeParameter('body', i) as string;

						const udh = this.getNodeParameter('udh', i) as string;

						body.udh = udh;

						body.body = data;
					}

					if (type === 'wappush') {
						const title = this.getNodeParameter('title', i) as string;

						const url = this.getNodeParameter('url', i) as string;

						const validity = this.getNodeParameter('validity', i) as number;

						body.title = title;

						body.url = url;

						body.validity = validity * 60000;
					}

					if (type === 'vcard') {
						const vcard = this.getNodeParameter('vcard', i) as string;

						body.vcard = vcard;
					}

					if (type === 'vcal') {
						const vcal = this.getNodeParameter('vcal', i) as string;

						body.vcal = vcal;
					}

					const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;

					Object.assign(body, additionalFields);

					if (body.ttl) {
						// transform minutes to milliseconds
						body.ttl = (body.ttl as number) * 60000;
					}

					responseData = await vonageApiRequest.call(this, 'POST', '/sms/json', body);

					responseData = responseData.messages;
				}
			}
		}
		if (Array.isArray(responseData)) {
			returnData.push.apply(returnData, responseData as IDataObject[]);
		} else if (responseData !== undefined) {
			returnData.push(responseData as IDataObject);
		}
		return [this.helpers.returnJsonArray(returnData)];
	}
}


responseData = await githubApiRequest.call(this, 'POST', endpoint, body);

// Get the data of the already registered webhook

responseData = await githubApiRequest.call(this, 'GET', endpoint, body);

keep keep keep keep keep replace keep keep keep keep keep

import { INodeProperties } from 'n8n-workflow';

export const emailOperations = [
	{
		displayName: 'Operation',
		name: 'operation',
		type: 'options',
		displayOptions: {
			show: {
				resource: [
					'email',
				],
			},
		},
		options: [
			{
				name: 'Send',
				value: 'send',
				description: 'Send a email',
			},
			{
				name: 'Send Template',
				value: 'sendTemplate',
				description: 'Send a email template',
			},
		],
		default: 'send',
		description: 'The operation to perform.',
	},
] as INodeProperties[];

export const emailFields = [

/* -------------------------------------------------------------------------- */
/*                                email:send                                  */
/* -------------------------------------------------------------------------- */
	{
		displayName: 'From Email',
		name: 'fromEmail',
		type: 'string',
		required: true,
		default: '',
		displayOptions: {
			show: {
				resource: [
					'email',
				],
				operation: [
					'send',
				],
			},
		},
		placeholder: 'admin@example.com',
		description: 'The title for the email',
	},
	{
		displayName: 'To Email',
		name: 'toEmail',
		type: 'string',
		required: true,
		placeholder: 'info@example.com',
		description: 'Email address of the recipient. Multiple ones can be separated by comma.',
		displayOptions: {
			show: {
				resource: [
					'email',
				],
				operation: [
					'send',
				],
			},
		},
	},
	{
		displayName: 'Subject',
		name: 'subject',
		type: 'string',
		default: '',
		placeholder: 'My subject line',
		description: 'Subject line of the email.',
	},
	{
		displayName: 'Text',
		name: 'text',
		type: 'string',
		typeOptions: {
			alwaysOpenEditWindow: true,
		},
		displayOptions: {
			show: {
				resource: [
					'email',
				],
				operation: [
					'send',
				],
			},
		},
		default: '',
		description: 'Plain text message of email.',
	},
	{
		displayName: 'HTML',
		name: 'html',
		type: 'string',
		typeOptions: {
			alwaysOpenEditWindow: true,
		},
		displayOptions: {
			show: {
				resource: [
					'email',
				],
				operation: [
					'send',
				],
			},
		},
		default: '',
		description: 'HTML text message of email.',
	},
	{
		displayName: 'Additional Fields',
		name: 'additionalFields',
		type: 'collection',
		placeholder: 'Add Field',
		default: {},
		displayOptions: {
			show: {
				resource: [
					'email',
				],
				operation: [
					'send',
				],
			},
		},
		options: [
			{
				displayName: 'Bcc Email',
				name: 'bccEmail',
				type: 'string',
				description: 'Bcc Email address of the recipient. Multiple ones can be separated by comma.',
				default: '',
			},
			{
				displayName: 'Cc Addresses',
				name: 'ccAddresses',
				type: 'string',
				description: 'Cc Email address of the recipient. Multiple ones can be separated by comma.',
				default: '',
			},
			{
				displayName: 'From Name',
				name: 'fromName',
				type: 'string',
				default: '',
			},
			{
				displayName: 'Priority',
				name: 'priority',
				type: 'number',
				default: 2,
			},
			{
				displayName: 'Template Language',
				name: 'templateLanguage',
				type: 'boolean',
				default: true,
			},
			{
				displayName: 'Track Clicks',
				name: 'trackClicks',
				type: 'options',
				options: [
					{
						name: 'Account Default',
						value: 'account_default',
						description: 'Use the values specified in the Mailjet account',
					},
					{
						name: 'Disabled',
						value: 'disabled',
						description: 'Disable tracking for this message',
					},
					{
						name: 'Enabled',
						value: 'enabled',
						description: 'Enable tracking for this message',
					},
				],
				description: 'Enable or disable open tracking on this message.',
				default: 'account_default',
			},
			{
				displayName: 'Track Opens',
				name: 'trackOpens',
				type: 'options',
				options: [
					{
						name: 'Account Default',
						value: 'account_default',
						description: 'Use the values specified in the Mailjet account',
					},
					{
						name: 'Disabled',
						value: 'disabled',
						description: 'Disable tracking for this message',
					},
					{
						name: 'Enabled',
						value: 'enabled',
						description: 'Enable tracking for this message',
					},
				],
				description: 'Enable or disable open tracking on this message.',
				default: 'account_default',
			},
		],
	},
	{
		displayName: 'Variables',
		name: 'variablesUi',
		type: 'fixedCollection',
		typeOptions: {
			multipleValues: true,
		},
		displayOptions: {
			show: {
				resource: [
					'email',
				],
				operation: [
					'send',
				],
			},
		},
		placeholder: 'Add Variable',
		default: {},
		options: [
			{
				name: 'variablesValues',
				displayName: 'Variable',
				values: [
					{
						displayName: 'Name',
						name: 'name',
						type: 'string',
						default: '',
					},
					{
						displayName: 'Value',
						name: 'value',
						type: 'string',
						default: '',
					},
				],
			},
		],
	},
/* -------------------------------------------------------------------------- */
/*                                email:sendTemplate                          */
/* -------------------------------------------------------------------------- */
	{
		displayName: 'From Email',
		name: 'fromEmail',
		type: 'string',
		required: true,
		default: '',
		displayOptions: {
			show: {
				resource: [
					'email',
				],
				operation: [
					'sendTemplate',
				],
			},
		},
		placeholder: 'admin@example.com',
		description: 'The title for the email',
	},
	{
		displayName: 'To Email',
		name: 'toEmail',
		type: 'string',
		required: true,
		placeholder: 'info@example.com',
		description: 'Email address of the recipient. Multiple ones can be separated by comma.',
		displayOptions: {
			show: {
				resource: [
					'email',
				],
				operation: [
					'sendTemplate',
				],
			},
		},
	},
	{
		displayName: 'Template ID',
		name: 'templateId',
		type: 'string',
		required: true,
		default: '',
		displayOptions: {
			show: {
				resource: [
					'email',
				],
				operation: [
					'sendTemplate',
				],
			},
		},
	},
	{
		displayName: 'Additional Fields',
		name: 'additionalFields',
		type: 'collection',
		placeholder: 'Add Field',
		default: {},
		displayOptions: {
			show: {
				resource: [
					'email',
				],
				operation: [
					'sendTemplate',
				],
			},
		},
		options: [
			{
				displayName: 'Bcc Email',
				name: 'bccEmail',
				type: 'string',
				description: 'Bcc Recipients of the email separated by ,.',
				default: '',
			},
			{
				displayName: 'Cc Email',
				name: 'ccEmail',
				type: 'string',
				description: 'Cc recipients of the email separated by ,.',
				default: '',
			},
			{
				displayName: 'From Name',
				name: 'fromName',
				type: 'string',
				default: '',
			},
			{
				displayName: 'Priority',
				name: 'priority',
				type: 'number',
				default: 2,
			},
			{
				displayName: 'Subject',
				name: 'subject',
				type: 'string',
				default: '',
			},
			{
				displayName: 'Track Opens',
				name: 'trackOpens',
				type: 'string',
				description: 'Enable or disable open tracking on this message.',
				default: '',
			},
			{
				displayName: 'Track Clicks',
				name: 'trackClicks',
				type: 'string',
				description: 'Enable or disable open tracking on this message.',
				default: '',
			},
			{
				displayName: 'Template Language',
				name: 'templateLanguage',
				type: 'boolean',
				default: true,
			},
		],
	},
	{
		displayName: 'Variables',
		name: 'variablesUi',
		type: 'fixedCollection',
		typeOptions: {
			multipleValues: true,
		},
		displayOptions: {
			show: {
				resource: [
					'email',
				],
				operation: [
					'sendTemplate',
				],
			},
		},
		placeholder: 'Add Variable',
		default: {},
		options: [
			{
				name: 'variablesValues',
				displayName: 'Variable',
				values: [
					{
						displayName: 'Name',
						name: 'name',
						type: 'string',
						default: '',
					},
					{
						displayName: 'Value',
						name: 'value',
						type: 'string',
						default: '',
					},
				],
			},
		],
	},
] as INodeProperties[];


const endpoint = `/projects/${path}/hooks/${webhookData.webhookId}`;

await gitlabApiRequest.call(this, 'GET', endpoint, {});

import {
	IHookFunctions,
	IWebhookFunctions,
} from 'n8n-core';

import {
	IDataObject,
	ILoadOptionsFunctions,
	INodePropertyOptions,
	INodeType,
	INodeTypeDescription,
	IWebhookResponseData,
	NodeApiError,
	NodeOperationError,
} from 'n8n-workflow';

import {
	getresponseApiRequest,
	getResponseApiRequestAllItems,
} from './GenericFunctions';

export class GetResponseTrigger implements INodeType {
	description: INodeTypeDescription = {
		displayName: 'GetResponse Trigger',
		name: 'getResponseTrigger',
		icon: 'file:getResponse.png',
		group: ['trigger'],
		version: 1,
		description: 'Starts the workflow when GetResponse events occure.',
		defaults: {
			name: 'GetResponse Trigger',
			color: '#00afec',
		},
		inputs: [],
		outputs: ['main'],
		credentials: [
			{
				name: 'getResponseApi',
				required: true,
				displayOptions: {
					show: {
						authentication: [
							'apiKey',
						],
					},
				},
			},
			{
				name: 'getResponseOAuth2Api',
				required: true,
				displayOptions: {
					show: {
						authentication: [
							'oAuth2',
						],
					},
				},
			},
		],
		webhooks: [
			{
				name: 'default',
				httpMethod: 'GET',
				responseMode: 'onReceived',
				path: 'webhook',
			},
		],
		properties: [
			{
				displayName: 'Authentication',
				name: 'authentication',
				type: 'options',
				options: [
					{
						name: 'API Key',
						value: 'apiKey',
					},
					{
						name: 'OAuth2',
						value: 'oAuth2',
					},
				],
				default: 'apiKey',
				description: 'The resource to operate on.',
			},
			{
				displayName: 'Events',
				name: 'events',
				type: 'multiOptions',
				options: [
					{
						name: 'Customer Subscribed',
						value: 'subscribe',
						description: 'Receive notifications when a customer is subscribed to a list.',
					},
					{
						name: 'Customer Unsubscribed',
						value: 'unsubscribe',
						description: 'Receive notifications when a customer is unsubscribed from a list.',
					},
					{
						name: 'Email Opened',
						value: 'open',
						description: 'Receive notifications when a email is opened.',
					},
					{
						name: 'Email Clicked',
						value: 'click',
						description: 'Receive notifications when a email is clicked.',
					},
					{
						name: 'Survey Submitted',
						value: 'survey',
						description: 'Receive notifications when a survey is submitted.',
					},
				],
				default: [],
				required: true,
			},
			{
				displayName: 'List IDs',
				name: 'listIds',
				type: 'multiOptions',
				typeOptions: {
					loadOptionsMethod: 'getLists',
				},
				default: [],
			},
			{
				displayName: 'Options',
				name: 'options',
				placeholder: 'Add Option',
				type: 'collection',
				default: {},
				options: [
					{
						displayName: 'Delete Current Subscription',
						name: 'delete',
						type: 'boolean',
						default: false,
						description: 'Delete the current subscription.',
					},
				],
			},
		],
	};

	methods = {
		loadOptions: {
			// Get all the available teams to display them to user so that he can
			// select them easily
			async getLists(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]> {
				const returnData: INodePropertyOptions[] = [];
				const lists = await getResponseApiRequestAllItems.call(this, 'GET', '/campaigns');
				returnData.push({ name: '*', value: '*' });
				for (const list of lists) {
					returnData.push({
						name: list.name,
						value: list.campaignId,
					});
				}
				return returnData;
			},
		},
	};

	// @ts-ignore (because of request)
	webhookMethods = {
		default: {
			async checkExists(this: IHookFunctions): Promise<boolean> {
				const webhookUrl = this.getNodeWebhookUrl('default');
				const deleteCurrentSubscription = this.getNodeParameter('options.delete', false) as boolean;

				try {
					const data = await getresponseApiRequest.call(this, 'GET', '/accounts/callbacks', {});

					if (data.url !== webhookUrl) {
						if (deleteCurrentSubscription === false) {
							throw new NodeApiError(this.getNode(), data, { message: `The webhook (${data.url}) is active in the account. Delete it manually or set the parameter "Delete Current Subscription" to true, and the node will delete it for you.` });
						}
					}
				} catch (error) {
					if (error.message.includes('[404]')) {
						return false;
					}
				}

				await getresponseApiRequest.call(this, 'DELETE', '/accounts/callbacks');

				return false;
			},
			async create(this: IHookFunctions): Promise<boolean> {
				const webhookUrl = this.getNodeWebhookUrl('default');
				const events = this.getNodeParameter('events') as string[];

				const body = {
					url: webhookUrl,
					actions: events.reduce((accumulator: { [key: string]: boolean }, currentValue: string) => {
						accumulator[currentValue] = true;
						return accumulator;
					}, {}),
				};

				await getresponseApiRequest.call(this, 'POST', '/accounts/callbacks', body);

				return true;
			},
			async delete(this: IHookFunctions): Promise<boolean> {
				try {
					await getresponseApiRequest.call(this, 'DELETE', '/accounts/callbacks');
				} catch (error) {
					return false;
				}

				return true;
			},
		},
	};

	async webhook(this: IWebhookFunctions): Promise<IWebhookResponseData> {
		const query = this.getQueryData() as IDataObject;
		const listIds = this.getNodeParameter('listIds') as string[];

		if (!listIds.includes('*') && !listIds.includes(query['CAMPAIGN_ID'] as string)) {
			return {};
		}

		return {
			workflowData: [
				this.helpers.returnJsonArray(query),
			],
		};
	}
}


import {
	OptionsWithUri,
 } from 'request';

import {
	IExecuteFunctions,
	IExecuteSingleFunctions,
	ILoadOptionsFunctions,
} from 'n8n-core';

import {
	IDataObject, NodeApiError,
} from 'n8n-workflow';

export async function xeroApiRequest(this: IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, method: string, resource: string, body: any = {}, qs: IDataObject = {}, uri?: string, headers: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
	const options: OptionsWithUri = {
		headers: {
			'Content-Type': 'application/json',
		},
		method,
		body,
		qs,
		uri: uri || `https://api.xero.com/api.xro/2.0${resource}`,
		json: true,
	};
	try {
		if (body.organizationId) {
			options.headers = { ...options.headers, 'Xero-tenant-id': body.organizationId };
			delete body.organizationId;
		}
		if (Object.keys(headers).length !== 0) {
			options.headers = Object.assign({}, options.headers, headers);
		}
		if (Object.keys(body).length === 0) {
			delete options.body;
		}
		//@ts-ignore
		return await this.helpers.requestOAuth2.call(this, 'xeroOAuth2Api', options);
	} catch (error) {
		throw new NodeApiError(this.getNode(), error);
	}
}

export async function xeroApiRequestAllItems(this: IExecuteFunctions | ILoadOptionsFunctions, propertyName: string ,method: string, endpoint: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any

	const returnData: IDataObject[] = [];

	let responseData;
	query.page = 1;

	do {
		responseData = await xeroApiRequest.call(this, method, endpoint, body, query);
		query.page++;
		returnData.push.apply(returnData, responseData[propertyName]);
	} while (
		responseData[propertyName].length !== 0
	);

	return returnData;
}


import {
	IConnections,
	IDataObject,
	INode,
	IWorkflowSettings,
} from 'n8n-workflow';

import {
	IWorkflowDb,
} from '../../';

import {
	Column,
	Entity,
	PrimaryGeneratedColumn,
} from 'typeorm';

@Entity()
export class WorkflowEntity implements IWorkflowDb {

	@PrimaryGeneratedColumn()
	id: number;

	@Column({
		length: 128,
	})
	name: string;

	@Column()
	active: boolean;

	@Column('simple-json')
	nodes: INode[];

	@Column('simple-json')
	connections: IConnections;

	@Column()
	createdAt: Date;

	@Column()
	updatedAt: Date;

	@Column({
		type: 'simple-json',
		nullable: true,
	})
	settings?: IWorkflowSettings;

	@Column({
		type: 'simple-json',
		nullable: true,
	})
	staticData?: IDataObject;
}


import { useDebouncedValue, useMutableCallback } from '@rocket.chat/fuselage-hooks';

import { Pagination } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { useRoute, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts';
import { useQuery } from '@tanstack/react-query';
import type { MutableRefObject } from 'react';
import React, { useMemo, useState, useEffect } from 'react';

import FilterByText from '../../../components/FilterByText';
import GenericNoResults from '../../../components/GenericNoResults';
import {
	GenericTable,
	GenericTableHeader,
	GenericTableHeaderCell,
	GenericTableRow,
	GenericTableCell,
	GenericTableBody,
	GenericTableLoadingTable,
} from '../../../components/GenericTable';
import { usePagination } from '../../../components/GenericTable/hooks/usePagination';
import { useSort } from '../../../components/GenericTable/hooks/useSort';
import RemoveCustomFieldButton from './RemoveCustomFieldButton';

const CustomFieldsTable = ({ reload }: { reload: MutableRefObject<() => void> }) => {
	const t = useTranslation();
	const [filter, setFilter] = useState('');
	const departmentsRoute = useRoute('omnichannel-customfields');

	const { current, itemsPerPage, setItemsPerPage: onSetItemsPerPage, setCurrent: onSetCurrent, ...paginationProps } = usePagination();
	const { sortBy, sortDirection, setSort } = useSort<'_id' | 'label' | 'scope' | 'visibility'>('_id');

	const query = useMemo(
		() => ({
			text: filter,
			sort: `{ "${sortBy}": ${sortDirection === 'asc' ? 1 : -1} }`,
			...(itemsPerPage && { count: itemsPerPage }),
			...(current && { offset: current }),
		}),
		[filter, itemsPerPage, current, sortBy, sortDirection],
	);

	const getCustomFields = useEndpoint('GET', '/v1/livechat/custom-fields');
	const { data, isSuccess, isLoading, refetch } = useQuery(['livechat-customFields', query], async () => getCustomFields(query));

	useEffect(() => {
		reload.current = refetch;
	}, [reload, refetch]);
	reload.current = refetch;

	const headers = (
		<>
			<GenericTableHeaderCell key='field' direction={sortDirection} active={sortBy === '_id'} onClick={setSort} sort='_id'>
				{t('Field')}
			</GenericTableHeaderCell>
			<GenericTableHeaderCell key='label' direction={sortDirection} active={sortBy === 'label'} onClick={setSort} sort='label'>
				{t('Label')}
			</GenericTableHeaderCell>
			<GenericTableHeaderCell key='scope' direction={sortDirection} active={sortBy === 'scope'} onClick={setSort} sort='scope'>
				{t('Scope')}
			</GenericTableHeaderCell>
			<GenericTableHeaderCell
				key='visibility'
				direction={sortDirection}
				active={sortBy === 'visibility'}
				onClick={setSort}
				sort='visibility'
			>
				{t('Visibility')}
			</GenericTableHeaderCell>
			<GenericTableHeaderCell key='remove' w='x60'>
				{t('Remove')}
			</GenericTableHeaderCell>
		</>
	);

	const onRowClick = useMutableCallback(
		(id) => () =>
			departmentsRoute.push({
				context: 'edit',
				id,
			}),
	);

	return (
		<>
			<FilterByText onChange={({ text }) => setFilter(text)} />
			{isLoading && (
				<GenericTable>
					<GenericTableHeader>{headers}</GenericTableHeader>
					<GenericTableBody>
						<GenericTableLoadingTable headerCells={5} />
					</GenericTableBody>
				</GenericTable>
			)}
			{isSuccess && data.customFields.length === 0 && <GenericNoResults />}
			{isSuccess && data.customFields.length > 0 && (
				<>
					<GenericTable data-qa='GenericTableCustomFieldsInfoBody'>
						<GenericTableHeader>{headers}</GenericTableHeader>
						<GenericTableBody>
							{data.customFields.map(({ label, _id, scope, visibility }) => (
								<GenericTableRow key={_id} tabIndex={0} role='link' onClick={onRowClick(_id)} action qa-user-id={_id}>
									<GenericTableCell withTruncatedText>{_id}</GenericTableCell>
									<GenericTableCell withTruncatedText>{label}</GenericTableCell>
									<GenericTableCell withTruncatedText>{scope === 'visitor' ? t('Visitor') : t('Room')}</GenericTableCell>
									<GenericTableCell withTruncatedText>{visibility === 'visible' ? t('Visible') : t('Hidden')}</GenericTableCell>
									<RemoveCustomFieldButton _id={_id} reload={refetch} />
								</GenericTableRow>
							))}
						</GenericTableBody>
					</GenericTable>
					<Pagination
						divider
						current={current}
						itemsPerPage={itemsPerPage}
						count={data.total || 0}
						onSetItemsPerPage={onSetItemsPerPage}
						onSetCurrent={onSetCurrent}
						{...paginationProps}
					/>
				</>
			)}
		</>
	);
};

export default CustomFieldsTable;


import { useMemo } from 'react';
import type { CustomFieldMetadata } from '@rocket.chat/core-typings';

import { useSetting } from './useSetting';

export const useAccountsCustomFields = (): CustomFieldMetadata[] => {
	const accountsCustomFieldsJSON = useSetting('Accounts_CustomFields');

	return useMemo(() => {
		if (typeof accountsCustomFieldsJSON !== 'string' || accountsCustomFieldsJSON.trim() === '') {
			return [];
		}
		try {
			return Object.entries(JSON.parse(accountsCustomFieldsJSON)).map(([fieldName, fieldData]) => {
				return { ...(fieldData as any), name: fieldName };
			});
		} catch {
			console.error('Invalid JSON for Accounts_CustomFields');
		}
		return [];
	}, [accountsCustomFieldsJSON]);
};


import { ImporterInfo } from '../../importer/lib/ImporterInfo';

export class PendingFileImporterInfo extends ImporterInfo {
	constructor() {
		super('pending-files', 'Pending Files', '');
	}
}


import type { ServerMethods } from '@rocket.chat/ui-contexts';
import { Meteor } from 'meteor/meteor';

import { generateUsernameSuggestion } from '../functions';

declare module '@rocket.chat/ui-contexts' {
	// eslint-disable-next-line @typescript-eslint/naming-convention
	interface ServerMethods {
		getUsernameSuggestion(): Promise<string | undefined>;
	}
}

Meteor.methods<ServerMethods>({
	async getUsernameSuggestion() {
		const user = await Meteor.userAsync();

		if (!user) {
			throw new Meteor.Error('error-invalid-user', 'Invalid user', {
				method: 'getUsernameSuggestion',
			});
		}

		return generateUsernameSuggestion(user);
	},
});


const CustomFieldsTable = ({ reload }: { reload: MutableRefObject<() => void> }) => {

const [filter, setFilter] = useState('');

const departmentsRoute = useRoute('omnichannel-customfields');

const { current, itemsPerPage, setItemsPerPage: onSetItemsPerPage, setCurrent: onSetCurrent, ...paginationProps } = usePagination();

const { sortBy, sortDirection, setSort } = useSort<'_id' | 'label' | 'scope' | 'visibility'>('_id');

const debouncedFilter = useDebouncedValue(filter, 500);


{
	"extends": "../../../tsconfig.base.server.json",
	"compilerOptions": {
		"outDir": "./dist",
		"declaration": true,
	},
	"include": ["./src/**/*", "../../../apps/meteor/definition/externals/meteor"],
	"exclude": ["./dist"]
}


import { PaginatedSelectFiltered } from '@rocket.chat/fuselage';
import { useDebouncedValue } from '@rocket.chat/fuselage-hooks';
import type { ReactElement } from 'react';
import React, { memo, useMemo, useState } from 'react';

import { useRecordList } from '../hooks/lists/useRecordList';
import { AsyncStatePhase } from '../lib/asyncState';
import { useAgentsList } from './Omnichannel/hooks/useAgentsList';

type AutoCompleteAgentProps = {
	value: string;
	error?: string;
	placeholder?: string;
	onChange: (value: string) => void;
	haveAll?: boolean;
	haveNoAgentsSelectedOption?: boolean;
};

const AutoCompleteAgent = ({
	value,
	error,
	placeholder,
	onChange,
	haveAll = false,
	haveNoAgentsSelectedOption = false,
}: AutoCompleteAgentProps): ReactElement => {
	const [agentsFilter, setAgentsFilter] = useState<string>('');

	const debouncedAgentsFilter = useDebouncedValue(agentsFilter, 500);

	const { itemsList: AgentsList, loadMoreItems: loadMoreAgents } = useAgentsList(
		useMemo(
			() => ({ text: debouncedAgentsFilter, haveAll, haveNoAgentsSelectedOption }),
			[debouncedAgentsFilter, haveAll, haveNoAgentsSelectedOption],
		),
	);

	const { phase: agentsPhase, itemCount: agentsTotal, items: agentsItems } = useRecordList(AgentsList);

	const sortedByName = agentsItems.sort((a, b) => {
		if (a.label > b.label) {
			return 1;
		}
		if (a.label < b.label) {
			return -1;
		}

		return 0;
	});

	return (
		<PaginatedSelectFiltered
			value={value}
			error={error}
			placeholder={placeholder}
			onChange={onChange}
			flexShrink={0}
			filter={agentsFilter}
			setFilter={setAgentsFilter as (value: string | number | undefined) => void}
			options={sortedByName}
			data-qa='autocomplete-agent'
			endReached={
				agentsPhase === AsyncStatePhase.LOADING ? (): void => undefined : (start): void => loadMoreAgents(start, Math.min(50, agentsTotal))
			}
		/>
	);
};

export default memo(AutoCompleteAgent);


import { QueryClientProvider } from '@tanstack/react-query';
import type { ReactElement } from 'react';
import React, { lazy, Suspense } from 'react';
import { createPortal } from 'react-dom';

import { queryClient } from '../../lib/queryClient';
import OutermostErrorBoundary from './OutermostErrorBoundary';
import PageLoading from './PageLoading';

const MeteorProvider = lazy(() => import('../../providers/MeteorProvider'));
const AppLayout = lazy(() => import('./AppLayout'));

const AppRoot = (): ReactElement => (
	<OutermostErrorBoundary>
		{createPortal(
			<>
				<meta charSet='utf-8' />
				<meta httpEquiv='content-type' content='text/html; charset=utf-8' />
				<meta httpEquiv='expires' content='-1' />
				<meta httpEquiv='X-UA-Compatible' content='IE=edge' />
				<meta name='fragment' content='!' />
				<meta name='distribution' content='global' />
				<meta
					name='viewport'
					content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, interactive-widget=resizes-content'
				/>
				<meta name='rating' content='general' />
				<meta name='mobile-web-app-capable' content='yes' />
				<meta name='apple-mobile-web-app-capable' content='yes' />
				<meta name='msapplication-TileImage' content='assets/tile_144.png' />
				<meta name='msapplication-config' content='images/browserconfig.xml' />
				<meta property='og:image' content='assets/favicon_512.png' />
				<meta property='twitter:image' content='assets/favicon_512.png' />
				<link rel='manifest' href='images/manifest.json' />
				<link rel='chrome-webstore-item' href='https://chrome.google.com/webstore/detail/nocfbnnmjnndkbipkabodnheejiegccf' />
				<link rel='mask-icon' href='assets/safari_pinned.svg' color='#04436a' />
				<link rel='apple-touch-icon' sizes='180x180' href='assets/touchicon_180.png' />
				<link rel='apple-touch-icon-precomposed' href='assets/touchicon_180_pre.png' />
			</>,
			document.head,
		)}
		<Suspense fallback={<PageLoading />}>
			<QueryClientProvider client={queryClient}>
				<MeteorProvider>
					<AppLayout />
				</MeteorProvider>
			</QueryClientProvider>
		</Suspense>
	</OutermostErrorBoundary>
);

export default AppRoot;


sort: `{ "${sortBy}": ${sortDirection === 'asc' ? 1 : -1} }`,

...(itemsPerPage && { count: itemsPerPage }),

keep keep keep keep replace keep keep keep keep


# @rocket.chat/models

## 0.0.9

### Patch Changes

- @rocket.chat/model-typings@0.0.9

## 0.0.8

### Patch Changes

- @rocket.chat/model-typings@0.0.8

## 0.0.7

### Patch Changes

- @rocket.chat/model-typings@0.0.7

## 0.0.6

### Patch Changes

- Updated dependencies [7832a40a6d]
- Updated dependencies [b837cb9f2a]
- Updated dependencies [ee5993625b]
- Updated dependencies [9da856cc67]
- Updated dependencies [0f0b8e17bf]
- Updated dependencies [c31f93ed96]
- Updated dependencies [b837cb9f2a]
- Updated dependencies [916c0dcaf2]
- Updated dependencies [94477bd9f8]
- Updated dependencies [16dca466ea]
  - @rocket.chat/model-typings@0.0.6

## 0.0.6-rc.10

### Patch Changes

- @rocket.chat/model-typings@0.0.6-rc.10

## 0.0.6-rc.9

### Patch Changes

- @rocket.chat/model-typings@0.0.6-rc.9

## 0.0.6-rc.8

### Patch Changes

- @rocket.chat/model-typings@0.0.6-rc.8

## 0.0.6-rc.7

### Patch Changes

- @rocket.chat/model-typings@0.0.6-rc.7

## 0.0.6-rc.6

### Patch Changes

- @rocket.chat/model-typings@0.0.6-rc.6

## 0.0.6-rc.5

### Patch Changes

- @rocket.chat/model-typings@0.0.6-rc.5

## 0.0.6-rc.4

### Patch Changes

- @rocket.chat/model-typings@0.0.6-rc.4

## 0.0.6-rc.3

### Patch Changes

- @rocket.chat/model-typings@0.0.6-rc.3

## 0.0.6-rc.2

### Patch Changes

- @rocket.chat/model-typings@0.0.6-rc.2

## 0.0.6-rc.1

### Patch Changes

- @rocket.chat/model-typings@0.0.6-rc.1

## 0.0.5

### Patch Changes

- @rocket.chat/model-typings@0.0.5

## 0.0.4

## 0.0.3-rc.0

### Patch Changes

- Updated dependencies [7832a40a6d]
- Updated dependencies [b837cb9f2a]
- Updated dependencies [ee5993625b]
- Updated dependencies [9da856cc67]
- Updated dependencies [0f0b8e17bf]
- Updated dependencies [c31f93ed96]
- Updated dependencies [b837cb9f2a]
- Updated dependencies [916c0dcaf2]
- Updated dependencies [94477bd9f8]
- Updated dependencies [16dca466ea]
  - @rocket.chat/model-typings@0.0.3-rc.0

## 0.0.2

### Patch Changes

- Updated dependencies []:
  - @rocket.chat/model-typings@0.0.2


import { memo, useCallback } from 'preact/compat';

import KebabIcon from '../../../../icons/kebab.svg';
import { Button } from '../../../Button';
import Menu, { PopoverMenu } from '../../../Menu';
import { createClassName } from '../../../helpers';
import { usePerformAction } from '../Block';
import styles from './styles.scss';

const OverflowTrigger = ({ loading, onClick }) => {
	const handleMouseUp = useCallback(({ target }) => {
		target.blur();
	}, []);

	return (
		<Button
			className={createClassName(styles, 'uikit-overflow__trigger')}
			disabled={loading}
			outline
			secondary
			onClick={onClick}
			onMouseUp={handleMouseUp}
		>
			<KebabIcon width={20} height={20} />
		</Button>
	);
};

const OverflowOption = ({ confirm, text, value, url, parser, onClick }) => {
	const handleClick = useCallback(
		async (event) => {
			event.preventDefault();

			if (confirm) {
				// TODO
			}

			if (url) {
				const newTab = window.open();
				newTab.opener = null;
				newTab.location = url;
				return;
			}

			await onClick(value);
		},
		[confirm, onClick, url, value],
	);

	return <Menu.Item onClick={handleClick}>{parser.text(text)}</Menu.Item>;
};

const OverflowElement = ({ actionId, confirm, options, parser }) => {
	const [performAction, performingAction] = usePerformAction(actionId);

	const handleClick = useCallback(
		async (value) => {
			await performAction({ value });
		},
		[performAction],
	);

	return (
		<PopoverMenu trigger={({ pop }) => <OverflowTrigger loading={performingAction} onClick={pop} />}>
			<Menu.Group>
				{Array.isArray(options) &&
					options.map((option, i) => <OverflowOption key={i} {...option} confirm={confirm} parser={parser} onClick={handleClick} />)}
			</Menu.Group>
		</PopoverMenu>
	);
};

export default memo(OverflowElement);


import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import { DDPRateLimiter } from 'meteor/ddp-rate-limiter';
import type { ServerMethods } from '@rocket.chat/ui-contexts';
import { Users } from '@rocket.chat/models';

import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission';
import { Livechat } from '../lib/LivechatTyped';

declare module '@rocket.chat/ui-contexts' {
	// eslint-disable-next-line @typescript-eslint/naming-convention
	interface ServerMethods {
		'livechat:sendTranscript'(token: string, rid: string, email: string, subject: string): boolean;
	}
}

Meteor.methods<ServerMethods>({
	async 'livechat:sendTranscript'(token, rid, email, subject) {
		check(rid, String);
		check(email, String);

		const uid = Meteor.userId();
		if (!uid || !(await hasPermissionAsync(uid, 'send-omnichannel-chat-transcript'))) {
			throw new Meteor.Error('error-not-allowed', 'Not allowed', {
				method: 'livechat:sendTranscript',
			});
		}

		const user = await Users.findOneById(uid, {
			projection: { _id: 1, username: 1, name: 1, utcOffset: 1 },
		});
		return Livechat.sendTranscript({ token, rid, email, subject, user });
	},
});

DDPRateLimiter.addRule(
	{
		type: 'method',
		name: 'livechat:sendTranscript',
		connectionId() {
			return true;
		},
	},
	1,
	5000,
);


const getCustomFields = useEndpoint('GET', '/v1/livechat/custom-fields');

const { data, isSuccess, isLoading, refetch } = useQuery(['livechat-customFields', query], async () => getCustomFields(query));

keep keep keep keep replace keep keep keep keep keep

[debouncedFilter, itemsPerPage, current, sortBy, sortDirection],

import { Pagination } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { useEndpoint, useRoute, useTranslation } from '@rocket.chat/ui-contexts';
import { useQuery } from '@tanstack/react-query';
import type { MutableRefObject } from 'react';
import React, { useMemo, useState, useEffect } from 'react';

import FilterByText from '../../../../client/components/FilterByText';
import GenericNoResults from '../../../../client/components/GenericNoResults/GenericNoResults';
import {
	GenericTable,
	GenericTableHeader,
	GenericTableHeaderCell,
	GenericTableBody,
	GenericTableCell,
	GenericTableLoadingRow,
	GenericTableRow,
} from '../../../../client/components/GenericTable';
import { usePagination } from '../../../../client/components/GenericTable/hooks/usePagination';
import { useSort } from '../../../../client/components/GenericTable/hooks/useSort';
import RemoveUnitButton from './RemoveUnitButton';

const UnitsTable = ({ reload }: { reload: MutableRefObject<() => void> }) => {
	const t = useTranslation();
	const [filter, setFilter] = useState('');
	const unitsRoute = useRoute('omnichannel-units');

	const { current, itemsPerPage, setItemsPerPage: onSetItemsPerPage, setCurrent: onSetCurrent, ...paginationProps } = usePagination();
	const { sortBy, sortDirection, setSort } = useSort<'name' | 'visibility'>('name');

	const query = useMemo(
		() => ({
			fields: JSON.stringify({ name: 1 }),
			text: filter,
			sort: JSON.stringify({ [sortBy]: sortDirection === 'asc' ? 1 : -1 }),
			...(itemsPerPage && { count: itemsPerPage }),
			...(current && { offset: current }),
		}),
		[filter, itemsPerPage, current, sortBy, sortDirection],
	);

	const getUnits = useEndpoint('GET', '/v1/livechat/units');
	const { isSuccess, isLoading, data, refetch } = useQuery(['livechat-units', query], async () => getUnits(query));

	useEffect(() => {
		reload.current = refetch;
	}, [refetch, reload]);

	const onRowClick = useMutableCallback(
		(id) => () =>
			unitsRoute.push({
				context: 'edit',
				id,
			}),
	);
	const headers = (
		<>
			<GenericTableHeaderCell key={'name'} direction={sortDirection} active={sortBy === 'name'} onClick={setSort} sort='name'>
				{t('Name')}
			</GenericTableHeaderCell>
			<GenericTableHeaderCell
				key={'visibility'}
				direction={sortDirection}
				active={sortBy === 'visibility'}
				onClick={setSort}
				sort='visibility'
			>
				{t('Visibility')}
			</GenericTableHeaderCell>
			<GenericTableHeaderCell key={'remove'} w='x60'>
				{t('Remove')}
			</GenericTableHeaderCell>
		</>
	);

	return (
		<>
			<FilterByText onChange={({ text }): void => setFilter(text)} />
			{isLoading && (
				<GenericTable>
					<GenericTableHeader>{headers}</GenericTableHeader>
					<GenericTableBody>
						<GenericTableLoadingRow cols={3} />
					</GenericTableBody>
				</GenericTable>
			)}
			{isSuccess && data.units.length === 0 && <GenericNoResults />}
			{isSuccess && data?.units.length > 0 && (
				<>
					<GenericTable>
						<GenericTableHeader>{headers}</GenericTableHeader>
						<GenericTableBody>
							{data.units.map(({ _id, name, visibility }) => (
								<GenericTableRow key={_id} tabIndex={0} role='link' onClick={onRowClick(_id)} action qa-user-id={_id}>
									<GenericTableCell withTruncatedText>{name}</GenericTableCell>
									<GenericTableCell withTruncatedText>{visibility}</GenericTableCell>
									<RemoveUnitButton _id={_id} reload={refetch} />
								</GenericTableRow>
							))}
						</GenericTableBody>
					</GenericTable>
					<Pagination
						divider
						current={current}
						itemsPerPage={itemsPerPage}
						count={data?.total || 0}
						onSetItemsPerPage={onSetItemsPerPage}
						onSetCurrent={onSetCurrent}
						{...paginationProps}
					/>
				</>
			)}
		</>
	);
};

export default UnitsTable;


import { registerModel } from '@rocket.chat/models';

import { db } from '../database/utils';
import { OEmbedCacheRaw } from './raw/OEmbedCache';

registerModel('IOEmbedCacheModel', new OEmbedCacheRaw(db));


import { Divider } from '@rocket.chat/fuselage';
import type { ComponentMeta, ComponentStory } from '@storybook/react';
import React, { useRef } from 'react';

import TextEditor from '.';

export default {
	title: 'Enterprise/Omnichannel/TextEditor',
	component: TextEditor,
} as ComponentMeta<typeof TextEditor>;

export const Default: ComponentStory<typeof TextEditor> = () => {
	// eslint-disable-next-line react-hooks/rules-of-hooks
	const textAreaRef = useRef<HTMLTextAreaElement>(null);

	const action = () => {
		if (!textAreaRef.current) {
			return;
		}

		const text = textAreaRef.current.value;
		const startPos = textAreaRef.current.selectionStart;
		const endPos = textAreaRef.current.selectionEnd;
		textAreaRef.current.value = `${text.slice(0, startPos)}*${text.slice(startPos, endPos)}*${text.slice(endPos)}`;
		textAreaRef.current.focus();
		textAreaRef.current.setSelectionRange(startPos + 1, endPos + 1);
	};

	return (
		<TextEditor>
			<TextEditor.Toolbox>
				<TextEditor.Toolbox.IconButton name='bold' action={action} />
				<TextEditor.Toolbox.TextButton text='Insert_Placeholder' action={action} />
			</TextEditor.Toolbox>
			<Divider w='full' mbe='16px' />
			<TextEditor.Textarea rows={10} ref={textAreaRef} />
		</TextEditor>
	);
};
Default.storyName = 'TextEditor';


export const actionWithSingleLineInput = [
  {
    type: 'actions',
    elements: [
      {
        type: 'plain_text_input',
        actionId: 'plain_text_input_1',
        placeholder: {
          type: 'plain_text',
          text: 'Enter name',
          emoji: true,
        },
        initialValue: 'Vivek Srivastava',
        multiline: false,
      },
    ],
  },
];

export const actionWithMultiLineInput = [
  {
    type: 'actions',
    elements: [
      {
        type: 'plain_text_input',
        actionId: 'plain_text_input_1',
        placeholder: {
          type: 'plain_text',
          text: 'Enter name',
          emoji: true,
        },
        initialValue: 'Vivek Srivastava',
        multiline: true,
      },
    ],
  },
];


import { useEndpoint, useRoute, useTranslation } from '@rocket.chat/ui-contexts';

import React, { useMemo, useState, useEffect } from 'react';


import { Pagination } from '@rocket.chat/fuselage';

import { useMutableCallback } from '@rocket.chat/fuselage-hooks';

import { Meteor } from 'meteor/meteor';
import { Match, check } from 'meteor/check';
import type { ServerMethods } from '@rocket.chat/ui-contexts';
import { Subscriptions, Users } from '@rocket.chat/models';

type UserPreferences = {
	language: string;
	newRoomNotification: string;
	newMessageNotification: string;
	clockMode: number;
	useEmojis: boolean;
	convertAsciiEmoji: boolean;
	saveMobileBandwidth: boolean;
	collapseMediaByDefault: boolean;
	autoImageLoad: boolean;
	emailNotificationMode: string;
	unreadAlert: boolean;
	notificationsSoundVolume: number;
	desktopNotifications: string;
	pushNotifications: string;
	enableAutoAway: boolean;
	highlights: string[];
	hideUsernames: boolean;
	hideRoles: boolean;
	displayAvatars: boolean;
	hideFlexTab: boolean;
	sendOnEnter: string;
	idleTimeLimit: number;
	sidebarShowFavorites: boolean;
	sidebarShowUnread: boolean;
	sidebarSortby: string;
	sidebarViewMode: string;
	sidebarDisplayAvatar: boolean;
	sidebarGroupByType: boolean;
	muteFocusedConversations: boolean;
	dontAskAgainList: { action: string; label: string }[];
	themeAppearence: 'auto' | 'light' | 'dark';
	receiveLoginDetectionEmail: boolean;
	notifyCalendarEvents: boolean;
};

declare module '@rocket.chat/ui-contexts' {
	// eslint-disable-next-line @typescript-eslint/naming-convention
	interface ServerMethods {
		saveUserPreferences(preferences: Partial<UserPreferences>): boolean;
	}
}

export const saveUserPreferences = async (settings: Partial<UserPreferences>, userId: string): Promise<void> => {
	const keys = {
		language: Match.Optional(String),
		newRoomNotification: Match.Optional(String),
		newMessageNotification: Match.Optional(String),
		clockMode: Match.Optional(Number),
		useEmojis: Match.Optional(Boolean),
		convertAsciiEmoji: Match.Optional(Boolean),
		saveMobileBandwidth: Match.Optional(Boolean),
		collapseMediaByDefault: Match.Optional(Boolean),
		autoImageLoad: Match.Optional(Boolean),
		emailNotificationMode: Match.Optional(String),
		unreadAlert: Match.Optional(Boolean),
		notificationsSoundVolume: Match.Optional(Number),
		desktopNotifications: Match.Optional(String),
		pushNotifications: Match.Optional(String),
		enableAutoAway: Match.Optional(Boolean),
		highlights: Match.Optional([String]),
		hideUsernames: Match.Optional(Boolean),
		hideRoles: Match.Optional(Boolean),
		displayAvatars: Match.Optional(Boolean),
		hideFlexTab: Match.Optional(Boolean),
		sendOnEnter: Match.Optional(String),
		idleTimeLimit: Match.Optional(Number),
		sidebarShowFavorites: Match.Optional(Boolean),
		sidebarShowUnread: Match.Optional(Boolean),
		sidebarSortby: Match.Optional(String),
		sidebarViewMode: Match.Optional(String),
		sidebarDisplayAvatar: Match.Optional(Boolean),
		sidebarGroupByType: Match.Optional(Boolean),
		muteFocusedConversations: Match.Optional(Boolean),
		omnichannelTranscriptEmail: Match.Optional(Boolean),
		omnichannelTranscriptPDF: Match.Optional(Boolean),
		notifyCalendarEvents: Match.Optional(Boolean),
	};
	check(settings, Match.ObjectIncluding(keys));
	const user = await Users.findOneById(userId);

	if (!user) {
		return;
	}

	const {
		desktopNotifications: oldDesktopNotifications,
		pushNotifications: oldMobileNotifications,
		emailNotificationMode: oldEmailNotifications,
	} = user.settings?.preferences || {};

	if (user.settings == null) {
		await Users.clearSettings(user._id);
	}

	if (settings.language != null) {
		await Users.setLanguage(user._id, settings.language);
	}

	// Keep compatibility with old values
	if (settings.emailNotificationMode === 'all') {
		settings.emailNotificationMode = 'mentions';
	} else if (settings.emailNotificationMode === 'disabled') {
		settings.emailNotificationMode = 'nothing';
	}

	if (settings.idleTimeLimit != null && settings.idleTimeLimit < 60) {
		throw new Meteor.Error('invalid-idle-time-limit-value', 'Invalid idleTimeLimit');
	}

	await Users.setPreferences(user._id, settings);

	// propagate changed notification preferences
	setImmediate(async () => {
		if (settings.desktopNotifications && oldDesktopNotifications !== settings.desktopNotifications) {
			if (settings.desktopNotifications === 'default') {
				await Subscriptions.clearNotificationUserPreferences(user._id, 'desktopNotifications', 'desktopPrefOrigin');
			} else {
				await Subscriptions.updateNotificationUserPreferences(
					user._id,
					settings.desktopNotifications,
					'desktopNotifications',
					'desktopPrefOrigin',
				);
			}
		}

		if (settings.pushNotifications && oldMobileNotifications !== settings.pushNotifications) {
			if (settings.pushNotifications === 'default') {
				await Subscriptions.clearNotificationUserPreferences(user._id, 'mobilePushNotifications', 'mobilePrefOrigin');
			} else {
				await Subscriptions.updateNotificationUserPreferences(
					user._id,
					settings.pushNotifications,
					'mobilePushNotifications',
					'mobilePrefOrigin',
				);
			}
		}

		if (settings.emailNotificationMode && oldEmailNotifications !== settings.emailNotificationMode) {
			if (settings.emailNotificationMode === 'default') {
				await Subscriptions.clearNotificationUserPreferences(user._id, 'emailNotifications', 'emailPrefOrigin');
			} else {
				await Subscriptions.updateNotificationUserPreferences(
					user._id,
					settings.emailNotificationMode,
					'emailNotifications',
					'emailPrefOrigin',
				);
			}
		}

		if (Array.isArray(settings.highlights)) {
			await Subscriptions.updateUserHighlights(user._id, settings.highlights);
		}
	});
};

Meteor.methods<ServerMethods>({
	async saveUserPreferences(settings) {
		const userId = Meteor.userId();
		if (!userId) {
			throw new Meteor.Error('error-invalid-user', 'Invalid user', { method: 'saveUserPreferences' });
		}

		await saveUserPreferences(settings, userId);

		return true;
	},
});


---
'@rocket.chat/meteor': patch
---

Bump @rocket.chat/meteor version.


export { registerMarketplaceRoute } from './routes';
export { registerMarketplaceSidebarItem, unregisterMarketplaceSidebarItem } from './sidebarItems';


const UnitsTable = ({ reload }: { reload: MutableRefObject<() => void> }) => {

const unitsRoute = useRoute('omnichannel-units');

const { sortBy, sortDirection, setSort } = useSort<'name' | 'visibility'>('name');

keep keep keep add keep keep keep keep keep

import { Pagination } from '@rocket.chat/fuselage';
import { useMutableCallback } from '@rocket.chat/fuselage-hooks';
import { useRoute, useTranslation, useEndpoint } from '@rocket.chat/ui-contexts';
import { useQuery } from '@tanstack/react-query';
import type { MutableRefObject } from 'react';
import React, { useMemo, useState, useEffect } from 'react';

import FilterByText from '../../../components/FilterByText';
import GenericNoResults from '../../../components/GenericNoResults';
import {
	GenericTable,
	GenericTableHeader,
	GenericTableHeaderCell,
	GenericTableRow,
	GenericTableCell,
	GenericTableBody,
	GenericTableLoadingTable,
} from '../../../components/GenericTable';
import { usePagination } from '../../../components/GenericTable/hooks/usePagination';
import { useSort } from '../../../components/GenericTable/hooks/useSort';
import RemoveCustomFieldButton from './RemoveCustomFieldButton';

const CustomFieldsTable = ({ reload }: { reload: MutableRefObject<() => void> }) => {
	const t = useTranslation();
	const [filter, setFilter] = useState('');
	const departmentsRoute = useRoute('omnichannel-customfields');

	const { current, itemsPerPage, setItemsPerPage: onSetItemsPerPage, setCurrent: onSetCurrent, ...paginationProps } = usePagination();
	const { sortBy, sortDirection, setSort } = useSort<'_id' | 'label' | 'scope' | 'visibility'>('_id');

	const query = useMemo(
		() => ({
			text: filter,
			sort: `{ "${sortBy}": ${sortDirection === 'asc' ? 1 : -1} }`,
			...(itemsPerPage && { count: itemsPerPage }),
			...(current && { offset: current }),
		}),
		[filter, itemsPerPage, current, sortBy, sortDirection],
	);

	const getCustomFields = useEndpoint('GET', '/v1/livechat/custom-fields');
	const { data, isSuccess, isLoading, refetch } = useQuery(['livechat-customFields', query], async () => getCustomFields(query));

	useEffect(() => {
		reload.current = refetch;
	}, [reload, refetch]);
	reload.current = refetch;

	const headers = (
		<>
			<GenericTableHeaderCell key='field' direction={sortDirection} active={sortBy === '_id'} onClick={setSort} sort='_id'>
				{t('Field')}
			</GenericTableHeaderCell>
			<GenericTableHeaderCell key='label' direction={sortDirection} active={sortBy === 'label'} onClick={setSort} sort='label'>
				{t('Label')}
			</GenericTableHeaderCell>
			<GenericTableHeaderCell key='scope' direction={sortDirection} active={sortBy === 'scope'} onClick={setSort} sort='scope'>
				{t('Scope')}
			</GenericTableHeaderCell>
			<GenericTableHeaderCell
				key='visibility'
				direction={sortDirection}
				active={sortBy === 'visibility'}
				onClick={setSort}
				sort='visibility'
			>
				{t('Visibility')}
			</GenericTableHeaderCell>
			<GenericTableHeaderCell key='remove' w='x60'>
				{t('Remove')}
			</GenericTableHeaderCell>
		</>
	);

	const onRowClick = useMutableCallback(
		(id) => () =>
			departmentsRoute.push({
				context: 'edit',
				id,
			}),
	);

	return (
		<>
			<FilterByText onChange={({ text }) => setFilter(text)} />
			{isLoading && (
				<GenericTable>
					<GenericTableHeader>{headers}</GenericTableHeader>
					<GenericTableBody>
						<GenericTableLoadingTable headerCells={5} />
					</GenericTableBody>
				</GenericTable>
			)}
			{isSuccess && data.customFields.length === 0 && <GenericNoResults />}
			{isSuccess && data.customFields.length > 0 && (
				<>
					<GenericTable data-qa='GenericTableCustomFieldsInfoBody'>
						<GenericTableHeader>{headers}</GenericTableHeader>
						<GenericTableBody>
							{data.customFields.map(({ label, _id, scope, visibility }) => (
								<GenericTableRow key={_id} tabIndex={0} role='link' onClick={onRowClick(_id)} action qa-user-id={_id}>
									<GenericTableCell withTruncatedText>{_id}</GenericTableCell>
									<GenericTableCell withTruncatedText>{label}</GenericTableCell>
									<GenericTableCell withTruncatedText>{scope === 'visitor' ? t('Visitor') : t('Room')}</GenericTableCell>
									<GenericTableCell withTruncatedText>{visibility === 'visible' ? t('Visible') : t('Hidden')}</GenericTableCell>
									<RemoveCustomFieldButton _id={_id} reload={refetch} />
								</GenericTableRow>
							))}
						</GenericTableBody>
					</GenericTable>
					<Pagination
						divider
						current={current}
						itemsPerPage={itemsPerPage}
						count={data.total || 0}
						onSetItemsPerPage={onSetItemsPerPage}
						onSetCurrent={onSetCurrent}
						{...paginationProps}
					/>
				</>
			)}
		</>
	);
};

export default CustomFieldsTable;


import { Meteor } from 'meteor/meteor';
import _ from 'underscore';
import type { SettingValue } from '@rocket.chat/core-typings';

import { sdk } from '../../utils/client/lib/SDKClient';

type SettingComposedValue<T extends SettingValue = SettingValue> = { key: string; value: T };
type SettingCallback = (key: string, value: SettingValue, initialLoad?: boolean) => void;

interface ISettingRegexCallbacks {
	regex: RegExp;
	callbacks: SettingCallback[];
}

export class SettingsBase {
	private callbacks = new Map<string, SettingCallback[]>();

	private regexCallbacks = new Map<string, ISettingRegexCallbacks>();

	// private ts = new Date()

	public get(_id: RegExp, callback: SettingCallback): void;

	public get(_id: string, callback: SettingCallback): void;

	public get<T extends SettingValue = SettingValue>(_id: RegExp): SettingComposedValue<T>[];

	public get<T extends SettingValue = SettingValue>(_id: string): T | undefined;

	public get<T extends SettingValue = SettingValue>(
		_id: string | RegExp,
		callback?: SettingCallback,
	): T | undefined | SettingComposedValue<T>[] | void {
		if (callback != null) {
			this.onload(_id, callback);
			if (!Meteor.settings) {
				return;
			}
			if (_id === '*') {
				return Object.keys(Meteor.settings).forEach((key) => {
					const value = Meteor.settings[key];
					callback(key, value);
				});
			}
			if (_.isRegExp(_id) && Meteor.settings) {
				return Object.keys(Meteor.settings).forEach((key) => {
					if (!_id.test(key)) {
						return;
					}
					const value = Meteor.settings[key];
					callback(key, value);
				});
			}

			if (typeof _id === 'string') {
				const value = Meteor.settings[_id];
				if (value != null) {
					callback(_id, Meteor.settings[_id]);
				}
				return;
			}
		}

		if (!Meteor.settings) {
			return;
		}

		if (_.isRegExp(_id)) {
			return Object.keys(Meteor.settings).reduce((items: SettingComposedValue<T>[], key) => {
				const value = Meteor.settings[key];
				if (_id.test(key)) {
					items.push({
						key,
						value,
					});
				}
				return items;
			}, []);
		}

		return Meteor.settings?.[_id];
	}

	set(_id: string, value: SettingValue, callback: (err?: unknown, result?: any) => void): void {
		sdk
			.call('saveSetting', _id, value)
			.then((result) => callback(undefined, result))
			.catch(callback);
	}

	batchSet(settings: Array<{ _id: string; value: SettingValue }>, callback: (err?: unknown, result?: any) => void): void {
		sdk
			.call('saveSettings', settings)
			.then((result) => callback(undefined, result))
			.catch(callback);
	}

	load(key: string, value: SettingValue, initialLoad: boolean): void {
		['*', key].forEach((item) => {
			const callbacks = this.callbacks.get(item);
			if (callbacks) {
				callbacks.forEach((callback) => callback(key, value, initialLoad));
			}
		});
		this.regexCallbacks.forEach((cbValue) => {
			if (!cbValue?.regex.test(key)) {
				return;
			}
			cbValue.callbacks.forEach((callback) => callback(key, value, initialLoad));
		});
	}

	onload(key: string | string[] | RegExp | RegExp[], callback: SettingCallback): void {
		// if key is '*'
		// 	for key, value in Meteor.settings
		// 		callback key, value, false
		// else if Meteor.settings?[_id]?
		// 	callback key, Meteor.settings[_id], false
		const keys: Array<string | RegExp> = Array.isArray(key) ? key : [key];
		keys.forEach((k) => {
			if (_.isRegExp(k)) {
				if (!this.regexCallbacks.has(k.source)) {
					this.regexCallbacks.set(k.source, {
						regex: k,
						callbacks: [],
					});
				}
				this.regexCallbacks.get(k.source)?.callbacks.push(callback);
			} else {
				if (!this.callbacks.has(k)) {
					this.callbacks.set(k, []);
				}
				this.callbacks.get(k)?.push(callback);
			}
		});
	}
}