vpim 0.17 → 0.323
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/vpim/dirinfo.rb +11 -5
- data/lib/vpim/field.rb +26 -21
- data/lib/vpim/icalendar.rb +21 -105
- data/lib/vpim/maker/vcard.rb +31 -28
- data/lib/vpim/property/base.rb +63 -0
- data/lib/vpim/property/common.rb +159 -0
- data/lib/vpim/property/location.rb +29 -0
- data/lib/vpim/property/priority.rb +35 -0
- data/lib/vpim/property/resources.rb +16 -0
- data/lib/vpim/rfc2425.rb +21 -4
- data/lib/vpim/rrule.rb +1 -1
- data/lib/vpim/vcard.rb +5 -5
- data/lib/vpim/version.rb +16 -0
- data/lib/vpim/vevent.rb +54 -204
- data/lib/vpim/vpim.rb +10 -15
- data/lib/vpim.rb +14 -0
- metadata +9 -19
- data/lib/vpim/agent/plist.rb +0 -86
- data/lib/vpim/date.rb~ +0 -198
- data/lib/vpim/dirinfo.rb~ +0 -242
- data/lib/vpim/duration.rb~ +0 -121
- data/lib/vpim/enumerator.rb~ +0 -29
- data/lib/vpim/field.rb~ +0 -594
- data/lib/vpim/icalendar.rb~ +0 -548
- data/lib/vpim/maker/vcard.rb~ +0 -382
- data/lib/vpim/rfc2425.rb~ +0 -246
- data/lib/vpim/rrule.rb~ +0 -482
- data/lib/vpim/time.rb~ +0 -42
- data/lib/vpim/vcard.rb~ +0 -232
- data/lib/vpim/vevent.rb~ +0 -381
- data/lib/vpim/vpim.rb~ +0 -128
data/lib/vpim/vcard.rb~
DELETED
@@ -1,232 +0,0 @@
|
|
1
|
-
=begin
|
2
|
-
$Id: vcard.rb,v 1.13 2004/12/05 03:16:33 sam Exp $
|
3
|
-
|
4
|
-
Copyright (C) 2005 Sam Roberts
|
5
|
-
|
6
|
-
This library is free software; you can redistribute it and/or modify it
|
7
|
-
under the same terms as the ruby language itself, see the file COPYING for
|
8
|
-
details.
|
9
|
-
=end
|
10
|
-
|
11
|
-
require 'vpim/dirinfo'
|
12
|
-
require 'vpim/vpim'
|
13
|
-
|
14
|
-
module Vpim
|
15
|
-
# A vCard, a specialization of a directory info object.
|
16
|
-
#
|
17
|
-
# The vCard format is specified by:
|
18
|
-
# - RFC2426: vCard MIME Directory Profile (vCard 3.0)
|
19
|
-
# - RFC2425: A MIME Content-Type for Directory Information
|
20
|
-
#
|
21
|
-
# This implements vCard 3.0, but it is also capable of decoding vCard 2.1.
|
22
|
-
#
|
23
|
-
# For information about:
|
24
|
-
# - link:rfc2426.txt: vCard MIME Directory Profile (vCard 3.0)
|
25
|
-
# - link:rfc2425.txt: A MIME Content-Type for Directory Information
|
26
|
-
# - http://www.imc.org/pdi/pdiproddev.html: vCard 2.1 Specifications
|
27
|
-
#
|
28
|
-
# vCards are usually transmitted in files with <code>.vcf</code>
|
29
|
-
# extensions.
|
30
|
-
#
|
31
|
-
# TODO - an open question is what exactly "vcard 2.1" support means. While I
|
32
|
-
# decode vCard 2.1 correctly, I don't encode it. Should I implement a
|
33
|
-
# transcoder, so vCards can be decoded from either version, and then written
|
34
|
-
# to either version? Maybe an option to Field#encode()?
|
35
|
-
#
|
36
|
-
# TODO - there are very few methods that Vcard has that DirectoryInfo
|
37
|
-
# doesn't. I could probably just do away with it entirely, but I suspect
|
38
|
-
# that there are methods that could be usefully added to Vcard, perhaps to
|
39
|
-
# get the email addresses, or the name, or perhaps to set fields, like
|
40
|
-
# email=. What would be useful?
|
41
|
-
#
|
42
|
-
# = Example
|
43
|
-
#
|
44
|
-
# Here's an example of encoding a simple vCard using the low-level API:
|
45
|
-
#
|
46
|
-
# card = Vpim::Vcard.create
|
47
|
-
# card << Vpim::DirectoryInfo::Field.create('email', 'user.name@example.com', 'type' => 'internet' )
|
48
|
-
# card << Vpim::DirectoryInfo::Field.create('url', 'http://www.example.com/user' )
|
49
|
-
# card << Vpim::DirectoryInfo::Field.create('fn', 'User Name' )
|
50
|
-
# puts card.to_s
|
51
|
-
#
|
52
|
-
# New! Use the Vpim::Maker::Vcard to make vCards!
|
53
|
-
class Vcard < DirectoryInfo
|
54
|
-
|
55
|
-
private_class_method :new
|
56
|
-
|
57
|
-
# Create a vCard 3.0 object with the minimum required fields, plus any
|
58
|
-
# +fields+ you want in the card (they can also be added later).
|
59
|
-
def Vcard.create(fields = [] )
|
60
|
-
fields.unshift Field.create('VERSION', "3.0")
|
61
|
-
super(fields, 'VCARD')
|
62
|
-
end
|
63
|
-
|
64
|
-
# Decode a collection of vCards into an array of Vcard objects.
|
65
|
-
#
|
66
|
-
# +card+ can be either a String or an IO object.
|
67
|
-
#
|
68
|
-
# Since vCards are self-delimited (by a BEGIN:vCard and an END:vCard),
|
69
|
-
# multiple vCards can be concatenated into a single directory info object.
|
70
|
-
# They may or may not be related. For example, AddressBook.app (the OS X
|
71
|
-
# contact manager) will export multiple selected cards in this format.
|
72
|
-
#
|
73
|
-
# Input data will be converted from unicode if it is detected. The heuristic
|
74
|
-
# is based on the first bytes in the string:
|
75
|
-
# - 0xEF 0xBB 0xBF: UTF-8 with a BOM, the BOM is stripped
|
76
|
-
# - 0xFE 0xFF: UTF-16 with a BOM (big-endian), the BOM is stripped and string
|
77
|
-
# is converted to UTF-8
|
78
|
-
# - 0xFF 0xFE: UTF-16 with a BOM (little-endian), the BOM is stripped and string
|
79
|
-
# is converted to UTF-8
|
80
|
-
# - 0x00 'B' or 0x00 'b': UTF-16 (big-endian), the string is converted to UTF-8
|
81
|
-
# - 'B' 0x00 or 'b' 0x00: UTF-16 (little-endian), the string is converted to UTF-8
|
82
|
-
#
|
83
|
-
# If you know that you have only one vCard, then you can decode that
|
84
|
-
# single vCard by doing something like:
|
85
|
-
#
|
86
|
-
# vcard = Vcard.decode(card_data).first
|
87
|
-
#
|
88
|
-
# Note: Should the import encoding be remembered, so that it can be reencoded in
|
89
|
-
# the same format?
|
90
|
-
def Vcard.decode(card)
|
91
|
-
if card.respond_to? :to_str
|
92
|
-
string = card.to_str
|
93
|
-
elsif card.kind_of? IO
|
94
|
-
string = card.read(nil)
|
95
|
-
else
|
96
|
-
raise ArgumentError, "Vcard.decode cannot be called with a #{card.type}"
|
97
|
-
end
|
98
|
-
|
99
|
-
case string
|
100
|
-
when /^\xEF\xBB\xBF/
|
101
|
-
string = string.sub("\xEF\xBB\xBF", '')
|
102
|
-
when /^\xFE\xFF/
|
103
|
-
arr = string.unpack('n*')
|
104
|
-
arr.shift
|
105
|
-
string = arr.pack('U*')
|
106
|
-
when /^\xFF\xFE/
|
107
|
-
arr = string.unpack('v*')
|
108
|
-
arr.shift
|
109
|
-
string = arr.pack('U*')
|
110
|
-
when /^\x00\x62/i
|
111
|
-
string = string.unpack('n*').pack('U*')
|
112
|
-
when /^\x62\x00/i
|
113
|
-
string = string.unpack('v*').pack('U*')
|
114
|
-
end
|
115
|
-
|
116
|
-
entities = Vpim.expand(Vpim.decode(string))
|
117
|
-
|
118
|
-
# Since all vCards must have a begin/end, the top-level should consist
|
119
|
-
# entirely of entities/arrays, even if its a single vCard.
|
120
|
-
if entities.detect { |e| ! e.kind_of? Array }
|
121
|
-
raise "Not a valid vCard"
|
122
|
-
end
|
123
|
-
|
124
|
-
vcards = []
|
125
|
-
|
126
|
-
for e in entities
|
127
|
-
vcards.push(new(e.flatten, 'VCARD'))
|
128
|
-
end
|
129
|
-
|
130
|
-
vcards
|
131
|
-
end
|
132
|
-
|
133
|
-
# The vCard version multiplied by 10 as an Integer. If no VERSION field
|
134
|
-
# is present (which is non-conformant), nil is returned. For example, a
|
135
|
-
# version 2.1 vCard would have a version of 21, and a version 3.0 vCard
|
136
|
-
# would have a version of 30.
|
137
|
-
def version
|
138
|
-
v = self["version"]
|
139
|
-
unless v
|
140
|
-
raise Vpim::InvalidEncodingError, 'Invalid vCard - it has no version field!'
|
141
|
-
end
|
142
|
-
v = v.to_f * 10
|
143
|
-
v = v.to_i
|
144
|
-
end
|
145
|
-
|
146
|
-
# The value of the field named +name+, optionally limited to fields of
|
147
|
-
# type +type+. If no match is found, nil is returned, if multiple matches
|
148
|
-
# are found, the first match to have one of its type values be 'pref'
|
149
|
-
# (preferred) is returned, otherwise the first match is returned.
|
150
|
-
def [](name, type=nil)
|
151
|
-
fields = enum_by_name(name).find_all { |f| type == nil || f.type?(type) }
|
152
|
-
|
153
|
-
valued = fields.select { |f| f.value != '' }
|
154
|
-
if valued.first
|
155
|
-
fields = valued
|
156
|
-
end
|
157
|
-
|
158
|
-
# limit to preferred, if possible
|
159
|
-
pref = fields.select { |f| f.pref? }
|
160
|
-
|
161
|
-
if pref.first
|
162
|
-
fields = pref
|
163
|
-
end
|
164
|
-
|
165
|
-
fields.first ? fields.first.value : nil
|
166
|
-
end
|
167
|
-
|
168
|
-
=begin
|
169
|
-
TODO
|
170
|
-
def name
|
171
|
-
h = {} Create a Struct from the FN fields (maybe add the N field value, to?)
|
172
|
-
|
173
|
-
def h.to_s
|
174
|
-
concatenate the name fields nicely to make the name
|
175
|
-
end
|
176
|
-
end
|
177
|
-
=end
|
178
|
-
|
179
|
-
# The nickname or nil if there is none.
|
180
|
-
def nickname
|
181
|
-
nn = self['nickname']
|
182
|
-
if nn && nn == ''
|
183
|
-
nn = nil
|
184
|
-
end
|
185
|
-
nn
|
186
|
-
end
|
187
|
-
|
188
|
-
# All the nicknames, [] if there are none.
|
189
|
-
def nicknames
|
190
|
-
nn = self['nickname']
|
191
|
-
if nn && nn == ''
|
192
|
-
nn = nil
|
193
|
-
end
|
194
|
-
nn
|
195
|
-
end
|
196
|
-
|
197
|
-
# Returns the birthday as a Date on success, nil if there was no birthday
|
198
|
-
# field, and raises an error if the birthday field could not be expressed
|
199
|
-
# as a recurring event.
|
200
|
-
#
|
201
|
-
# Also, I have a lot of vCards with dates that look like:
|
202
|
-
# 678-09-23
|
203
|
-
# The 678 is garbage, but 09-23 is really the birthday. This substitutes the
|
204
|
-
# current year for the 3 digit year, and then converts to a Date.
|
205
|
-
def birthday
|
206
|
-
bday = self.field('BDAY')
|
207
|
-
|
208
|
-
return nil unless bday
|
209
|
-
|
210
|
-
begin
|
211
|
-
date = bday.to_date.first
|
212
|
-
|
213
|
-
rescue Vpim::InvalidEncodingError
|
214
|
-
if bday.value =~ /(\d+)-(\d+)-(\d+)/
|
215
|
-
y = $1.to_i
|
216
|
-
m = $2.to_i
|
217
|
-
d = $3.to_i
|
218
|
-
if(y < 1900)
|
219
|
-
y = Time.now.year
|
220
|
-
end
|
221
|
-
date = Date.new(y, m, d)
|
222
|
-
else
|
223
|
-
raise
|
224
|
-
end
|
225
|
-
end
|
226
|
-
|
227
|
-
date
|
228
|
-
end
|
229
|
-
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
data/lib/vpim/vevent.rb~
DELETED
@@ -1,381 +0,0 @@
|
|
1
|
-
=begin
|
2
|
-
$Id: vevent.rb,v 1.12 2005/01/21 04:09:55 sam Exp $
|
3
|
-
|
4
|
-
Copyright (C) 2005 Sam Roberts
|
5
|
-
|
6
|
-
This library is free software; you can redistribute it and/or modify it
|
7
|
-
under the same terms as the ruby language itself, see the file COPYING for
|
8
|
-
details.
|
9
|
-
=end
|
10
|
-
|
11
|
-
require 'vpim/dirinfo'
|
12
|
-
require 'vpim/field'
|
13
|
-
require 'vpim/rfc2425'
|
14
|
-
require 'vpim/vpim'
|
15
|
-
|
16
|
-
=begin
|
17
|
-
A vTodo that is done:
|
18
|
-
|
19
|
-
BEGIN:VTODO
|
20
|
-
COMPLETED:20040303T050000Z
|
21
|
-
DTSTAMP:20040304T011707Z
|
22
|
-
DTSTART;TZID=Canada/Eastern:20030524T115238
|
23
|
-
SEQUENCE:2
|
24
|
-
STATUS:COMPLETED
|
25
|
-
SUMMARY:Wash Car
|
26
|
-
UID:E7609713-8E13-11D7-8ACC-000393AD088C
|
27
|
-
END:VTODO
|
28
|
-
|
29
|
-
BEGIN:VTODO
|
30
|
-
DTSTAMP:20030909T015533Z
|
31
|
-
DTSTART;TZID=Canada/Eastern:20030808T000000
|
32
|
-
SEQUENCE:1
|
33
|
-
SUMMARY:Renew Passport
|
34
|
-
UID:EC76B256-BBE9-11D7-8401-000393AD088C
|
35
|
-
END:VTODO
|
36
|
-
|
37
|
-
|
38
|
-
=end
|
39
|
-
|
40
|
-
module Vpim
|
41
|
-
class Icalendar
|
42
|
-
class Vtodo
|
43
|
-
def initialize(fields) #:nodoc:
|
44
|
-
@fields = fields
|
45
|
-
|
46
|
-
outer, inner = Vpim.outer_inner(fields)
|
47
|
-
|
48
|
-
@properties = Vpim::DirectoryInfo.create(outer)
|
49
|
-
|
50
|
-
@elements = inner
|
51
|
-
|
52
|
-
# TODO - don't get properties here, put the accessor in a module,
|
53
|
-
# which can cache the results.
|
54
|
-
|
55
|
-
@summary = @properties.text('SUMMARY').first
|
56
|
-
@description = @properties.text('DESCRIPTION').first
|
57
|
-
@comment = @properties.text('COMMENT').first
|
58
|
-
@location = @properties.text('LOCATION').first
|
59
|
-
@status = @properties.text('STATUS').first
|
60
|
-
@uid = @properties.text('UID').first
|
61
|
-
@priority = @properties.text('PRIORITY').first
|
62
|
-
|
63
|
-
# See "TODO - fields" in dirinfo.rb
|
64
|
-
@dtstamp = @properties.field('dtstamp')
|
65
|
-
@dtstart = @properties.field('dtstart')
|
66
|
-
@dtend = @properties.field('dtend')
|
67
|
-
@duration = @properties.field('duration')
|
68
|
-
@due = @properties.field('due')
|
69
|
-
@rrule = @properties['rrule']
|
70
|
-
|
71
|
-
# Need to seperate status-handling out into a module...
|
72
|
-
@status_values = [ 'COMPLETED' ];
|
73
|
-
|
74
|
-
end
|
75
|
-
|
76
|
-
attr_reader :description, :summary, :comment, :location
|
77
|
-
attr_reader :properties, :fields # :nodoc:
|
78
|
-
|
79
|
-
# Create a new Vtodo object.
|
80
|
-
#
|
81
|
-
# If specified, +fields+ must be either an array of Field objects to
|
82
|
-
# add, or a Hash of String names to values that will be used to build
|
83
|
-
# Field objects. The latter is a convenient short-cut allowing the Field
|
84
|
-
# objects to be created for you when called like:
|
85
|
-
#
|
86
|
-
# Vtodo.create('SUMMARY' => "buy mangos")
|
87
|
-
#
|
88
|
-
# TODO - maybe todos are usually created in a particular way? I can
|
89
|
-
# make it easier. Ideally, I would like to make it hard to encode an invalid
|
90
|
-
# Event.
|
91
|
-
def Vtodo.create(fields=[])
|
92
|
-
di = DirectoryInfo.create([], 'VTODO')
|
93
|
-
|
94
|
-
Vpim::DirectoryInfo::Field.create_array(fields).each { |f| di.push_unique f }
|
95
|
-
|
96
|
-
new(di.to_a)
|
97
|
-
end
|
98
|
-
|
99
|
-
=begin
|
100
|
-
I think that the initialization shouldn't be done in the #initialize, so, for example,
|
101
|
-
@status = @properties.text('STATUS').first
|
102
|
-
should be in the method below.
|
103
|
-
|
104
|
-
That way, I can construct a Vtodo by just including a module for each field that is allowed
|
105
|
-
in a Vtodo, simply.
|
106
|
-
=end
|
107
|
-
def status
|
108
|
-
if(!@status); return nil; end
|
109
|
-
|
110
|
-
s = @status.upcase
|
111
|
-
|
112
|
-
unless @status_values.include?(s)
|
113
|
-
raise Vpim::InvalidEncodingError, "Invalid status '#{@status}'"
|
114
|
-
end
|
115
|
-
|
116
|
-
s
|
117
|
-
end
|
118
|
-
|
119
|
-
# +priority+ is a number from 1 to 9, with 1 being the highest and 0
|
120
|
-
# meaning "no priority", equivalent to not specifying the PRIORITY field.
|
121
|
-
# Other values are reserved by RFC2446.
|
122
|
-
def priority
|
123
|
-
p = @priority ? @priority.to_i : 0
|
124
|
-
|
125
|
-
if( p < 0 || p > 9 )
|
126
|
-
raise Vpim::InvalidEncodingError, 'Invalid priority #{@priority} - it must be 0-9!'
|
127
|
-
end
|
128
|
-
p
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
module Vpim
|
135
|
-
class Icalendar
|
136
|
-
class Vevent
|
137
|
-
def initialize(fields) #:nodoc:
|
138
|
-
@fields = fields
|
139
|
-
|
140
|
-
outer, inner = Vpim.outer_inner(fields)
|
141
|
-
|
142
|
-
@properties = Vpim::DirectoryInfo.create(outer)
|
143
|
-
|
144
|
-
@elements = inner
|
145
|
-
|
146
|
-
# TODO - don't get properties here, put the accessor in a module,
|
147
|
-
# which can cache the results.
|
148
|
-
|
149
|
-
@summary = @properties.text('SUMMARY').first
|
150
|
-
@description = @properties.text('DESCRIPTION').first
|
151
|
-
@comment = @properties.text('COMMENT').first
|
152
|
-
@location = @properties.text('LOCATION').first
|
153
|
-
@status = @properties.text('STATUS').first
|
154
|
-
@uid = @properties.text('UID').first
|
155
|
-
|
156
|
-
# See "TODO - fields" in dirinfo.rb
|
157
|
-
@dtstamp = @properties.field('dtstamp')
|
158
|
-
@dtstart = @properties.field('dtstart')
|
159
|
-
@dtend = @properties.field('dtend')
|
160
|
-
@duration = @properties.field('duration')
|
161
|
-
@rrule = @properties['rrule']
|
162
|
-
|
163
|
-
# Need to seperate status-handling out into a module...
|
164
|
-
@status_values = [ 'TENTATIVE', 'CONFIRMED', 'CANCELLED' ];
|
165
|
-
|
166
|
-
end
|
167
|
-
|
168
|
-
# Create a new Vevent object. All events must have a DTSTART field,
|
169
|
-
# specify it as either a Time or a Date in +start+, it defaults to "now"
|
170
|
-
# (is this useful?).
|
171
|
-
#
|
172
|
-
# If specified, +fields+ must be either an array of Field objects to
|
173
|
-
# add, or a Hash of String names to values that will be used to build
|
174
|
-
# Field objects. The latter is a convenient short-cut allowing the Field
|
175
|
-
# objects to be created for you when called like:
|
176
|
-
#
|
177
|
-
# Vevent.create(Date.today, 'SUMMARY' => "today's event")
|
178
|
-
#
|
179
|
-
# TODO - maybe events are usually created in a particular way? With a
|
180
|
-
# start/duration or a start/end? Maybe I can make it easier. Ideally, I
|
181
|
-
# would like to make it hard to encode an invalid Event.
|
182
|
-
def Vevent.create(start = Time.now, fields=[])
|
183
|
-
dtstart = DirectoryInfo::Field.create('DTSTART', start)
|
184
|
-
di = DirectoryInfo.create([ dtstart ], 'VEVENT')
|
185
|
-
|
186
|
-
Vpim::DirectoryInfo::Field.create_array(fields).each { |f| di.push_unique f }
|
187
|
-
|
188
|
-
new(di.to_a)
|
189
|
-
end
|
190
|
-
|
191
|
-
# Creates a yearly repeating event, such as for a birthday.
|
192
|
-
def Vevent.create_yearly(date, summary)
|
193
|
-
create(
|
194
|
-
date,
|
195
|
-
'SUMMARY' => summary.to_str,
|
196
|
-
'RRULE' => 'FREQ=YEARLY'
|
197
|
-
)
|
198
|
-
end
|
199
|
-
|
200
|
-
attr_reader :description, :summary, :comment, :location
|
201
|
-
attr_reader :properties, :fields # :nodoc:
|
202
|
-
|
203
|
-
#--
|
204
|
-
# The methods below should be shared, somehow, by all calendar components, not just Events.
|
205
|
-
#++
|
206
|
-
|
207
|
-
# Accept an event invitation. The +invitee+ is the Address that wishes
|
208
|
-
# to accept the event invitation as confirmed.
|
209
|
-
def accept(invitee)
|
210
|
-
# The event created is identical to this one, but
|
211
|
-
# - without the attendees
|
212
|
-
# - with the invitee added with a PARTSTAT of ACCEPTED
|
213
|
-
invitee = invitee.copy
|
214
|
-
invitee.partstat = 'ACCEPTED'
|
215
|
-
|
216
|
-
fields = []
|
217
|
-
|
218
|
-
@properties.each_with_index do
|
219
|
-
|f,i|
|
220
|
-
|
221
|
-
# put invitee in as field[1]
|
222
|
-
fields << invitee.field if i == 1
|
223
|
-
|
224
|
-
fields << f unless f.name? 'ATTENDEE'
|
225
|
-
end
|
226
|
-
|
227
|
-
Vevent.new(fields)
|
228
|
-
end
|
229
|
-
|
230
|
-
# Status values are not rejected during decoding. However, if the
|
231
|
-
# status is requested, and it's value is not one of the defined
|
232
|
-
# allowable values, an exception is raised.
|
233
|
-
def status
|
234
|
-
if(!@status); return nil; end
|
235
|
-
|
236
|
-
s = @status.upcase
|
237
|
-
|
238
|
-
unless @status_values.include?(s)
|
239
|
-
raise Vpim::InvalidEncodingError, "Invalid status '#{@status}'"
|
240
|
-
end
|
241
|
-
|
242
|
-
s
|
243
|
-
end
|
244
|
-
|
245
|
-
# TODO - def status? ...
|
246
|
-
|
247
|
-
# TODO - def status= ...
|
248
|
-
|
249
|
-
# The unique identifier of this calendar component, a string. It cannot be
|
250
|
-
# nil, if it is not found in the component, the calendar is malformed, and
|
251
|
-
# this method will raise an exception.
|
252
|
-
def uid
|
253
|
-
if(!@uid)
|
254
|
-
raise Vpim::InvalidEncodingError, 'Invalid component - no UID field was found!'
|
255
|
-
end
|
256
|
-
|
257
|
-
@uid
|
258
|
-
end
|
259
|
-
|
260
|
-
# The time stamp for this calendar component. Describe what this is....
|
261
|
-
# This field is required!
|
262
|
-
def dtstamp
|
263
|
-
if(!@dtstamp)
|
264
|
-
raise Vpim::InvalidEncodingError, 'Invalid component - no DTSTAMP field was found!'
|
265
|
-
end
|
266
|
-
|
267
|
-
@dtstamp.to_time.first
|
268
|
-
end
|
269
|
-
|
270
|
-
# The start time for this calendar component. Describe what this is....
|
271
|
-
# This field is required!
|
272
|
-
def dtstart
|
273
|
-
if(!@dtstart)
|
274
|
-
raise Vpim::InvalidEncodingError, 'Invalid component - no DTSTART field was found!'
|
275
|
-
end
|
276
|
-
|
277
|
-
@dtstart.to_time.first
|
278
|
-
end
|
279
|
-
|
280
|
-
=begin
|
281
|
-
# Set the start time for the event to +start+, a Time object.
|
282
|
-
# TODO - def dtstart=(start) ... start should be allowed to be Time/Date/DateTime
|
283
|
-
=end
|
284
|
-
|
285
|
-
# The duration in seconds of a Event, Todo, or Vfreebusy component, or
|
286
|
-
# for Alarms, the delay period prior to repeating the alarm. The
|
287
|
-
# duration is calculated from the DTEND and DTBEGIN fields if the
|
288
|
-
# DURATION field is not present. Durations of zero seconds are possible.
|
289
|
-
def duration
|
290
|
-
if(!@duration)
|
291
|
-
return nil unless @dtend
|
292
|
-
|
293
|
-
b = dtstart
|
294
|
-
e = dtend
|
295
|
-
|
296
|
-
return (e - b).to_i
|
297
|
-
end
|
298
|
-
|
299
|
-
Icalendar.decode_duration(@duration.value_raw)
|
300
|
-
end
|
301
|
-
|
302
|
-
# The end time for this calendar component. For an Event, if there is no
|
303
|
-
# end time, then nil is returned, and the event takes up no time.
|
304
|
-
# However, the end time will be calculated from the event duration, if
|
305
|
-
# present.
|
306
|
-
def dtend
|
307
|
-
if(@dtend)
|
308
|
-
@dtend.to_time.first
|
309
|
-
elsif duration
|
310
|
-
dtstart + duration
|
311
|
-
else
|
312
|
-
nil
|
313
|
-
end
|
314
|
-
end
|
315
|
-
|
316
|
-
# The recurrence rule, if any, for this event. Recurrence starts at the
|
317
|
-
# DTSTART time.
|
318
|
-
def rrule
|
319
|
-
@rrule
|
320
|
-
end
|
321
|
-
|
322
|
-
# The times this event occurs, as a Vpim::Rrule.
|
323
|
-
#
|
324
|
-
# Note: the event may occur only once.
|
325
|
-
#
|
326
|
-
# Note: occurences are currently calculated only from DTSTART and RRULE,
|
327
|
-
# no allowance for EXDATE or other fields is made.
|
328
|
-
def occurences
|
329
|
-
Vpim::Rrule.new(dtstart, @rrule)
|
330
|
-
end
|
331
|
-
|
332
|
-
# Check if this event overlaps with the time period later than or equal to +t0+, but
|
333
|
-
# earlier than +t1+.
|
334
|
-
def occurs_in?(t0, t1)
|
335
|
-
occurences.each_until(t1).detect { |t| tend = t + (duration || 0); tend > t0 }
|
336
|
-
end
|
337
|
-
|
338
|
-
# Return the event organizer, an object of Icalendar::Address (or nil if
|
339
|
-
# there is no ORGANIZER field).
|
340
|
-
#
|
341
|
-
# TODO - verify that it is illegal for there to be more than one
|
342
|
-
# ORGANIZER, if more than one is allowed, this needs to return an array.
|
343
|
-
def organizer
|
344
|
-
unless instance_variables.include? "@organizer"
|
345
|
-
@organizer = @properties.field('ORGANIZER')
|
346
|
-
|
347
|
-
if @organizer
|
348
|
-
@organizer = Icalendar::Address.new(@organizer)
|
349
|
-
end
|
350
|
-
end
|
351
|
-
@organizer.freeze
|
352
|
-
end
|
353
|
-
|
354
|
-
# Return an array of attendees, an empty array if there are none. The
|
355
|
-
# attendees are objects of Icalendar::Address. If +uri+ is specified
|
356
|
-
# only the return the attendees with this +uri+.
|
357
|
-
def attendees(uri = nil)
|
358
|
-
unless instance_variables.include? "@attendees"
|
359
|
-
@attendees = @properties.enum_by_name('ATTENDEE').map { |a| Icalendar::Address.new(a).freeze }
|
360
|
-
@attendees.freeze
|
361
|
-
end
|
362
|
-
if uri
|
363
|
-
@attendees.select { |a| a == uri } .freeze
|
364
|
-
else
|
365
|
-
@attendees
|
366
|
-
end
|
367
|
-
end
|
368
|
-
|
369
|
-
# Return true if the +uri+, usually a mailto: URI, is an attendee.
|
370
|
-
def attendee?(uri)
|
371
|
-
attendees.include? uri
|
372
|
-
end
|
373
|
-
|
374
|
-
# CONTACT - value is text, parameters are ALTREP and LANGUAGE.
|
375
|
-
#
|
376
|
-
# textual contact information, or an altrep referring to a URI pointing
|
377
|
-
# at a vCard or LDAP entry...
|
378
|
-
end
|
379
|
-
end
|
380
|
-
end
|
381
|
-
|