2022-07-04 02:12:08 -07:00
/* eslint-disable n8n-nodes-base/node-filename-against-convention */
2023-01-27 03:22:44 -08:00
import type {
2023-03-09 09:13:15 -08:00
IExecuteFunctions ,
2023-01-27 03:22:44 -08:00
IDataObject ,
INodeExecutionData ,
INodeType ,
INodeTypeDescription ,
} from 'n8n-workflow' ;
2021-05-16 11:35:11 -07:00
2022-08-17 08:50:24 -07:00
import { promisify } from 'util' ;
2021-05-16 11:35:11 -07:00
2022-04-08 14:32:08 -07:00
import moment from 'moment-timezone' ;
2021-05-16 11:35:11 -07:00
import * as ics from 'ics' ;
const createEvent = promisify ( ics . createEvent ) ;
export class ICalendar implements INodeType {
description : INodeTypeDescription = {
displayName : 'iCalendar' ,
name : 'iCal' ,
icon : 'fa:calendar' ,
group : [ 'input' ] ,
version : 1 ,
subtitle : '={{$parameter["operation"]}}' ,
description : 'Create iCalendar file' ,
defaults : {
name : 'iCalendar' ,
color : '#408000' ,
} ,
inputs : [ 'main' ] ,
outputs : [ 'main' ] ,
credentials : [ ] ,
properties : [
{
displayName : 'Operation' ,
name : 'operation' ,
type : 'options' ,
2022-05-20 14:47:24 -07:00
noDataExpression : true ,
2021-05-16 11:35:11 -07:00
options : [
{
name : 'Create Event File' ,
value : 'createEventFile' ,
} ,
] ,
default : 'createEventFile' ,
} ,
{
displayName : 'Event Title' ,
name : 'title' ,
type : 'string' ,
default : '' ,
} ,
{
displayName : 'Start' ,
name : 'start' ,
type : 'dateTime' ,
default : '' ,
required : true ,
2022-08-17 08:50:24 -07:00
description :
'Date and time at which the event begins. (For all-day events, the time will be ignored.).' ,
2021-05-16 11:35:11 -07:00
} ,
{
displayName : 'End' ,
name : 'end' ,
type : 'dateTime' ,
default : '' ,
required : true ,
2022-08-17 08:50:24 -07:00
description :
'Date and time at which the event ends. (For all-day events, the time will be ignored.).' ,
2021-05-16 11:35:11 -07:00
} ,
{
displayName : 'All Day' ,
name : 'allDay' ,
type : 'boolean' ,
default : false ,
2022-05-06 14:01:25 -07:00
description : 'Whether the event lasts all day or not' ,
2021-05-16 11:35:11 -07:00
} ,
{
displayName : 'Binary Property' ,
name : 'binaryPropertyName' ,
type : 'string' ,
default : 'data' ,
required : true ,
2022-05-06 14:01:25 -07:00
description : 'The field that your iCalendar file will be available under in the output' ,
2021-05-16 11:35:11 -07:00
} ,
{
displayName : 'Additional Fields' ,
name : 'additionalFields' ,
type : 'collection' ,
placeholder : 'Add Field' ,
default : { } ,
displayOptions : {
show : {
2022-08-17 08:50:24 -07:00
operation : [ 'createEventFile' ] ,
2021-05-16 11:35:11 -07:00
} ,
} ,
options : [
{
displayName : 'Attendees' ,
name : 'attendeesUi' ,
type : 'fixedCollection' ,
typeOptions : {
multipleValues : true ,
} ,
placeholder : 'Add Attendee' ,
default : { } ,
options : [
{
displayName : 'Attendees' ,
name : 'attendeeValues' ,
values : [
{
displayName : 'Name' ,
name : 'name' ,
type : 'string' ,
required : true ,
default : '' ,
} ,
{
displayName : 'Email' ,
name : 'email' ,
type : 'string' ,
2022-06-20 07:54:01 -07:00
placeholder : 'name@email.com' ,
2021-05-16 11:35:11 -07:00
required : true ,
default : '' ,
} ,
{
displayName : 'RSVP' ,
name : 'rsvp' ,
type : 'boolean' ,
default : false ,
2022-05-06 14:01:25 -07:00
description : 'Whether the attendee has to confirm attendance or not' ,
2021-05-16 11:35:11 -07:00
} ,
] ,
} ,
] ,
} ,
{
displayName : 'Busy Status' ,
name : 'busyStatus' ,
type : 'options' ,
options : [
{
name : 'Busy' ,
value : 'BUSY' ,
} ,
{
name : 'Tentative' ,
value : 'TENTATIVE' ,
} ,
] ,
default : '' ,
2022-05-06 14:01:25 -07:00
description : 'Used to specify busy status for Microsoft applications, like Outlook' ,
2021-05-16 11:35:11 -07:00
} ,
{
displayName : 'Calendar Name' ,
name : 'calName' ,
type : 'string' ,
default : '' ,
2022-08-17 08:50:24 -07:00
description :
'Specifies the calendar (not event) name. Used by Apple iCal and Microsoft Outlook (<a href="https://docs.microsoft.com/en-us/openspecs/exchange_server_protocols/ms-oxcical/1da58449-b97e-46bd-b018-a1ce576f3e6d">spec</a>).' ,
2021-05-16 11:35:11 -07:00
} ,
{
displayName : 'Description' ,
name : 'description' ,
type : 'string' ,
default : '' ,
} ,
{
displayName : 'File Name' ,
name : 'fileName' ,
type : 'string' ,
default : '' ,
description : 'The name of the file to be generated. Default value is event.ics.' ,
} ,
{
displayName : 'Geolocation' ,
name : 'geolocationUi' ,
type : 'fixedCollection' ,
typeOptions : {
multipleValues : false ,
} ,
placeholder : 'Add Geolocation' ,
default : { } ,
options : [
{
displayName : 'Geolocation' ,
name : 'geolocationValues' ,
values : [
{
displayName : 'Latitude' ,
name : 'lat' ,
type : 'string' ,
default : '' ,
} ,
{
displayName : 'Longitude' ,
name : 'lon' ,
type : 'string' ,
default : '' ,
} ,
] ,
} ,
] ,
} ,
{
displayName : 'Location' ,
name : 'location' ,
type : 'string' ,
default : '' ,
2022-05-06 14:01:25 -07:00
description : 'The intended venue' ,
2021-05-16 11:35:11 -07:00
} ,
{
displayName : 'Recurrence Rule' ,
name : 'recurrenceRule' ,
type : 'string' ,
default : '' ,
2022-08-17 08:50:24 -07:00
description :
'A rule to define the repeat pattern of the event (RRULE). (<a href="https://icalendar.org/rrule-tool.html">Rule generator</a>).' ,
2021-05-16 11:35:11 -07:00
} ,
{
displayName : 'Organizer' ,
name : 'organizerUi' ,
type : 'fixedCollection' ,
typeOptions : {
multipleValues : false ,
} ,
placeholder : 'Add Organizer' ,
default : { } ,
options : [
{
displayName : 'Organizer' ,
name : 'organizerValues' ,
values : [
{
displayName : 'Name' ,
name : 'name' ,
type : 'string' ,
default : '' ,
required : true ,
} ,
{
displayName : 'Email' ,
name : 'email' ,
type : 'string' ,
2022-06-20 07:54:01 -07:00
placeholder : 'name@email.com' ,
2021-05-16 11:35:11 -07:00
default : '' ,
required : true ,
} ,
] ,
} ,
] ,
} ,
{
displayName : 'Sequence' ,
name : 'sequence' ,
type : 'number' ,
default : 0 ,
2022-08-17 08:50:24 -07:00
description :
'When sending an update for an event (with the same uid), defines the revision sequence number' ,
2021-05-16 11:35:11 -07:00
} ,
{
displayName : 'Status' ,
name : 'status' ,
type : 'options' ,
options : [
{
name : 'Confirmed' ,
value : 'CONFIRMED' ,
} ,
{
name : 'Cancelled' ,
value : 'CANCELLED' ,
} ,
{
name : 'Tentative' ,
value : 'TENTATIVE' ,
} ,
] ,
default : 'CONFIRMED' ,
} ,
{
displayName : 'UID' ,
name : 'uid' ,
type : 'string' ,
default : '' ,
2022-08-17 08:50:24 -07:00
description :
'Universally unique ID for the event (will be auto-generated if not specified here). Should be globally unique.' ,
2021-05-16 11:35:11 -07:00
} ,
{
displayName : 'URL' ,
name : 'url' ,
type : 'string' ,
default : '' ,
2022-05-06 14:01:25 -07:00
description : 'URL associated with event' ,
2021-05-16 11:35:11 -07:00
} ,
] ,
} ,
] ,
} ;
async execute ( this : IExecuteFunctions ) : Promise < INodeExecutionData [ ] [ ] > {
const items = this . getInputData ( ) ;
2022-04-22 09:29:51 -07:00
const length = items . length ;
2021-05-16 11:35:11 -07:00
const returnData : INodeExecutionData [ ] = [ ] ;
2022-12-02 03:53:59 -08:00
const operation = this . getNodeParameter ( 'operation' , 0 ) ;
2021-05-16 11:35:11 -07:00
if ( operation === 'createEventFile' ) {
for ( let i = 0 ; i < length ; i ++ ) {
const title = this . getNodeParameter ( 'title' , i ) as string ;
const allDay = this . getNodeParameter ( 'allDay' , i ) as boolean ;
const start = this . getNodeParameter ( 'start' , i ) as string ;
let end = this . getNodeParameter ( 'end' , i ) as string ;
2022-12-02 12:54:28 -08:00
end = allDay ? moment ( end ) . utc ( ) . add ( 1 , 'day' ) . format ( ) : end ;
2023-01-06 06:09:32 -08:00
const binaryPropertyName = this . getNodeParameter ( 'binaryPropertyName' , i ) ;
2022-11-18 07:29:44 -08:00
const additionalFields = this . getNodeParameter ( 'additionalFields' , i ) ;
2021-05-16 11:35:11 -07:00
let fileName = 'event.ics' ;
2022-08-17 08:50:24 -07:00
const eventStart = moment ( start )
. toArray ( )
. splice ( 0 , allDay ? 3 : 6 ) as ics . DateArray ;
2022-01-08 04:11:58 -08:00
eventStart [ 1 ] ++ ;
2022-08-17 08:50:24 -07:00
const eventEnd = moment ( end )
. toArray ( )
. splice ( 0 , allDay ? 3 : 6 ) as ics . DateArray ;
2022-01-08 04:11:58 -08:00
eventEnd [ 1 ] ++ ;
2021-05-16 11:35:11 -07:00
if ( additionalFields . fileName ) {
fileName = additionalFields . fileName as string ;
}
const data : ics.EventAttributes = {
title ,
2022-01-08 04:11:58 -08:00
start : eventStart ,
end : eventEnd ,
2021-05-16 11:35:11 -07:00
startInputType : 'utc' ,
endInputType : 'utc' ,
} ;
if ( additionalFields . geolocationUi ) {
2022-08-17 08:50:24 -07:00
data . geo = ( additionalFields . geolocationUi as IDataObject )
. geolocationValues as ics . GeoCoordinates ;
2021-05-16 11:35:11 -07:00
delete additionalFields . geolocationUi ;
}
if ( additionalFields . organizerUi ) {
2022-08-17 08:50:24 -07:00
data . organizer = ( additionalFields . organizerUi as IDataObject )
. organizerValues as ics . Person ;
2021-05-16 11:35:11 -07:00
delete additionalFields . organizerUi ;
}
if ( additionalFields . attendeesUi ) {
2022-08-17 08:50:24 -07:00
data . attendees = ( additionalFields . attendeesUi as IDataObject )
. attendeeValues as ics . Attendee [ ] ;
2021-05-16 11:35:11 -07:00
delete additionalFields . attendeesUi ;
}
Object . assign ( data , additionalFields ) ;
2022-08-17 08:50:24 -07:00
const buffer = Buffer . from ( ( await createEvent ( data ) ) as string ) ;
2021-05-16 11:35:11 -07:00
const binaryData = await this . helpers . prepareBinaryData ( buffer , fileName , 'text/calendar' ) ;
2022-08-17 08:50:24 -07:00
returnData . push ( {
json : { } ,
binary : {
[ binaryPropertyName ] : binaryData ,
} ,
pairedItem : {
item : i ,
2021-05-16 11:35:11 -07:00
} ,
2022-08-17 08:50:24 -07:00
} ) ;
2021-05-16 11:35:11 -07:00
}
}
return [ returnData ] ;
}
}