2021-05-16 11:35:11 -07:00
import {
IExecuteFunctions ,
} from 'n8n-core' ;
import {
IDataObject ,
INodeExecutionData ,
INodeType ,
INodeTypeDescription ,
} from 'n8n-workflow' ;
import {
promisify ,
} from 'util' ;
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-04-22 09:29:51 -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-04-22 09:29:51 -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 : {
operation : [
'createEventFile' ,
] ,
} ,
} ,
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' ,
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 : '' ,
2021-08-26 10:42:38 -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-04-22 09:29:51 -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' ,
default : '' ,
required : true ,
} ,
] ,
} ,
] ,
} ,
{
displayName : 'Sequence' ,
name : 'sequence' ,
type : 'number' ,
default : 0 ,
2022-05-06 14:01:25 -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-05-06 14:01:25 -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 [ ] = [ ] ;
const operation = this . getNodeParameter ( 'operation' , 0 ) as string ;
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 ;
end = ( allDay ) ? moment ( end ) . utc ( ) . add ( 1 , 'day' ) . format ( ) as string : end ;
const binaryPropertyName = this . getNodeParameter ( 'binaryPropertyName' , i ) as string ;
const additionalFields = this . getNodeParameter ( 'additionalFields' , i ) as IDataObject ;
let fileName = 'event.ics' ;
2022-01-08 04:14:18 -08: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-01-08 04:14:18 -08: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 ) {
data . geo = ( additionalFields . geolocationUi as IDataObject ) . geolocationValues as ics . GeoCoordinates ;
delete additionalFields . geolocationUi ;
}
if ( additionalFields . organizerUi ) {
data . organizer = ( additionalFields . organizerUi as IDataObject ) . organizerValues as ics . Person ;
delete additionalFields . organizerUi ;
}
if ( additionalFields . attendeesUi ) {
data . attendees = ( additionalFields . attendeesUi as IDataObject ) . attendeeValues as ics . Attendee [ ] ;
delete additionalFields . attendeesUi ;
}
Object . assign ( data , additionalFields ) ;
const buffer = Buffer . from ( await createEvent ( data ) as string ) ;
const binaryData = await this . helpers . prepareBinaryData ( buffer , fileName , 'text/calendar' ) ;
returnData . push (
{
json : { } ,
binary : {
[ binaryPropertyName ] : binaryData ,
} ,
2022-06-03 08:25:07 -07:00
pairedItem : {
item : i ,
} ,
2021-05-16 11:35:11 -07:00
} ,
) ;
}
}
return [ returnData ] ;
}
}