vpim 0.323 → 0.357
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.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
|
|