vpim 0.323 → 0.357
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/vpim.rb +1 -2
- data/lib/vpim/address.rb +181 -0
- data/lib/vpim/attachment.rb +102 -0
- data/lib/vpim/dirinfo.rb +12 -2
- data/lib/vpim/field.rb +26 -14
- data/lib/vpim/icalendar.rb +54 -200
- data/lib/vpim/maker/vcard.rb +2 -404
- data/lib/vpim/property/base.rb +22 -0
- data/lib/vpim/property/common.rb +26 -5
- data/lib/vpim/property/location.rb +10 -1
- data/lib/vpim/property/priority.rb +8 -0
- data/lib/vpim/property/recurrence.rb +47 -0
- data/lib/vpim/property/resources.rb +8 -0
- data/lib/vpim/rfc2425.rb +44 -7
- data/lib/vpim/rrule.rb +1 -1
- data/lib/vpim/vcard.rb +1230 -106
- data/lib/vpim/version.rb +3 -1
- data/lib/vpim/vevent.rb +2 -90
- data/lib/vpim/vjournal.rb +46 -0
- data/lib/vpim/vpim.rb +15 -73
- data/lib/vpim/vtodo.rb +82 -0
- metadata +7 -2
data/lib/vpim.rb
CHANGED
data/lib/vpim/address.rb
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2006 Sam Roberts
|
3
|
+
|
4
|
+
This library is free software; you can redistribute it and/or modify it
|
5
|
+
under the same terms as the ruby language itself, see the file COPYING for
|
6
|
+
details.
|
7
|
+
=end
|
8
|
+
|
9
|
+
=begin
|
10
|
+
|
11
|
+
Notes on a CAL-ADDRESS
|
12
|
+
|
13
|
+
When used with ATTENDEE, the parameters are:
|
14
|
+
CN
|
15
|
+
CUTYPE
|
16
|
+
DELEGATED-FROM
|
17
|
+
DELEGATED-TO
|
18
|
+
DIR
|
19
|
+
LANGUAGE
|
20
|
+
MEMBER
|
21
|
+
PARTSTAT
|
22
|
+
ROLE
|
23
|
+
RSVP
|
24
|
+
SENT-BY
|
25
|
+
|
26
|
+
When used with ORGANIZER, the parameters are:
|
27
|
+
CN
|
28
|
+
DIR
|
29
|
+
LANGUAGE
|
30
|
+
SENT-BY
|
31
|
+
|
32
|
+
|
33
|
+
What I've seen in Notes invitations, and iCal responses:
|
34
|
+
ROLE
|
35
|
+
PARTSTAT
|
36
|
+
RSVP
|
37
|
+
CN
|
38
|
+
|
39
|
+
Support these last 4, for now.
|
40
|
+
|
41
|
+
=end
|
42
|
+
|
43
|
+
module Vpim
|
44
|
+
class Icalendar
|
45
|
+
# Used to represent calendar fields containing CAL-ADDRESS values.
|
46
|
+
# The organizer or the attendees of a calendar event are examples of such
|
47
|
+
# a field.
|
48
|
+
#
|
49
|
+
# Example:
|
50
|
+
# ORGANIZER;CN="A. Person":mailto:a_person@example.com
|
51
|
+
# ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION
|
52
|
+
# ;CN="Sam Roberts";RSVP=TRUE:mailto:SRoberts@example.com
|
53
|
+
#
|
54
|
+
class Address
|
55
|
+
|
56
|
+
# Create an Address from a DirectoryInfo::Field, +field+.
|
57
|
+
#
|
58
|
+
# TODO - make private, and split into the encode/decode/create trinity.
|
59
|
+
def initialize(field)
|
60
|
+
unless field.value
|
61
|
+
raise ArgumentError
|
62
|
+
end
|
63
|
+
|
64
|
+
@field = field
|
65
|
+
end
|
66
|
+
|
67
|
+
# Return a representation of this Address as a DirectoryInfo::Field.
|
68
|
+
def field
|
69
|
+
@field.copy
|
70
|
+
end
|
71
|
+
|
72
|
+
# Create a copy of Address. If the original Address was frozen, this one
|
73
|
+
# won't be.
|
74
|
+
def copy
|
75
|
+
Marshal.load(Marshal.dump(self))
|
76
|
+
end
|
77
|
+
|
78
|
+
# Addresses in a CAL-ADDRESS are represented as a URI, usually a mailto URI.
|
79
|
+
def uri
|
80
|
+
@field.value
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return true if the +uri+ is == to this address' URI. The comparison
|
84
|
+
# is case-insensitive.
|
85
|
+
#
|
86
|
+
# FIXME - why case insensitive? Email addresses. Should use a URI library
|
87
|
+
# if I can find one and it knows how to do URI comparisons.
|
88
|
+
def ==(uri)
|
89
|
+
Vpim::Methods.casecmp?(self.uri.to_str, uri.to_str)
|
90
|
+
end
|
91
|
+
|
92
|
+
# The common or displayable name associated with the calendar address,
|
93
|
+
# or nil if there is none.
|
94
|
+
def cn
|
95
|
+
return nil unless n = @field.param('CN')
|
96
|
+
|
97
|
+
# FIXME = the CN param may have no value, which is an error, but don't try
|
98
|
+
# to decode it, return either nil, or InvalidEncoding
|
99
|
+
Vpim.decode_text(n.first)
|
100
|
+
end
|
101
|
+
|
102
|
+
# A string representation of an address, using the common name, and the
|
103
|
+
# URI. The URI protocol is stripped if it's "mailto:".
|
104
|
+
#
|
105
|
+
# TODO - this needs to properly escape the cn string!
|
106
|
+
def to_s
|
107
|
+
u = uri
|
108
|
+
u.gsub!(/^mailto: */i, '')
|
109
|
+
|
110
|
+
if cn
|
111
|
+
"\"#{cn}\" <#{uri}>"
|
112
|
+
else
|
113
|
+
uri
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def inspect
|
118
|
+
"#<Vpim::Icalendar::Address:cn=#{cn.inspect} status=#{partstat} rsvp?=#{rsvp} #{uri.inspect}>"
|
119
|
+
end
|
120
|
+
|
121
|
+
# The participation role for the calendar user specified by the address.
|
122
|
+
#
|
123
|
+
# The standard roles are:
|
124
|
+
# - CHAIR Indicates chair of the calendar entity
|
125
|
+
# - REQ-PARTICIPANT Indicates a participant whose participation is required
|
126
|
+
# - OPT-PARTICIPANT Indicates a participant whose participation is optional
|
127
|
+
# - NON-PARTICIPANT Indicates a participant who is copied for information purposes only
|
128
|
+
#
|
129
|
+
# The default role is REQ-PARTICIPANT, returned if no ROLE parameter was
|
130
|
+
# specified.
|
131
|
+
def role
|
132
|
+
return 'REQ-PARTICIPANT' unless r = @field.param('ROLE')
|
133
|
+
r.first.upcase
|
134
|
+
end
|
135
|
+
|
136
|
+
# The participation status for the calendar user specified by the
|
137
|
+
# property PARTSTAT, a String.
|
138
|
+
#
|
139
|
+
# These are the participation statuses for an Event:
|
140
|
+
# - NEEDS-ACTION Event needs action
|
141
|
+
# - ACCEPTED Event accepted
|
142
|
+
# - DECLINED Event declined
|
143
|
+
# - TENTATIVE Event tentatively accepted
|
144
|
+
# - DELEGATED Event delegated
|
145
|
+
#
|
146
|
+
# Default is NEEDS-ACTION.
|
147
|
+
#
|
148
|
+
# TODO - make the default depend on the component type.
|
149
|
+
def partstat
|
150
|
+
return 'NEEDS-ACTION' unless r = @field.param('PARTSTAT')
|
151
|
+
r.first.upcase
|
152
|
+
end
|
153
|
+
|
154
|
+
# Set or change the participation status of the address, the PARTSTAT,
|
155
|
+
# to +status+.
|
156
|
+
#
|
157
|
+
# See #partstat.
|
158
|
+
def partstat=(status)
|
159
|
+
@field['PARTSTAT'] = status.to_str
|
160
|
+
status
|
161
|
+
end
|
162
|
+
|
163
|
+
# The value of the RSVP field, either +true+ or +false+. It is used to
|
164
|
+
# specify whether there is an expectation of a favor of a reply from the
|
165
|
+
# calendar user specified by the property value.
|
166
|
+
#
|
167
|
+
# TODO - should be #rsvp?
|
168
|
+
def rsvp
|
169
|
+
return false unless r = @field.param('RSVP')
|
170
|
+
r = r.first
|
171
|
+
return false unless r
|
172
|
+
case r
|
173
|
+
when /TRUE/i then true
|
174
|
+
when /FALSE/i then false
|
175
|
+
else raise InvalidEncodingError, "RSVP param value not TRUE/FALSE: #{r}"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
@@ -0,0 +1,102 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2006 Sam Roberts
|
3
|
+
|
4
|
+
This library is free software; you can redistribute it and/or modify it
|
5
|
+
under the same terms as the ruby language itself, see the file COPYING for
|
6
|
+
details.
|
7
|
+
=end
|
8
|
+
|
9
|
+
require 'vpim/icalendar'
|
10
|
+
|
11
|
+
module Vpim
|
12
|
+
|
13
|
+
# Attachments are used by both iCalendar and vCard. They are either a URI or
|
14
|
+
# inline data, and their decoded value will be either a Uri or a Inline, as
|
15
|
+
# appropriate.
|
16
|
+
#
|
17
|
+
# Besides the methods specific to their class, both kinds of object implement
|
18
|
+
# a set of common methods, allowing them to be treated uniformly:
|
19
|
+
# - Uri#to_io, Inline#to_io: return an IO from which the value can be read.
|
20
|
+
# - Uri#to_s, Inline#to_s: return the value as a String.
|
21
|
+
# - Uri#format, Inline#format: the format of the value. This is supposed to
|
22
|
+
# be an "iana defined" identifier (like "image/jpeg"), but could be almost
|
23
|
+
# anything (or nothing) in practice. Since the parameter is optional, it may
|
24
|
+
# be "".
|
25
|
+
#
|
26
|
+
# The objects can also be distinguished by their class, if necessary.
|
27
|
+
module Attachment
|
28
|
+
|
29
|
+
# TODO - It might be possible to autodetect the format from the first few
|
30
|
+
# bytes of the value, and return the appropriate MIME type when format
|
31
|
+
# isn't defined.
|
32
|
+
#
|
33
|
+
# iCalendar and vCard put the format in different parameters, and the
|
34
|
+
# default kind of value is different.
|
35
|
+
def Attachment.decode(field, defkind, fmtparam) #:nodoc:
|
36
|
+
format = field.pvalue(fmtparam) || ''
|
37
|
+
kind = field.kind || defkind
|
38
|
+
case kind
|
39
|
+
when 'text'
|
40
|
+
Inline.new(Vpim.decode_text(field.value), format)
|
41
|
+
when 'uri'
|
42
|
+
Uri.new(field.value_raw, format)
|
43
|
+
when 'binary'
|
44
|
+
Inline.new(field.value, format)
|
45
|
+
else
|
46
|
+
raise InvalidEncodingError, "Attachment of type #{kind} is not allowed"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Extends a String to support some of the same methods as Uri.
|
51
|
+
class Inline < String
|
52
|
+
def initialize(s, format) #:nodoc:
|
53
|
+
@format = format
|
54
|
+
super(s)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Return an IO object for the inline data. See +stringio+ for more
|
58
|
+
# information.
|
59
|
+
def to_io
|
60
|
+
StringIO.new(self)
|
61
|
+
end
|
62
|
+
|
63
|
+
# The format of the inline data.
|
64
|
+
# See Attachment.
|
65
|
+
attr_reader :format
|
66
|
+
end
|
67
|
+
|
68
|
+
# Encapsulates a URI and implements some methods of String.
|
69
|
+
class Uri
|
70
|
+
def initialize(uri, format) #:nodoc:
|
71
|
+
@uri = uri
|
72
|
+
@format = format
|
73
|
+
end
|
74
|
+
|
75
|
+
# The URI value.
|
76
|
+
attr_reader :uri
|
77
|
+
|
78
|
+
# The format of the data referred to by the URI.
|
79
|
+
# See Attachment.
|
80
|
+
attr_reader :format
|
81
|
+
|
82
|
+
# Return an IO object from opening the URI. See +open-uri+ for more
|
83
|
+
# information.
|
84
|
+
def to_io
|
85
|
+
open(@uri)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Return the String from reading the IO object to end-of-data.
|
89
|
+
def to_s
|
90
|
+
to_io.read(nil)
|
91
|
+
end
|
92
|
+
|
93
|
+
def inspect #:nodoc:
|
94
|
+
s = "<#{self.class.to_s}: #{uri.inspect}>"
|
95
|
+
s << ", #{@format.inspect}" if @format
|
96
|
+
s
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
data/lib/vpim/dirinfo.rb
CHANGED
@@ -21,6 +21,16 @@ module Vpim
|
|
21
21
|
# A vCard, for example, is a specialization of a directory info object.
|
22
22
|
#
|
23
23
|
# - [RFC2425] the directory information framework (ftp://ftp.ietf.org/rfc/rfc2425.txt)
|
24
|
+
#
|
25
|
+
# Here's an example of encoding a simple vCard using the low-level APIs:
|
26
|
+
#
|
27
|
+
# card = Vpim::Vcard.create
|
28
|
+
# card << Vpim::DirectoryInfo::Field.create('EMAIL', 'user.name@example.com', 'TYPE' => 'INTERNET' )
|
29
|
+
# card << Vpim::DirectoryInfo::Field.create('URL', 'http://www.example.com/user' )
|
30
|
+
# card << Vpim::DirectoryInfo::Field.create('FN', 'User Name' )
|
31
|
+
# puts card.to_s
|
32
|
+
#
|
33
|
+
# Don't do it like that, use Vpim::Vcard::Maker.
|
24
34
|
class DirectoryInfo
|
25
35
|
include Enumerable
|
26
36
|
|
@@ -215,8 +225,8 @@ module Vpim
|
|
215
225
|
# profile-specific fields can be deleted, including mandatory ones. For
|
216
226
|
# vCards in particular, in order to avoid destroying them, I suggest
|
217
227
|
# creating a new Vcard, and copying over all the fields that you still
|
218
|
-
# want, rather than using #delete. This is easy with Maker
|
219
|
-
# the Maker
|
228
|
+
# want, rather than using #delete. This is easy with Vcard::Maker#copy, see
|
229
|
+
# the Vcard::Maker examples.
|
220
230
|
def delete(field)
|
221
231
|
case
|
222
232
|
when field.name?('BEGIN'), field.name?('END')
|
data/lib/vpim/field.rb
CHANGED
@@ -15,19 +15,18 @@ module Vpim
|
|
15
15
|
class DirectoryInfo
|
16
16
|
|
17
17
|
# A field in a directory info object.
|
18
|
-
#
|
19
|
-
# TODO
|
20
|
-
# - Field should know which param values and field vales are
|
21
|
-
# case-insensitive, configurably, so it can down case them
|
22
|
-
# - perhaps should have pvalue_set/del/add, perhaps case-insensitive, or
|
23
|
-
# pvalue_iset/idel/iadd, where set sets them all, add adds if not present,
|
24
|
-
# and del deletes any that are present
|
25
|
-
# - I really, really, need a case-insensitive string...
|
26
|
-
# - should allow nil as a field value, its not the same as '', if there is
|
27
|
-
# more than one pvalue, the empty string will show up. This isn't strictly
|
28
|
-
# disallowed, but its odd. Should also strip empty strings on decoding, if
|
29
|
-
# I don't already.
|
30
18
|
class Field
|
19
|
+
# TODO
|
20
|
+
# - Field should know which param values and field values are
|
21
|
+
# case-insensitive, configurably, so it can down case them
|
22
|
+
# - perhaps should have pvalue_set/del/add, perhaps case-insensitive, or
|
23
|
+
# pvalue_iset/idel/iadd, where set sets them all, add adds if not present,
|
24
|
+
# and del deletes any that are present
|
25
|
+
# - I really, really, need a case-insensitive string...
|
26
|
+
# - should allow nil as a field value, its not the same as '', if there is
|
27
|
+
# more than one pvalue, the empty string will show up. This isn't strictly
|
28
|
+
# disallowed, but its odd. Should also strip empty strings on decoding, if
|
29
|
+
# I don't already.
|
31
30
|
private_class_method :new
|
32
31
|
|
33
32
|
def Field.create_array(fields)
|
@@ -263,6 +262,19 @@ module Vpim
|
|
263
262
|
# FIXME - remove my own uses of #params
|
264
263
|
alias params pnames # :nodoc:
|
265
264
|
|
265
|
+
# The first value of the param +name+, nil if there is no such param,
|
266
|
+
# the param has no value, or the first param value is zero-length.
|
267
|
+
def pvalue(name)
|
268
|
+
v = pvalues( name )
|
269
|
+
if v
|
270
|
+
v = v.first
|
271
|
+
end
|
272
|
+
if v
|
273
|
+
v = nil unless v.length > 0
|
274
|
+
end
|
275
|
+
v
|
276
|
+
end
|
277
|
+
|
266
278
|
# The Array of all values of the param +name+, nil if there is no such
|
267
279
|
# param, [] if the param has no values. If the Field isn't frozen, the
|
268
280
|
# Array is mutable.
|
@@ -402,9 +414,9 @@ module Vpim
|
|
402
414
|
if v.size > 1
|
403
415
|
raise InvalidEncodingError, "multi-valued param 'VALUE' (#{values})"
|
404
416
|
end
|
405
|
-
v = v.first
|
417
|
+
v = v.first.downcase
|
406
418
|
end
|
407
|
-
v
|
419
|
+
v
|
408
420
|
end
|
409
421
|
|
410
422
|
# The value as an array of Time objects (all times and dates in
|
data/lib/vpim/icalendar.rb
CHANGED
@@ -10,6 +10,8 @@ require 'vpim/rfc2425'
|
|
10
10
|
require 'vpim/dirinfo'
|
11
11
|
require 'vpim/rrule'
|
12
12
|
require 'vpim/vevent'
|
13
|
+
require 'vpim/vtodo'
|
14
|
+
require 'vpim/vjournal'
|
13
15
|
require 'vpim/vpim'
|
14
16
|
|
15
17
|
module Vpim
|
@@ -64,26 +66,24 @@ module Vpim
|
|
64
66
|
@properties = DirectoryInfo.create(outer)
|
65
67
|
@properties.check_begin_end('VCALENDAR')
|
66
68
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
69
|
+
@components = []
|
70
|
+
|
71
|
+
factory = {
|
72
|
+
'VEVENT' => Vevent,
|
73
|
+
'VTODO' => Vtodo,
|
74
|
+
'VJOURNAL' => Vjournal,
|
75
|
+
}
|
72
76
|
|
73
77
|
inner.each do |component|
|
74
|
-
# First field in every component should be a "BEGIN:".
|
75
78
|
name = component.first
|
76
|
-
|
79
|
+
unless name.name? 'BEGIN'
|
77
80
|
raise InvalidEncodingError, "calendar component begins with #{name.name}, instead of BEGIN!"
|
78
81
|
end
|
79
82
|
|
80
|
-
name = name.value
|
83
|
+
name = name.value
|
81
84
|
|
82
|
-
|
83
|
-
|
84
|
-
when 'VTODO' then @vtodos << Vtodo.new(component)
|
85
|
-
when 'VJOURNAL' then @vjournals << Vjournal.new(component)
|
86
|
-
else @others << component
|
85
|
+
if klass = factory[name]
|
86
|
+
@components << klass.new(component)
|
87
87
|
end
|
88
88
|
end
|
89
89
|
end
|
@@ -141,9 +141,7 @@ module Vpim
|
|
141
141
|
|
142
142
|
last = fields.pop
|
143
143
|
|
144
|
-
@
|
145
|
-
@vtodos.each { |c| fields << c.fields }
|
146
|
-
@others.each { |c| fields << c.fields }
|
144
|
+
@components.each { |c| fields << c.fields }
|
147
145
|
|
148
146
|
fields << last
|
149
147
|
end
|
@@ -153,14 +151,10 @@ module Vpim
|
|
153
151
|
# Push a calendar component onto the calendar.
|
154
152
|
def push(component)
|
155
153
|
case component
|
156
|
-
when Vevent
|
157
|
-
@
|
158
|
-
when Vtodo
|
159
|
-
@vtodos << component
|
160
|
-
when Vjournal
|
161
|
-
@vjournals << component
|
154
|
+
when Vevent, Vtodo, Vjournal
|
155
|
+
@components << component
|
162
156
|
else
|
163
|
-
raise ArgumentError, "can't add
|
157
|
+
raise ArgumentError, "can't add a #{component.type} to a calendar"
|
164
158
|
end
|
165
159
|
end
|
166
160
|
|
@@ -270,196 +264,56 @@ module Vpim
|
|
270
264
|
m ? m.upcase : m
|
271
265
|
end
|
272
266
|
|
273
|
-
# The
|
267
|
+
# The value of the CALSCALE: property, or "GREGORIAN" if CALSCALE: is not
|
268
|
+
# present.
|
274
269
|
#
|
275
|
-
#
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
# The array of all calendar todo components (each is a Vtodo).
|
281
|
-
def todos
|
282
|
-
@vtodos
|
270
|
+
# This is of academic interest, really because there aren't any other
|
271
|
+
# calendar scales defined, and given that its hard enough just dealing with
|
272
|
+
# Gregorian calendars, there probably won't be.
|
273
|
+
def calscale
|
274
|
+
proptext('CALSCALE') || 'GREGORIAN'
|
283
275
|
end
|
284
276
|
|
285
|
-
# The array of all calendar
|
286
|
-
|
287
|
-
@vjournals
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
end
|
292
|
-
|
293
|
-
=begin
|
294
|
-
|
295
|
-
Notes on a CAL-ADDRESS
|
296
|
-
|
297
|
-
When used with ATTENDEE, the parameters are:
|
298
|
-
CN
|
299
|
-
CUTYPE
|
300
|
-
DELEGATED-FROM
|
301
|
-
DELEGATED-TO
|
302
|
-
DIR
|
303
|
-
LANGUAGE
|
304
|
-
MEMBER
|
305
|
-
PARTSTAT
|
306
|
-
ROLE
|
307
|
-
RSVP
|
308
|
-
SENT-BY
|
309
|
-
|
310
|
-
When used with ORGANIZER, the parameters are:
|
311
|
-
CN
|
312
|
-
DIR
|
313
|
-
LANGUAGE
|
314
|
-
SENT-BY
|
315
|
-
|
316
|
-
|
317
|
-
What I've seen in Notes invitations, and iCal responses:
|
318
|
-
ROLE
|
319
|
-
PARTSTAT
|
320
|
-
RSVP
|
321
|
-
CN
|
322
|
-
|
323
|
-
Support these last 4, for now.
|
324
|
-
|
325
|
-
=end
|
326
|
-
|
327
|
-
module Vpim
|
328
|
-
class Icalendar
|
329
|
-
# Used to represent calendar fields containing CAL-ADDRESS values.
|
330
|
-
# The organizer or the attendees of a calendar event are examples of such
|
331
|
-
# a field.
|
277
|
+
# The array of all supported calendar components. If a class is provided,
|
278
|
+
# return only the components of that class.
|
332
279
|
#
|
333
|
-
#
|
334
|
-
# ORGANIZER;CN="A. Person":mailto:a_person@example.com
|
335
|
-
# ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION
|
336
|
-
# ;CN="Sam Roberts";RSVP=TRUE:mailto:SRoberts@example.com
|
280
|
+
# If a block is provided, yield the components instead of returning them.
|
337
281
|
#
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
end
|
350
|
-
|
351
|
-
# Return a representation of this Address as a DirectoryInfo::Field.
|
352
|
-
def field
|
353
|
-
@field.copy
|
354
|
-
end
|
355
|
-
|
356
|
-
# Create a copy of Address. If the original Address was frozen, this one
|
357
|
-
# won't be.
|
358
|
-
def copy
|
359
|
-
Marshal.load(Marshal.dump(self))
|
360
|
-
end
|
361
|
-
|
362
|
-
# Addresses in a CAL-ADDRESS are represented as a URI, usually a mailto URI.
|
363
|
-
def uri
|
364
|
-
@field.value
|
365
|
-
end
|
366
|
-
|
367
|
-
# Return true if the +uri+ is == to this address' URI. The comparison
|
368
|
-
# is case-insensitive.
|
369
|
-
#
|
370
|
-
# FIXME - why case insensitive? Email addresses. Should use a URI library
|
371
|
-
# if I can find one and it knows how to do URI comparisons.
|
372
|
-
def ==(uri)
|
373
|
-
Vpim::Methods.casecmp?(self.uri.to_str, uri.to_str)
|
374
|
-
end
|
375
|
-
|
376
|
-
# The common or displayable name associated with the calendar address,
|
377
|
-
# or nil if there is none.
|
378
|
-
def cn
|
379
|
-
return nil unless n = @field.param('CN')
|
282
|
+
# Examples:
|
283
|
+
# calendar.components(Vpim::Icalendar::Vevent)
|
284
|
+
# => array of all calendar components
|
285
|
+
#
|
286
|
+
# calendar.components(Vpim::Icalendar::Vtodo) {|c| c... }
|
287
|
+
# => yield all todo components
|
288
|
+
#
|
289
|
+
# calendar.components {|c| c... }
|
290
|
+
# => yield all components
|
291
|
+
def components(klass=Object) #:yields:component
|
292
|
+
# TODO - should this take an interval: t0,t1?
|
380
293
|
|
381
|
-
|
382
|
-
|
383
|
-
Vpim.decode_text(n.first)
|
294
|
+
unless block_given?
|
295
|
+
return @components.select{|c| klass === c}.freeze
|
384
296
|
end
|
385
297
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
# TODO - this needs to properly escape the cn string!
|
390
|
-
def to_s
|
391
|
-
u = uri
|
392
|
-
u.gsub!(/^mailto: */i, '')
|
393
|
-
|
394
|
-
if cn
|
395
|
-
"\"#{cn}\" <#{uri}>"
|
396
|
-
else
|
397
|
-
uri
|
298
|
+
@components.each do |c|
|
299
|
+
if klass === c
|
300
|
+
yield c
|
398
301
|
end
|
399
302
|
end
|
303
|
+
self
|
304
|
+
end
|
400
305
|
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
# The participation role for the calendar user specified by the address.
|
406
|
-
#
|
407
|
-
# The standard roles are:
|
408
|
-
# - CHAIR Indicates chair of the calendar entity
|
409
|
-
# - REQ-PARTICIPANT Indicates a participant whose participation is required
|
410
|
-
# - OPT-PARTICIPANT Indicates a participant whose participation is optional
|
411
|
-
# - NON-PARTICIPANT Indicates a participant who is copied for information purposes only
|
412
|
-
#
|
413
|
-
# The default role is REQ-PARTICIPANT, returned if no ROLE parameter was
|
414
|
-
# specified.
|
415
|
-
def role
|
416
|
-
return 'REQ-PARTICIPANT' unless r = @field.param('ROLE')
|
417
|
-
r.first.upcase
|
418
|
-
end
|
419
|
-
|
420
|
-
# The participation status for the calendar user specified by the
|
421
|
-
# property PARTSTAT, a String.
|
422
|
-
#
|
423
|
-
# These are the participation statuses for an Event:
|
424
|
-
# - NEEDS-ACTION Event needs action
|
425
|
-
# - ACCEPTED Event accepted
|
426
|
-
# - DECLINED Event declined
|
427
|
-
# - TENTATIVE Event tentatively accepted
|
428
|
-
# - DELEGATED Event delegated
|
429
|
-
#
|
430
|
-
# Default is NEEDS-ACTION.
|
431
|
-
#
|
432
|
-
# TODO - make the default depend on the component type.
|
433
|
-
def partstat
|
434
|
-
return 'NEEDS-ACTION' unless r = @field.param('PARTSTAT')
|
435
|
-
r.first.upcase
|
436
|
-
end
|
437
|
-
|
438
|
-
# Set or change the participation status of the address, the PARTSTAT,
|
439
|
-
# to +status+.
|
440
|
-
#
|
441
|
-
# See #partstat.
|
442
|
-
def partstat=(status)
|
443
|
-
@field['PARTSTAT'] = status.to_str
|
444
|
-
status
|
445
|
-
end
|
306
|
+
# For backwards compatibility. Use #components.
|
307
|
+
def events #:nodoc:
|
308
|
+
components Icalendar::Vevent
|
309
|
+
end
|
446
310
|
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
#
|
451
|
-
# TODO - should be #rsvp?
|
452
|
-
def rsvp
|
453
|
-
return false unless r = @field.param('RSVP')
|
454
|
-
r = r.first
|
455
|
-
return false unless r
|
456
|
-
case r
|
457
|
-
when /TRUE/i then true
|
458
|
-
when /FALSE/i then false
|
459
|
-
else raise InvalidEncodingError, "RSVP param value not TRUE/FALSE: #{r}"
|
460
|
-
end
|
461
|
-
end
|
311
|
+
# For backwards compatibility. Use #components.
|
312
|
+
def todos #:nodoc:
|
313
|
+
components Icalendar::Vtodo
|
462
314
|
end
|
315
|
+
|
463
316
|
end
|
317
|
+
|
464
318
|
end
|
465
319
|
|