Commit a5e74292 authored by Sebastian Pekarek's avatar Sebastian Pekarek
Browse files

basic attendee support

#12
parent b1366b9c
......@@ -300,9 +300,12 @@ Appointment description
#### location([String location])
Appointment location
#### organizer([String|Object organizer])
Appointment organizer
```javascript
......@@ -317,6 +320,40 @@ cal.organizer('Organizer\'s Name <organizer@example.com>');
```
#### createAttendee([Object options])
Creates a new [Attendee](#attendee) ([`ICalAttendee`](#attendee)) and returns it. Use options to prefill the attendee's attributes.
Calling this method without options will create an empty attendee.
```javascript
var ical = require('ical-generator'),
cal = ical(),
event = cal.createEvent(),
attendee = event.createAttendee({email: 'hui@example.com', 'name': 'Hui'});
// overwrite attendee's email address
attendee.email('hui@example.net');
// add another attendee
event.createAttendee('Buh <buh@example.net>');
```
#### attendees([Object attendees])
Add Attendees to the event or return all attached attendees.
```javascript
var event = ical().createEvent();
cal.attendees([
{email: 'a@example.com', name: 'Person A'},
{email: 'b@example.com', name: 'Person B'}
]);
cal.attendees(); // --> [ICalAttendee, ICalAttendee]
```
#### url([String url])
Appointment URL
......@@ -333,6 +370,55 @@ Appointment status. May be any of the following: confirmed, tenative, cancelled.
### Attendee
#### name([String name])
Use this method to set the attendee's name.
#### email([String email])
The attendee's email address. An email address is required for every attendee!
#### role([String role])
Set the attendee's role, defaults to `REQ-PARTICIPANT`. May be one of the following: req-participant, non-participant
#### status([String status])
Set the attendee's status. May be one of the following: accepted, tentative, declined
#### delegatesTo(ICalAttendee|Object attendee)
Creates a new Attendee if passed object is not already an attendee. Will set the delegatedTo and delegatedFrom attributes.
```javascript
var cal = ical(),
event = cal.createEvent(),
attendee = cal.createAttendee();
attendee.delegatesTo({email: 'foo@bar.com', name: 'Foo'});
```
#### delegatesFrom(ICalAttendee|Object attendee)
Creates a new Attendee if passed object is not already an attendee. Will set the delegatedTo and delegatedFrom attributes.
```javascript
var cal = ical(),
event = cal.createEvent(),
attendee = cal.createAttendee();
attendee.delegatesFrom({email: 'foo@bar.com', name: 'Foo'});
```
## Tests
```
......
'use strict';
/**
* @author Sebastian Pekarek
* @module attendee
* @constructor ICalAttendee Attendee
*/
var ICalAttendee = function(_data, event) {
var vars,
i,
data;
if(!event) {
throw '`event` option required!';
}
vars = {
allowedRoles: ['REQ-PARTICIPANT', 'NON-PARTICIPANT'],
allowedStatuses: ['ACCEPTED', 'TENTATIVE', 'DECLINED', 'DELEGATED']
};
data = {
name: null,
email: null,
status: null,
role: 'REQ-PARTICIPANT',
delegatedTo: null,
delegatedFrom: null
};
/**
* Set/Get the attendee's name
*
* @param name Name
* @since 0.2.0
* @returns {ICalAttendee|String}
*/
this.name = function(name) {
if(!name) {
return data.name;
}
data.name = name;
return this;
};
/**
* Set/Get the attendee's email address
*
* @param email Email address
* @since 0.2.0
* @returns {ICalAttendee|String}
*/
this.email = function(email) {
if(!email) {
return data.email;
}
data.email = email;
return this;
};
/**
* Set/Get attendee's role
*
* @param {String} role
* @since 0.2.0
* @returns {ICalAttendee|String}
*/
this.role = function(role) {
if(!role) {
return data.role;
}
if(vars.allowedRoles.indexOf(role.toUpperCase()) === -1) {
throw '`role` must be one of the following: ' + vars.allowedRoles.join(', ') + '!';
}
data.role = role.toUpperCase();
return this;
};
/**
* Set/Get attendee's status
*
* @param {String} status
* @since 0.2.0
* @returns {ICalAttendee|String}
*/
this.status = function(status) {
if(!status) {
return data.status;
}
if(vars.allowedStatuses.indexOf(status.toUpperCase()) === -1) {
throw '`status` must be one of the following: ' + vars.allowedStatuses.join(', ') + '!';
}
data.status = status.toUpperCase();
return this;
};
/**
* Set/Get the attendee's delegated-to field
*
* @param delegatedTo Email address
* @since 0.2.0
* @returns {ICalAttendee|String}
*/
this.delegatedTo = function(delegatedTo) {
if(!delegatedTo) {
return data.delegatedTo;
}
data.delegatedTo = delegatedTo;
data.status = 'DELEGATED';
return this;
};
/**
* Set/Get the attendee's delegated-from field
*
* @param delegatedFrom Email address
* @since 0.2.0
* @returns {ICalAttendee|String}
*/
this.delegatedFrom = function(delegatedFrom) {
if(!delegatedFrom) {
return data.delegatedFrom;
}
data.delegatedFrom = delegatedFrom;
return this;
};
/**
* Create a new attendee this attendee delegates to
* and returns this new attendee
*
* @param {Object} options
* @since 0.2.0
* @returns {ICalAttendee}
*/
this.delegatesTo = function(options) {
var a = options instanceof ICalAttendee ? options : event.createAttendee(options);
this.delegatedTo(a);
a.delegatedFrom(this);
return a;
};
/**
* Create a new attendee this attendee delegates from
* and returns this new attendee
*
* @param {Object} options
* @since 0.2.0
* @returns {ICalAttendee}
*/
this.delegatesFrom = function(options) {
var a = options instanceof ICalAttendee ? options : event.createAttendee(options);
this.delegatedFrom(a);
a.delegatedTo(this);
return a;
};
/**
* Export Event to iCal
*
* @since 0.2.0
* @returns {String}
*/
this.generate = function() {
var g = 'ATTENDEE';
if(!data.email) {
throw 'No value for `email` in ICalAttendee given!';
}
// ROLE
g += ';ROLE=' + data.role;
// PARTSTAT
if(data.status) {
g += ';PARTSTAT=' + data.status;
}
// DELEGATED-TO
if(data.delegatedTo) {
g += ';DELEGATED-TO="' + (data.delegatedTo instanceof ICalAttendee ? data.delegatedTo.email() : data.delegatedTo) + '"';
}
// DELEGATED-FROM
if(data.delegatedFrom) {
g += ';DELEGATED-FROM="' + (data.delegatedFrom instanceof ICalAttendee ? data.delegatedFrom.email() : data.delegatedFrom) + '"';
}
// CN / Name
if(data.name) {
g += ';CN="' + data.name + '"';
}
g += ':MAILTO:' + data.email + '\n';
return g;
};
for(i in _data) {
if(_data.hasOwnProperty(i) && ['name', 'email', 'role', 'status', 'delegatedTo', 'delegatedFrom', 'delegatesFrom', 'delegatesTo'].indexOf(i) > -1) {
this[i](_data[i]);
}
}
};
module.exports = ICalAttendee;
\ No newline at end of file
......@@ -3,7 +3,7 @@
/**
* @author Sebastian Pekarek
* @module obj-calendar
* @module calendar
* @constructor ICalCalendar Calendar
*/
var ICalCalendar = function(_data) {
......@@ -289,12 +289,11 @@ var ICalCalendar = function(_data) {
return data.events;
}
var ref = [],
cal = this;
var cal = this;
events.forEach(function(e) {
ref.push(cal.createEvent(e));
cal.createEvent(e);
});
return ref;
return cal;
};
......
......@@ -3,10 +3,10 @@
/**
* @author Sebastian Pekarek
* @module obj-event
* @module event
* @constructor ICalEvent Event
*/
var ICalEvent= function(_data) {
var ICalEvent = function(_data) {
var vars,
i,
data;
......@@ -29,6 +29,7 @@ var ICalEvent= function(_data) {
location: null,
description: null,
organizer: null,
attendees: [],
method: null,
status: null,
url: null
......@@ -328,6 +329,57 @@ var ICalEvent= function(_data) {
};
/**
* Create a new Attendee and return the attendee object…
*
* @param [attendeeData] Attendee-Options
* @since 0.2.0
* @returns {ICalAttendee}
*/
this.createAttendee = function(_attendeeData) {
var ICalAttendee = require('./attendee.js'),
attendeeRegEx = /^(.+) ?<([^>]+)>$/,
attendee;
if(typeof _attendeeData === 'string' && attendeeRegEx.test(_attendeeData)) {
attendee = new ICalAttendee({
name: RegExp.$1.trim(),
email: RegExp.$2
}, this);
data.attendees.push(attendee);
return attendee;
}
if(typeof _attendeeData === 'string') {
// @todo URL with Documentation on GitHub
throw '`attendee` isn\'t formated correctly. See documentation for help…';
}
attendee = new ICalAttendee(_attendeeData, this);
data.attendees.push(attendee);
return attendee;
};
/**
* Get all attendees or add attendees…
*
* @since 0.2.0
* @returns {ICalAttendees[]|ICalEvent}
*/
this.attendees = function(attendees) {
if(!attendees) {
return data.attendees;
}
var cal = this;
attendees.forEach(function(e) {
cal.createAttendee(e);
});
return cal;
};
/**
* Set/Get the event's method
*
......@@ -492,6 +544,11 @@ var ICalEvent= function(_data) {
g += 'ORGANIZER;CN="' + data.organizer.name.replace(/"/g, '\\"') + '":mailto:' + data.organizer.email + '\n';
}
// ATTENDEES
data.attendees.forEach(function(attendee) {
g += attendee.generate();
});
// URL
if(data.url) {
g += 'URL;VALUE=URI:' + data.url + '\n';
......@@ -511,7 +568,7 @@ var ICalEvent= function(_data) {
for(i in _data) {
if(_data.hasOwnProperty(i) && ['id', 'uid', 'start', 'end', 'stamp', 'timestamp', 'allDay', 'floating', 'repeating', 'summary', 'location', 'description', 'organizer', 'method', 'status', 'url'].indexOf(i) > -1) {
if(_data.hasOwnProperty(i) && ['id', 'uid', 'start', 'end', 'stamp', 'timestamp', 'allDay', 'floating', 'repeating', 'summary', 'location', 'description', 'organizer', 'attendees', 'method', 'status', 'url'].indexOf(i) > -1) {
this[i](_data[i]);
}
}
......
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//sebbo.net//ical-generator.tests//EN
BEGIN:VEVENT
UID:20131004T223930Z-123@sebbo.net
DTSTAMP:20131004T233453Z
DTSTART;VALUE=DATE:20131004
DTEND;VALUE=DATE:20131006
SUMMARY:Sample Event
ORGANIZER;CN="Sebastian Pekarek":mailto:mail@sebbo.net
ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;DELEGATED-FROM="matt@example.com";CN="John":MAILTO:john@example.com
ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=DELEGATED;DELEGATED-TO="john@example.com";CN="Matt":MAILTO:matt@example.com
URL;VALUE=URI:http://sebbo.net/
METHOD:ADD
STATUS:CONFIRMED
END:VEVENT
END:VCALENDAR
\ No newline at end of file
......@@ -142,7 +142,7 @@ describe('ical-generator 0.2.x / ICalCalendar', function() {
var cal = ical(),
ICalEvent = require('../lib/event.js');
assert.ok(cal, cal.createEvent() instanceof ICalEvent);
assert.ok(cal.createEvent() instanceof ICalEvent);
});
it('should pass data to instance', function() {
......@@ -163,18 +163,13 @@ describe('ical-generator 0.2.x / ICalCalendar', function() {
assert.deepEqual(cal.events()[0], event);
});
it('setter should add events and return reference to new instances', function() {
var cal = ical(), events;
it('setter should add events and return this', function() {
var cal = ical(), events, cal2;
assert.equal(cal.length(), 0);
events = cal.events([{summary: 'Event A'}, {summary: 'Event B'}]);
cal2 = cal.events([{summary: 'Event A'}, {summary: 'Event B'}]);
assert.equal(cal.length(), 2);
assert.deepEqual(events[0], cal.events()[0]);
assert.equal(events[0].summary(), 'Event A');
assert.deepEqual(events[1], cal.events()[1]);
assert.equal(events[1].summary(), 'Event B');
assert.deepEqual(cal2, cal);
});
});
......@@ -746,6 +741,59 @@ describe('ical-generator 0.2.x / ICalCalendar', function() {
});
});
describe('createAttendee()', function() {
it('should return a ICalAttendee instance', function() {
var cal = ical(),
event = cal.createEvent(),
ICalAttendee = require('../lib/attendee.js');
assert.ok(event.createAttendee() instanceof ICalAttendee);
});
it('should pass data to instance', function() {
var cal = ical(),
event = cal.createEvent(),
attendee = event.createAttendee({name: 'Zac'});
assert.equal(attendee.name(), 'Zac');
});
it('getter should return value', function() {
var a = ical().createEvent().createAttendee('Sebastian Pekarek <mail@example.com>');
assert.equal('Sebastian Pekarek', a.name());
assert.equal('mail@example.com', a.email());
});
it('should throw error when string misformated', function() {
var e = ical().createEvent();
assert.throws(function() {
e.createAttendee('foo bar');
}, /`attendee`/);
});
});
describe('attendees()', function() {
it('getter should return an array of attendees…', function() {
var cal = ical(),
event = cal.createEvent(),
attendee;
assert.equal(event.attendees().length, 0);
attendee = event.createAttendee();
assert.equal(event.attendees().length, 1);
assert.deepEqual(event.attendees()[0], attendee);
});
it('setter should add events and return this', function() {
var cal = ical(),
event = cal.createEvent(),
foo = event.attendees([{name: 'Person A'}, {name: 'Person B'}]);
assert.equal(event.attendees().length, 2);
assert.deepEqual(foo, event);
});
});
describe('method()', function() {
it('setter should return this', function() {
var e = ical().createEvent();
......@@ -963,6 +1011,263 @@ describe('ical-generator 0.2.x / ICalCalendar', function() {
/*jslint stupid: true */
assert.equal(cal.toString(), fs.readFileSync(__dirname + '/results/generate_05.ics', 'utf8'));
});
it('case #6 (attendee with simple delegation)', function() {
var cal = ical({domain: 'sebbo.net', prodId: '//sebbo.net//ical-generator.tests//EN'});
cal.createEvent({
id: '123',
start: new Date('Fr Oct 04 2013 22:39:30 UTC'),
end: new Date('Fr Oct 06 2013 23:15:00 UTC'),
allDay: true,
stamp: new Date('Fr Oct 04 2013 23:34:53 UTC'),
summary: 'Sample Event',
organizer: 'Sebastian Pekarek <mail@sebbo.net>',
attendees: [
{
name: 'Matt',
email: 'matt@example.com',
delegatesTo: {
name: 'John',
email: 'john@example.com',
status: 'accepted'
}
}
],
method: 'add',
status: 'confirmed',
url: 'http://sebbo.net/'
});
/*jslint stupid: true */
assert.equal(cal.toString(), fs.readFileSync(__dirname + '/results/generate_06.ics', 'utf8'));
});
});
});
describe('ICalAttendee', function() {
it('shouldn\'t work without event reference', function() {
var ICalAttendee = require('../lib/attendee.js');
assert.throws(function() {
new ICalAttendee({email: 'foo@bar.com'});
}, /`event`/);
});
describe('name()', function() {
it('setter should return this', function() {
var a = ical().createEvent().createAttendee();
assert.deepEqual(a, a.name('Sebastian'));
});
it('getter should return value', function() {
var e = ical().createEvent().createAttendee().name('Sebastian');
assert.equal(e.name(), 'Sebastian');
});
it('should change something', function() {
var cal = ical(),
event = cal.createEvent({
start: new Date(),
end: new Date(new Date().getTime() + 3600000),
summary: 'Example Event'