From 9aac3e0e5e0bb54b1400da8ef42e1544f033a72a Mon Sep 17 00:00:00 2001 From: Sebastian Pekarek Date: Sat, 19 Mar 2022 16:23:45 +0100 Subject: [PATCH] feat: Add Support for Sent By Allows to specify the calendar user that is acting on behalf of the calendar user. Both supported within the Organizator and Attendees. see #358 --- src/attendee.ts | 31 +++++++++++++++++++++ src/event.ts | 8 ++++-- src/tools.ts | 3 ++- src/types.ts | 1 + test/attendee.ts | 70 ++++++++++++++++++++++++++++++++++-------------- test/event.ts | 24 ++++++++++++++--- 6 files changed, 111 insertions(+), 26 deletions(-) diff --git a/src/attendee.ts b/src/attendee.ts index b03a18b88..a96ace678 100755 --- a/src/attendee.ts +++ b/src/attendee.ts @@ -9,6 +9,7 @@ interface ICalInternalAttendeeData { name: string | null; email: string | null; mailto: string | null; + sentBy: string | null; status: ICalAttendeeStatus | null; role: ICalAttendeeRole; rsvp: boolean | null; @@ -22,6 +23,7 @@ export interface ICalAttendeeData { name?: string | null; email?: string | null; mailto?: string | null; + sentBy?: string | null; status?: ICalAttendeeStatus | null; role?: ICalAttendeeRole; rsvp?: boolean | null; @@ -37,6 +39,7 @@ export interface ICalAttendeeJSONData { name: string | null; email: string | null; mailto: string | null; + sentBy: string | null; status: ICalAttendeeStatus | null; role: ICalAttendeeRole; rsvp: boolean | null; @@ -105,6 +108,7 @@ export default class ICalAttendee { name: null, email: null, mailto: null, + sentBy: null, status: null, role: ICalAttendeeRole.REQ, rsvp: null, @@ -121,6 +125,7 @@ export default class ICalAttendee { data.name !== undefined && this.name(data.name); data.email !== undefined && this.email(data.email); data.mailto !== undefined && this.mailto(data.mailto); + data.sentBy !== undefined && this.sentBy(data.sentBy); data.status !== undefined && this.status(data.status); data.role !== undefined && this.role(data.role); data.rsvp !== undefined && this.rsvp(data.rsvp); @@ -195,6 +200,27 @@ export default class ICalAttendee { } + /** + * Get the acting user's email adress + * @since 3.3.0 + */ + sentBy(): string | null; + + /** + * Set the acting user's email adress + * @since 3.3.0 + */ + sentBy(email: string | null): this; + sentBy(email?: string | null): this | string | null { + if (!email) { + return this.data.sentBy; + } + + this.data.sentBy = email; + return this; + } + + /** * Get attendee's role * @since 0.2.0 @@ -549,6 +575,11 @@ export default class ICalAttendee { g += ';RSVP=' + this.data.rsvp.toString().toUpperCase(); } + // SENT-BY + if (this.data.sentBy !== null) { + g += ';SENT-BY="mailto:' + this.data.sentBy + '"'; + } + // DELEGATED-TO if (this.data.delegatedTo) { g += ';DELEGATED-TO="' + this.data.delegatedTo.email() + '"'; diff --git a/src/event.ts b/src/event.ts index 2ebef98bb..9d53d195c 100755 --- a/src/event.ts +++ b/src/event.ts @@ -823,13 +823,14 @@ export default class ICalEvent { * event.organizer('Organizer\'s Name '); * ``` * - * You can also add an explicit `mailto` email address. + * You can also add an explicit `mailto` email address or or the sentBy address. * * ```javascript * event.organizer({ * name: 'Organizer\'s Name', * email: 'organizer@example.com', - * mailto: 'explicit@mailto.com' + * mailto: 'explicit@mailto.com', + * sentBy: 'substitute@example.com' * }) * ``` * @@ -1585,6 +1586,9 @@ export default class ICalEvent { if (this.data.organizer) { g += 'ORGANIZER;CN="' + escape(this.data.organizer.name) + '"'; + if (this.data.organizer.sentBy) { + g += ';SENT-BY="mailto:' + escape(this.data.organizer.sentBy) + '"'; + } if (this.data.organizer.email && this.data.organizer.mailto) { g += ';EMAIL=' + escape(this.data.organizer.email); } diff --git a/src/tools.ts b/src/tools.ts index 260470d72..6de55e252 100755 --- a/src/tools.ts +++ b/src/tools.ts @@ -232,7 +232,8 @@ export function checkNameAndMail (attribute: string, value: string | ICalOrganiz result = { name: value.name, email: value.email, - mailto: value.mailto + mailto: value.mailto, + sentBy: value.sentBy }; } diff --git a/src/types.ts b/src/types.ts index d787e2100..2af72dfd7 100644 --- a/src/types.ts +++ b/src/types.ts @@ -41,6 +41,7 @@ export interface ICalOrganizer { name: string; email?: string; mailto?: string; + sentBy?: string; } export interface ICalDescription { diff --git a/test/attendee.ts b/test/attendee.ts index 55cb7b7c3..4e7672745 100644 --- a/test/attendee.ts +++ b/test/attendee.ts @@ -3,7 +3,12 @@ import assert from 'assert'; import ICalCalendar from '../src/calendar'; import ICalEvent from '../src/event'; -import ICalAttendee, {ICalAttendeeData, ICalAttendeeRole, ICalAttendeeStatus, ICalAttendeeType} from '../src/attendee'; +import ICalAttendee, { + ICalAttendeeData, + ICalAttendeeRole, + ICalAttendeeStatus, + ICalAttendeeType +} from '../src/attendee'; describe('ical-generator Attendee', function () { describe('constructor()', function () { @@ -12,6 +17,7 @@ describe('ical-generator Attendee', function () { name: 'John Doe', email: 'john@example.org', mailto: 'john+calendar@example.org', + sentBy: null, status: ICalAttendeeStatus.ACCEPTED, role: ICalAttendeeRole.REQ, rsvp: false, @@ -27,7 +33,7 @@ describe('ical-generator Attendee', function () { it('shouldn\'t work without event reference', function () { assert.throws(function () { // @ts-ignore - new ICalAttendee({email: 'foo@bar.com'}); + new ICalAttendee({ email: 'foo@bar.com' }); }, /`event`/); }); }); @@ -70,7 +76,7 @@ describe('ical-generator Attendee', function () { }); it('should change something', function () { - const a = new ICalAttendee({email: 'mail@example.com'}, new ICalEvent({}, new ICalCalendar())); + const a = new ICalAttendee({ email: 'mail@example.com' }, new ICalEvent({}, new ICalCalendar())); assert.ok(a.toString().indexOf('mail@example.com') > -1); }); }); @@ -91,7 +97,7 @@ describe('ical-generator Attendee', function () { }); it('should change mailto and keep email if present', function () { - const a = new ICalAttendee({email: 'mail@example.com'}, new ICalEvent({}, new ICalCalendar())); + const a = new ICalAttendee({ email: 'mail@example.com' }, new ICalEvent({}, new ICalCalendar())); a.mailto('mail2@example2.com'); assert.ok( a.toString().indexOf('EMAIL=mail@example.com') > -1 && @@ -100,6 +106,23 @@ describe('ical-generator Attendee', function () { }); }); + describe('sentBy()', function () { + it('getter should return value', function () { + const a = new ICalAttendee({}, new ICalEvent({}, new ICalCalendar())).sentBy('foo@example.com'); + assert.strictEqual(a.sentBy(), 'foo@example.com'); + }); + + it('setter should return this', function () { + const a = new ICalAttendee({}, new ICalEvent({}, new ICalCalendar())); + assert.deepStrictEqual(a, a.sentBy('foo@example.com')); + }); + + it('should change something', function () { + const a = new ICalAttendee({ email: 'foo@example.com', sentBy: 'bar@example.com' }, new ICalEvent({}, new ICalCalendar())); + assert.ok(a.toString().includes('bar@example.com')); + }); + }); + describe('role()', function () { it('setter should return this', function () { const a = new ICalAttendee({}, new ICalEvent({}, new ICalCalendar())); @@ -136,8 +159,8 @@ describe('ical-generator Attendee', function () { }); }); - describe('rsvp()', function() { - it('setter should return this', function() { + describe('rsvp()', function () { + it('setter should return this', function () { const a = new ICalAttendee({}, new ICalEvent({}, new ICalCalendar())); assert.deepStrictEqual(a, a.rsvp(null)); assert.deepStrictEqual(a, a.rsvp(true)); @@ -153,7 +176,7 @@ describe('ical-generator Attendee', function () { assert.strictEqual(a.rsvp(), false); }); - it('getter should return value', function() { + it('getter should return value', function () { const a = new ICalAttendee({}, new ICalEvent({}, new ICalCalendar())); assert.strictEqual(a.rsvp(), null); a.rsvp(false); @@ -162,8 +185,11 @@ describe('ical-generator Attendee', function () { assert.strictEqual(a.rsvp(), null); }); - it('should change something', function() { - const a = new ICalAttendee({email: 'mail@example.com', rsvp: true}, new ICalEvent({}, new ICalCalendar())); + it('should change something', function () { + const a = new ICalAttendee({ + email: 'mail@example.com', + rsvp: true + }, new ICalEvent({}, new ICalCalendar())); assert.ok(a.toString().indexOf(';RSVP=TRUE') > -1); }); }); @@ -196,7 +222,7 @@ describe('ical-generator Attendee', function () { it('should change something', function () { const a = new ICalAttendee( - {email: 'mail@example.com', status: ICalAttendeeStatus.DECLINED}, + { email: 'mail@example.com', status: ICalAttendeeStatus.DECLINED }, new ICalEvent({}, new ICalCalendar()) ); assert.ok(a.toString().indexOf('DECLINED') > -1); @@ -204,7 +230,7 @@ describe('ical-generator Attendee', function () { it('should change something too', function () { const a = new ICalAttendee( - {email: 'mail@example.com', status: ICalAttendeeStatus.NEEDSACTION}, + { email: 'mail@example.com', status: ICalAttendeeStatus.NEEDSACTION }, new ICalEvent({}, new ICalCalendar()) ); assert.ok(a.toString().indexOf('NEEDS-ACTION') > -1); @@ -294,7 +320,10 @@ describe('ical-generator Attendee', function () { }); it('should change something', function () { - const a = new ICalAttendee({email: 'mail@example.com', delegatedFrom: 'foo@example.com'}, new ICalEvent({}, new ICalCalendar())); + const a = new ICalAttendee({ + email: 'mail@example.com', + delegatedFrom: 'foo@example.com' + }, new ICalEvent({}, new ICalCalendar())); assert.ok(a.toString().indexOf('foo@example.com') > -1); }); }); @@ -307,14 +336,14 @@ describe('ical-generator Attendee', function () { it('should reuse the same ICalAttendee instance if passed', function () { const event = new ICalEvent({}, new ICalCalendar()); - const attendee = new ICalAttendee({name: 'Muh'}, event); + const attendee = new ICalAttendee({ name: 'Muh' }, event); assert.deepStrictEqual(new ICalAttendee({}, event).delegatesTo(attendee), attendee); }); it('should pass data to instance', function () { - const attendee = new ICalAttendee({name: 'Zac'}, new ICalEvent({}, new ICalCalendar())) - .delegatesTo({name: 'Cody'}); + const attendee = new ICalAttendee({ name: 'Zac' }, new ICalEvent({}, new ICalCalendar())) + .delegatesTo({ name: 'Cody' }); assert.strictEqual(attendee.name(), 'Cody'); }); @@ -328,27 +357,27 @@ describe('ical-generator Attendee', function () { it('should reuse the same ICalAttendee instance if passed', function () { const event = new ICalEvent({}, new ICalCalendar()); - const attendee = new ICalAttendee({name: 'Muh'}, event); + const attendee = new ICalAttendee({ name: 'Muh' }, event); assert.deepStrictEqual(new ICalAttendee({}, event).delegatesFrom(attendee), attendee); }); it('should pass data to instance', function () { - const a = new ICalAttendee({name: 'Zac'}, new ICalEvent({}, new ICalCalendar())).delegatesFrom({name: 'Cody'}); + const a = new ICalAttendee({ name: 'Zac' }, new ICalEvent({}, new ICalCalendar())).delegatesFrom({ name: 'Cody' }); assert.strictEqual(a.name(), 'Cody'); }); }); describe('x()', function () { it('works as expected', function () { - const a = new ICalAttendee({email: 'foo@example.org'}, new ICalEvent({}, new ICalCalendar())); + const a = new ICalAttendee({ email: 'foo@example.org' }, new ICalEvent({}, new ICalCalendar())); assert.deepStrictEqual(a, a.x('X-NUM-GUESTS', '5')); assert.ok(a.toString().includes('ATTENDEE;ROLE=REQ-PARTICIPANT;X-NUM-GUESTS=5:MAILTO:foo@example.org')); }); }); describe('toJSON()', function () { - it('should work', function() { + it('should work', function () { const a = new ICalAttendee({}, new ICalEvent({}, new ICalCalendar())); a.name('Max Mustermann'); a.delegatesTo('Moritz '); @@ -358,6 +387,7 @@ describe('ical-generator Attendee', function () { delegatedTo: 'moritz@example.com', email: null, mailto: null, + sentBy: null, name: 'Max Mustermann', role: 'REQ-PARTICIPANT', rsvp: null, @@ -375,7 +405,7 @@ describe('ical-generator Attendee', function () { describe('generate()', function () { it('should throw an error without email', function () { - const a = new ICalAttendee({name: 'Testuser'}, new ICalEvent({}, new ICalCalendar())); + const a = new ICalAttendee({ name: 'Testuser' }, new ICalEvent({}, new ICalCalendar())); assert.throws(function () { a.toString(); }, /`email`/); diff --git a/test/event.ts b/test/event.ts index b6ef500ba..9f97d3454 100644 --- a/test/event.ts +++ b/test/event.ts @@ -1209,14 +1209,31 @@ describe('ical-generator Event', function () { assert.deepStrictEqual(event.organizer(), { name: 'Sebastian Pekarek', email: 'mail@example.com', - mailto: undefined + mailto: undefined, + sentBy: undefined }); event.organizer({name: 'Sebastian Pekarek', email: 'mail@example.com', mailto: 'mail2@example2.com'}); assert.deepStrictEqual(event.organizer(), { name: 'Sebastian Pekarek', email: 'mail@example.com', - mailto: 'mail2@example2.com' + mailto: 'mail2@example2.com', + sentBy: undefined + }); + }); + + it('should support sent by when using object', function () { + const event = new ICalEvent({ + start: moment(), + summary: 'Example Event' + }, new ICalCalendar()); + + event.organizer({name: 'Sebastian Pekarek', email: 'mail@example.com', sentBy: 'bot@example.com'}); + assert.deepStrictEqual(event.organizer(), { + name: 'Sebastian Pekarek', + email: 'mail@example.com', + mailto: undefined, + sentBy: 'bot@example.com' }); }); @@ -1267,7 +1284,8 @@ describe('ical-generator Event', function () { assert.deepStrictEqual(event.organizer(), { name: 'Sebastian Pekarek', email: undefined, - mailto: undefined + mailto: undefined, + sentBy: undefined }); }); });