vpim 0.357 → 0.360
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/vpim/address.rb +125 -87
- data/lib/vpim/icalendar.rb +37 -20
- data/lib/vpim/property/base.rb +80 -2
- data/lib/vpim/property/common.rb +137 -2
- data/lib/vpim/rfc2425.rb +37 -1
- data/lib/vpim/vcard.rb +6 -13
- data/lib/vpim/version.rb +2 -2
- data/lib/vpim/vevent.rb +73 -9
- metadata +5 -5
data/lib/vpim/address.rb
CHANGED
@@ -47,77 +47,31 @@ module Vpim
|
|
47
47
|
# a field.
|
48
48
|
#
|
49
49
|
# Example:
|
50
|
+
#
|
50
51
|
# ORGANIZER;CN="A. Person":mailto:a_person@example.com
|
52
|
+
#
|
51
53
|
# ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION
|
52
54
|
# ;CN="Sam Roberts";RSVP=TRUE:mailto:SRoberts@example.com
|
53
55
|
#
|
54
56
|
class Address
|
55
57
|
|
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
58
|
# Create a copy of Address. If the original Address was frozen, this one
|
73
59
|
# won't be.
|
74
60
|
def copy
|
75
|
-
Marshal.load(Marshal.dump(self))
|
61
|
+
#Marshal.load(Marshal.dump(self))
|
62
|
+
self.dup.dirty
|
76
63
|
end
|
77
64
|
|
78
|
-
|
79
|
-
|
80
|
-
|
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}>"
|
65
|
+
def dirty #:nodoc:
|
66
|
+
@field = nil
|
67
|
+
self
|
119
68
|
end
|
120
69
|
|
70
|
+
# Addresses in a CAL-ADDRESS are represented as a URI, usually a mailto URI.
|
71
|
+
attr_accessor :uri
|
72
|
+
# The common or displayable name associated with the calendar address, or
|
73
|
+
# nil if there is none.
|
74
|
+
attr_accessor :cn
|
121
75
|
# The participation role for the calendar user specified by the address.
|
122
76
|
#
|
123
77
|
# The standard roles are:
|
@@ -128,11 +82,7 @@ module Vpim
|
|
128
82
|
#
|
129
83
|
# The default role is REQ-PARTICIPANT, returned if no ROLE parameter was
|
130
84
|
# specified.
|
131
|
-
|
132
|
-
return 'REQ-PARTICIPANT' unless r = @field.param('ROLE')
|
133
|
-
r.first.upcase
|
134
|
-
end
|
135
|
-
|
85
|
+
attr_accessor :role
|
136
86
|
# The participation status for the calendar user specified by the
|
137
87
|
# property PARTSTAT, a String.
|
138
88
|
#
|
@@ -145,36 +95,124 @@ module Vpim
|
|
145
95
|
#
|
146
96
|
# Default is NEEDS-ACTION.
|
147
97
|
#
|
148
|
-
#
|
149
|
-
|
150
|
-
|
151
|
-
|
98
|
+
# FIXME - make the default depend on the component type.
|
99
|
+
attr_accessor :partstat
|
100
|
+
# The value of the RSVP field, either +true+ or +false+. It is used to
|
101
|
+
# specify whether there is an expectation of a reply from the calendar
|
102
|
+
# user specified by the property value.
|
103
|
+
attr_accessor :rsvp
|
104
|
+
|
105
|
+
def initialize(field=nil) #:nodoc:
|
106
|
+
@field = field
|
107
|
+
@uri = ''
|
108
|
+
@cn = ''
|
109
|
+
@role = "REQ-PARTICIPANT"
|
110
|
+
@partstat = "NEEDS-ACTION"
|
111
|
+
@rsvp = false
|
152
112
|
end
|
153
113
|
|
154
|
-
#
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
@field['PARTSTAT'] = status.to_str
|
160
|
-
status
|
114
|
+
# Create a new Address. It will encode as a +name+ property.
|
115
|
+
def self.create(uri='')
|
116
|
+
adr = new
|
117
|
+
adr.uri = uri.to_str
|
118
|
+
adr
|
161
119
|
end
|
162
120
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
121
|
+
def self.decode(field)
|
122
|
+
adr = new(field)
|
123
|
+
adr.uri = field.value
|
124
|
+
|
125
|
+
cn = field.param('CN')
|
126
|
+
|
127
|
+
if cn
|
128
|
+
adr.cn = cn.first
|
129
|
+
end
|
130
|
+
|
131
|
+
role = field.param('ROLE')
|
132
|
+
|
133
|
+
if role
|
134
|
+
adr.role = role.first.strip.upcase
|
135
|
+
end
|
136
|
+
|
137
|
+
partstat = field.param('PARTSTAT')
|
138
|
+
|
139
|
+
if partstat
|
140
|
+
adr.partstat = partstat.first.strip.upcase
|
141
|
+
end
|
142
|
+
|
143
|
+
rsvp = field.param('RSVP')
|
144
|
+
|
145
|
+
if rsvp
|
146
|
+
adr.rsvp = case rsvp.first
|
147
|
+
when /TRUE/i then true
|
148
|
+
when /FALSE/i then false
|
149
|
+
else raise InvalidEncodingError, "RSVP param value not TRUE/FALSE: #{rsvp}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
adr.freeze
|
154
|
+
end
|
155
|
+
|
156
|
+
# Return a representation of this Address as a DirectoryInfo::Field.
|
157
|
+
def encode(name) #:nodoc:
|
158
|
+
if @field
|
159
|
+
# FIXME - set the field name, it could be different from cached
|
160
|
+
return @field
|
161
|
+
end
|
162
|
+
|
163
|
+
value = uri.to_str.strip
|
164
|
+
|
165
|
+
if value.empty?
|
166
|
+
raise Uencodeable, "Address#uri is zero-length"
|
167
|
+
end
|
168
|
+
|
169
|
+
params = {}
|
170
|
+
|
171
|
+
if cn.length > 0
|
172
|
+
params['CN'] = Vpim::encode_paramvalue(cn)
|
173
|
+
end
|
174
|
+
|
175
|
+
# FIXME - default value is different for non-vEvent
|
176
|
+
if role.length > 0 && role != 'REQ-PARTICIPANT'
|
177
|
+
params['ROLE'] = Vpim::encode_paramtext(role)
|
178
|
+
end
|
179
|
+
|
180
|
+
# FIXME - default value is different for non-vEvent
|
181
|
+
if partstat.length > 0 && partstat != 'NEEDS-ACTION'
|
182
|
+
params['PARTSTAT'] = Vpim::encode_paramtext(partstat)
|
183
|
+
end
|
184
|
+
|
185
|
+
if rsvp
|
186
|
+
params['RSVP'] = 'true'
|
187
|
+
end
|
188
|
+
|
189
|
+
Vpim::DirectoryInfo::Field.create(name, value, params)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Return true if the +uri+ is == to this address' URI. The comparison
|
193
|
+
# is case-insensitive (because email addresses and domain names are).
|
194
|
+
def ==(uri)
|
195
|
+
# TODO - could I use a URI library?
|
196
|
+
Vpim::Methods.casecmp?(self.uri.to_str, uri.to_str)
|
197
|
+
end
|
198
|
+
|
199
|
+
# A string representation of an address, using the common name, and the
|
200
|
+
# URI. The URI protocol is stripped if it's "mailto:".
|
201
|
+
def to_s
|
202
|
+
u = uri
|
203
|
+
u = u.gsub(/^mailto: */i, '')
|
204
|
+
|
205
|
+
if cn.length > 0
|
206
|
+
"#{cn.inspect} <#{uri}>"
|
207
|
+
else
|
208
|
+
uri
|
176
209
|
end
|
177
210
|
end
|
211
|
+
|
212
|
+
def inspect #:nodoc:
|
213
|
+
"#<Vpim::Icalendar::Address:cn=#{cn.inspect} status=#{partstat} rsvp=#{rsvp} #{uri.inspect}>"
|
214
|
+
end
|
215
|
+
|
178
216
|
end
|
179
217
|
end
|
180
218
|
end
|
data/lib/vpim/icalendar.rb
CHANGED
@@ -33,10 +33,12 @@ module Vpim
|
|
33
33
|
# (iTIP) Scheduling Events, BusyTime, To-dos and Journal Entries
|
34
34
|
# - link:rfc2447.txt: iCalendar Message-Based Interoperability Protocol
|
35
35
|
#
|
36
|
-
# iCalendar
|
37
|
-
#
|
38
|
-
# VERSION:
|
39
|
-
#
|
36
|
+
# = iCalendar and vCalendar
|
37
|
+
#
|
38
|
+
# iCalendar files have VERSION:2.0 and vCalendar have VERSION:1.0. iCalendar
|
39
|
+
# (RFC 2445) is based on vCalendar, but but is not very compatible. While
|
40
|
+
# much appears to be similar, the recurrence rule syntax is completely
|
41
|
+
# different.
|
40
42
|
#
|
41
43
|
# iCalendars are usually transmitted in files with <code>.ics</code>
|
42
44
|
# extensions.
|
@@ -88,18 +90,37 @@ module Vpim
|
|
88
90
|
end
|
89
91
|
end
|
90
92
|
|
93
|
+
# Add and event to this calendar.
|
94
|
+
#
|
95
|
+
# Yields an event maker, Icalendar::Vevent::Maker.
|
96
|
+
def add_event(&block) #:yield:event
|
97
|
+
push Vevent::Maker.make( &block )
|
98
|
+
end
|
99
|
+
|
100
|
+
# FIXME - could take mandatory fields as an arguments
|
101
|
+
# FIXME - args: support PRODID
|
102
|
+
# FIXME - yield an Icalendar::Maker if block provided
|
103
|
+
# FIXME - maker#prodid=
|
104
|
+
def Icalendar.create2(args = nil)
|
105
|
+
# FIXME - make the primary API
|
106
|
+
di = DirectoryInfo.create( [ DirectoryInfo::Field.create('VERSION', '2.0') ], 'VCALENDAR' )
|
107
|
+
|
108
|
+
di.push_unique DirectoryInfo::Field.create('PRODID', Vpim::PRODID)
|
109
|
+
di.push_unique DirectoryInfo::Field.create('CALSCALE', "Gregorian")
|
110
|
+
|
111
|
+
new(di.to_a)
|
112
|
+
end
|
113
|
+
|
91
114
|
# Create a new Icalendar object with the minimal set of fields for a valid
|
92
115
|
# Calendar. If specified, +fields+ must be an array of
|
93
116
|
# DirectoryInfo::Field objects to add. They can override the the default
|
94
117
|
# Calendar fields, so, for example, this can be used to set a custom PRODID field.
|
95
|
-
#
|
96
|
-
# TODO - allow hash args like Vevent.create
|
97
118
|
def Icalendar.create(fields=[])
|
98
119
|
di = DirectoryInfo.create( [ DirectoryInfo::Field.create('VERSION', '2.0') ], 'VCALENDAR' )
|
99
120
|
|
100
121
|
DirectoryInfo::Field.create_array(fields).each { |f| di.push_unique f }
|
101
122
|
|
102
|
-
di.push_unique DirectoryInfo::Field.create('PRODID',
|
123
|
+
di.push_unique DirectoryInfo::Field.create('PRODID', Vpim::PRODID)
|
103
124
|
di.push_unique DirectoryInfo::Field.create('CALSCALE', "Gregorian")
|
104
125
|
|
105
126
|
new(di.to_a)
|
@@ -124,10 +145,16 @@ module Vpim
|
|
124
145
|
Icalendar.create(fields)
|
125
146
|
end
|
126
147
|
|
148
|
+
# Used during encoding.
|
149
|
+
def fields # :nodoc:
|
150
|
+
f = @properties.to_a
|
151
|
+
last = f.pop
|
152
|
+
@components.each { |c| f << c.fields }
|
153
|
+
f.push last
|
154
|
+
end
|
155
|
+
|
127
156
|
# Encode the Calendar as a string. The width is the maximum width of the
|
128
157
|
# encoded lines, it can be specified, but is better left to the default.
|
129
|
-
#
|
130
|
-
# TODO - only does top-level now, needs to add the events/todos/etc.
|
131
158
|
def encode(width=nil)
|
132
159
|
# We concatenate the fields of all objects, create a DirInfo, then
|
133
160
|
# encode it.
|
@@ -135,17 +162,6 @@ module Vpim
|
|
135
162
|
di.encode(width)
|
136
163
|
end
|
137
164
|
|
138
|
-
# Used during encoding.
|
139
|
-
def fields # :nodoc:
|
140
|
-
fields = @properties.to_a
|
141
|
-
|
142
|
-
last = fields.pop
|
143
|
-
|
144
|
-
@components.each { |c| fields << c.fields }
|
145
|
-
|
146
|
-
fields << last
|
147
|
-
end
|
148
|
-
|
149
165
|
alias to_s encode
|
150
166
|
|
151
167
|
# Push a calendar component onto the calendar.
|
@@ -156,6 +172,7 @@ module Vpim
|
|
156
172
|
else
|
157
173
|
raise ArgumentError, "can't add a #{component.type} to a calendar"
|
158
174
|
end
|
175
|
+
self
|
159
176
|
end
|
160
177
|
|
161
178
|
# Check if the protocol method is +method+
|
data/lib/vpim/property/base.rb
CHANGED
@@ -8,10 +8,88 @@
|
|
8
8
|
|
9
9
|
module Vpim
|
10
10
|
class Icalendar
|
11
|
+
module Set #:nodoc:
|
12
|
+
module Util #:nodoc:
|
13
|
+
|
14
|
+
def rm_all(name)
|
15
|
+
rm = @comp.properties.select { |f| f.name? name }
|
16
|
+
rm.each { |f| @comp.properties.delete(f) }
|
17
|
+
end
|
18
|
+
|
19
|
+
def set_token(name, allowed, default, value) #:nodoc:
|
20
|
+
value = value.to_str
|
21
|
+
unless allowed.include?(value)
|
22
|
+
raise Vpim::Unencodeable, "Invalid #{name} value '#{value}'"
|
23
|
+
end
|
24
|
+
rm_all(name)
|
25
|
+
unless value == default
|
26
|
+
@comp.properties.push Vpim::DirectoryInfo::Field.create(name, value)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def field_create(name, value, default_value_type = nil, value_type = nil, params = {})
|
31
|
+
if value_type && value_type != default_value_type
|
32
|
+
params['VALUE'] = value_type
|
33
|
+
end
|
34
|
+
Vpim::DirectoryInfo::Field.create(name, value, params)
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_date_or_datetime(name, default, value)
|
38
|
+
f = nil
|
39
|
+
case value
|
40
|
+
when Date
|
41
|
+
f = field_create(name, Vpim.encode_date(value), default, 'DATE')
|
42
|
+
when Time
|
43
|
+
f = field_create(name, Vpim.encode_date_time(value), default, 'DATE-TIME')
|
44
|
+
else
|
45
|
+
raise Vpim::Unencodeable, "Invalid #{name} value #{value.inspect}"
|
46
|
+
end
|
47
|
+
rm_all(name)
|
48
|
+
@comp.properties.push(f)
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_datetime(name, value)
|
52
|
+
f = field_create(name, Vpim.encode_date_time(value))
|
53
|
+
rm_all(name)
|
54
|
+
@comp.properties.push(f)
|
55
|
+
end
|
56
|
+
|
57
|
+
def set_text(name, value)
|
58
|
+
f = field_create(name, Vpim.encode_text(value))
|
59
|
+
rm_all(name)
|
60
|
+
@comp.properties.push(f)
|
61
|
+
end
|
62
|
+
|
63
|
+
def set_text_list(name, value)
|
64
|
+
f = field_create(name, Vpim.encode_text_list(value))
|
65
|
+
rm_all(name)
|
66
|
+
@comp.properties.push(f)
|
67
|
+
end
|
68
|
+
|
69
|
+
def set_integer(name, value)
|
70
|
+
value = value.to_int.to_s
|
71
|
+
f = field_create(name, value)
|
72
|
+
rm_all(name)
|
73
|
+
@comp.properties.push(f)
|
74
|
+
end
|
75
|
+
|
76
|
+
def add_address(name, value)
|
77
|
+
f = value.encode(name)
|
78
|
+
@comp.properties.push(f)
|
79
|
+
end
|
80
|
+
|
81
|
+
def set_address(name, value)
|
82
|
+
rm_all(name)
|
83
|
+
add_address(name, value)
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
11
89
|
module Property #:nodoc:
|
12
90
|
|
13
|
-
# FIXME -
|
14
|
-
module Base
|
91
|
+
# FIXME - rename Base to Util
|
92
|
+
module Base #:nodoc:
|
15
93
|
# Value of first property with name +name+
|
16
94
|
def propvalue(name) #:nodoc:
|
17
95
|
prop = @properties.detect { |f| f.name? name }
|
data/lib/vpim/property/common.rb
CHANGED
@@ -78,7 +78,7 @@ module Vpim
|
|
78
78
|
organizer = @properties.field('ORGANIZER')
|
79
79
|
|
80
80
|
if organizer
|
81
|
-
organizer = Icalendar::Address.
|
81
|
+
organizer = Icalendar::Address.decode(organizer)
|
82
82
|
end
|
83
83
|
|
84
84
|
organizer.freeze
|
@@ -128,7 +128,7 @@ seq
|
|
128
128
|
# attendees are objects of Icalendar::Address. If +uri+ is specified
|
129
129
|
# only the return the attendees with this +uri+.
|
130
130
|
def attendees(uri = nil)
|
131
|
-
attendees = @properties.enum_by_name('ATTENDEE').map { |a| Icalendar::Address.
|
131
|
+
attendees = @properties.enum_by_name('ATTENDEE').map { |a| Icalendar::Address.decode(a) }
|
132
132
|
attendees.freeze
|
133
133
|
if uri
|
134
134
|
attendees.select { |a| a == uri }
|
@@ -174,6 +174,141 @@ seq
|
|
174
174
|
end
|
175
175
|
|
176
176
|
end
|
177
|
+
|
178
|
+
module Set
|
179
|
+
|
180
|
+
# Properties common to Vevent, Vtodo, and Vjournal.
|
181
|
+
module Common
|
182
|
+
|
183
|
+
# Set the access class of the component, see Icalendar::Get::Common#access_class.
|
184
|
+
def access_class(token)
|
185
|
+
set_token 'CLASS', ["PUBLIC", "PRIVATE", "CONFIDENTIAL"], "PUBLIC", token
|
186
|
+
end
|
187
|
+
|
188
|
+
# Set the creation time, see Icalendar::Get::Common#created
|
189
|
+
def created(time)
|
190
|
+
set_datetime 'CREATED', time
|
191
|
+
end
|
192
|
+
|
193
|
+
# Set the description, see Icalendar::Get::Common#description.
|
194
|
+
def description(text)
|
195
|
+
set_text 'DESCRIPTION', text
|
196
|
+
end
|
197
|
+
|
198
|
+
# Set the sequence number, see Icalendar::Get::Common#sequence.
|
199
|
+
# is no SEQUENCE; property.
|
200
|
+
def sequence(int)
|
201
|
+
set_integer 'SEQUENCE', int
|
202
|
+
end
|
203
|
+
|
204
|
+
# Set the timestamp, see Icalendar::Get::Common#timestamp.
|
205
|
+
def dtstamp(time)
|
206
|
+
set_datetime 'DTSTAMP', time
|
207
|
+
self
|
208
|
+
end
|
209
|
+
|
210
|
+
# The start time or date, see Icalendar::Get::Common#dtstart.
|
211
|
+
def dtstart(start)
|
212
|
+
set_date_or_datetime 'DTSTART', 'DATE-TIME', start
|
213
|
+
self
|
214
|
+
end
|
215
|
+
|
216
|
+
# Set the last modification time, see Icalendar::Get::Common#lastmod.
|
217
|
+
def lastmod(time)
|
218
|
+
set_datetime 'LAST-MODIFIED', time
|
219
|
+
self
|
220
|
+
end
|
221
|
+
|
222
|
+
# Set the event organizer, an Icalendar::Address, see Icalendar::Get::Common#organizer.
|
223
|
+
#
|
224
|
+
# Without an +adr+ it yields an Icalendar::Address that is a copy of
|
225
|
+
# the current organizer (if any), allowing it to be modified.
|
226
|
+
def organizer(adr=nil) #:yield: organizer
|
227
|
+
unless adr
|
228
|
+
adr = @comp.organizer
|
229
|
+
if adr
|
230
|
+
adr = adr.copy
|
231
|
+
else
|
232
|
+
adr = Icalendar::Address.create
|
233
|
+
end
|
234
|
+
yield adr
|
235
|
+
end
|
236
|
+
set_address('ORGANIZER', adr)
|
237
|
+
self
|
238
|
+
end
|
239
|
+
|
240
|
+
=begin
|
241
|
+
# Status values are not rejected during decoding. However, if the
|
242
|
+
# status is requested, and it's value is not one of the defined
|
243
|
+
# allowable values, an exception is raised.
|
244
|
+
def status
|
245
|
+
case self
|
246
|
+
when Vpim::Icalendar::Vevent
|
247
|
+
proptoken 'STATUS', ['TENTATIVE', 'CONFIRMED', 'CANCELLED']
|
248
|
+
|
249
|
+
when Vpim::Icalendar::Vtodo
|
250
|
+
proptoken 'STATUS', ['NEEDS-ACTION', 'COMPLETED', 'IN-PROCESS', 'CANCELLED']
|
251
|
+
|
252
|
+
when Vpim::Icalendar::Vevent
|
253
|
+
proptoken 'STATUS', ['DRAFT', 'FINAL', 'CANCELLED']
|
254
|
+
end
|
255
|
+
end
|
256
|
+
=end
|
257
|
+
|
258
|
+
# Set summary description of component, see Icalendar::Get::Common#summary.
|
259
|
+
def summary(text)
|
260
|
+
set_text 'SUMMARY', text
|
261
|
+
end
|
262
|
+
|
263
|
+
# Set the unique identifier of this calendar component, see Icalendar::Get::Common#uid.
|
264
|
+
def uid(uid)
|
265
|
+
set_text 'UID', uid
|
266
|
+
end
|
267
|
+
|
268
|
+
def url(url)
|
269
|
+
set_text 'URL', url
|
270
|
+
end
|
271
|
+
|
272
|
+
# Add an attendee Address, see Icalendar::Get::Common#attendees.
|
273
|
+
def add_attendee(adr)
|
274
|
+
add_address('ATTENDEE', adr)
|
275
|
+
end
|
276
|
+
|
277
|
+
# Set the categories, see Icalendar::Get::Common#attendees.
|
278
|
+
#
|
279
|
+
# If +cats+ is provided, the categories are set to cats, either a
|
280
|
+
# String or an Array of String. Otherwise, and array of the existing
|
281
|
+
# category strings is yielded, and it can be modified.
|
282
|
+
def categories(cats = nil) #:yield: categories
|
283
|
+
unless cats
|
284
|
+
cats = @comp.categories
|
285
|
+
yield cats
|
286
|
+
end
|
287
|
+
# TODO - strip the strings
|
288
|
+
set_text_list('CATEGORIES', cats)
|
289
|
+
end
|
290
|
+
|
291
|
+
# Set the comment, see Icalendar::Get::Common#comment.
|
292
|
+
def comment(value)
|
293
|
+
set_text 'COMMENT', value
|
294
|
+
end
|
295
|
+
|
296
|
+
=begin
|
297
|
+
def contacts
|
298
|
+
proptextarray 'CONTACT'
|
299
|
+
end
|
300
|
+
|
301
|
+
# An Array of attachments, see Attachment for more information.
|
302
|
+
def attachments
|
303
|
+
@properties.enum_by_name('ATTACH').map do |f|
|
304
|
+
attachment = Attachment.decode(f, 'uri', 'FMTTYPE')
|
305
|
+
end
|
306
|
+
end
|
307
|
+
=end
|
308
|
+
|
309
|
+
end
|
310
|
+
|
311
|
+
end
|
177
312
|
end
|
178
313
|
end
|
179
314
|
|
data/lib/vpim/rfc2425.rb
CHANGED
@@ -56,6 +56,14 @@ module Vpim
|
|
56
56
|
|
57
57
|
# integer = (["+"] / "-") 1*DIGIT
|
58
58
|
INTEGER = '[-+]?\d+'
|
59
|
+
|
60
|
+
# QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-US-ASCII
|
61
|
+
# ; Any character except CTLs and DQUOTE
|
62
|
+
QSAFECHAR = '[ \t\x21\x23-\x7e\x80-\xff]'
|
63
|
+
|
64
|
+
# SAFE-CHAR = WSP / %x21 / %x23-2B / %x2D-39 / %x3C-7E / NON-US-ASCII
|
65
|
+
# ; Any character except CTLs, DQUOTE, ";", ":", ","
|
66
|
+
SAFECHAR = '[ \t\x21\x23-\x2b\x2d-\x39\x3c-\x7e\x80-\xff]'
|
59
67
|
end
|
60
68
|
end
|
61
69
|
|
@@ -232,8 +240,13 @@ module Vpim
|
|
232
240
|
end
|
233
241
|
end
|
234
242
|
|
243
|
+
# v is an Array of String, or just a single String
|
235
244
|
def Vpim.encode_text_list(v, sep = ",") #:nodoc:
|
236
|
-
|
245
|
+
begin
|
246
|
+
v.to_ary.map{ |t| Vpim.encode_text(t) }.join(sep)
|
247
|
+
rescue
|
248
|
+
Vpim.encode_text(v)
|
249
|
+
end
|
237
250
|
end
|
238
251
|
|
239
252
|
# Convert a +sep+-seperated list of TEXT values into an array of values.
|
@@ -248,6 +261,29 @@ module Vpim
|
|
248
261
|
list
|
249
262
|
end
|
250
263
|
|
264
|
+
# param-value = paramtext / quoted-string
|
265
|
+
# paramtext = *SAFE-CHAR
|
266
|
+
# quoted-string = DQUOTE *QSAFE-CHAR DQUOTE
|
267
|
+
def Vpim.encode_paramtext(value)
|
268
|
+
case value
|
269
|
+
when %r{\A#{Bnf::SAFECHAR}*\z}
|
270
|
+
value
|
271
|
+
else
|
272
|
+
raise Vpim::Unencodable, "paramtext #{value.inspect}"
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def Vpim.encode_paramvalue(value)
|
277
|
+
case value
|
278
|
+
when %r{\A#{Bnf::SAFECHAR}*\z}
|
279
|
+
value
|
280
|
+
when %r{\A#{Bnf::QSAFECHAR}*\z}
|
281
|
+
'"' + value + '"'
|
282
|
+
else
|
283
|
+
raise Vpim::Unencodable, "param-value #{value.inspect}"
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
251
287
|
|
252
288
|
# Unfold the lines in +card+, then return an array of one Field object per
|
253
289
|
# line.
|
data/lib/vpim/vcard.rb
CHANGED
@@ -1078,19 +1078,12 @@ module Vpim
|
|
1078
1078
|
|
1079
1079
|
public
|
1080
1080
|
|
1081
|
-
#
|
1082
|
-
#
|
1083
|
-
#
|
1084
|
-
#
|
1085
|
-
#
|
1086
|
-
#
|
1087
|
-
# 'N',
|
1088
|
-
# x.map { |s| s ? s.to_str : '' }
|
1089
|
-
# )
|
1090
|
-
# self
|
1091
|
-
# end
|
1092
|
-
# Set with #name now.
|
1093
|
-
# Use m.name do |n| n.fullname = foo end
|
1081
|
+
# Deprecated, see #name.
|
1082
|
+
#
|
1083
|
+
# Use
|
1084
|
+
# maker.name do |n| n.fullname = "foo" end
|
1085
|
+
# to set just fullname, or set the other fields to set fullname and the
|
1086
|
+
# name.
|
1094
1087
|
def fullname=(fullname) #:nodoc: bacwards compat
|
1095
1088
|
if @card.field('FN')
|
1096
1089
|
raise Vpim::InvalidEncodingError, "Not allowed to add more than one FN field to a vCard."
|
data/lib/vpim/version.rb
CHANGED
data/lib/vpim/vevent.rb
CHANGED
@@ -20,6 +20,7 @@ require 'vpim/property/recurrence'
|
|
20
20
|
module Vpim
|
21
21
|
class Icalendar
|
22
22
|
class Vevent
|
23
|
+
|
23
24
|
include Vpim::Icalendar::Property::Base
|
24
25
|
include Vpim::Icalendar::Property::Common
|
25
26
|
include Vpim::Icalendar::Property::Priority
|
@@ -37,6 +38,19 @@ module Vpim
|
|
37
38
|
# See "TODO - fields" in dirinfo.rb
|
38
39
|
end
|
39
40
|
|
41
|
+
# TODO - derive everything from Icalendar::Component to get this kind of stuff?
|
42
|
+
def fields #:nodoc:
|
43
|
+
f = @properties.to_a
|
44
|
+
last = f.pop
|
45
|
+
f.push @elements
|
46
|
+
f.push last
|
47
|
+
end
|
48
|
+
|
49
|
+
def properties #:nodoc:
|
50
|
+
@properties
|
51
|
+
end
|
52
|
+
|
53
|
+
|
40
54
|
# Create a new Vevent object. All events must have a DTSTART field,
|
41
55
|
# specify it as either a Time or a Date in +start+, it defaults to "now"
|
42
56
|
# (is this useful?).
|
@@ -71,10 +85,12 @@ module Vpim
|
|
71
85
|
|
72
86
|
# Accept an event invitation. The +invitee+ is the Address that wishes
|
73
87
|
# to accept the event invitation as confirmed.
|
88
|
+
#
|
89
|
+
# The event created is identical to this one, but
|
90
|
+
# - without the attendees
|
91
|
+
# - with the invitee added with a PARTSTAT of ACCEPTED
|
74
92
|
def accept(invitee)
|
75
|
-
#
|
76
|
-
# - without the attendees
|
77
|
-
# - with the invitee added with a PARTSTAT of ACCEPTED
|
93
|
+
# FIXME - move to Vpim::Itip.
|
78
94
|
invitee = invitee.copy
|
79
95
|
invitee.partstat = 'ACCEPTED'
|
80
96
|
|
@@ -84,7 +100,7 @@ module Vpim
|
|
84
100
|
|f,i|
|
85
101
|
|
86
102
|
# put invitee in as field[1]
|
87
|
-
fields << invitee.
|
103
|
+
fields << invitee.encode('ATTENDEE') if i == 1
|
88
104
|
|
89
105
|
fields << f unless f.name? 'ATTENDEE'
|
90
106
|
end
|
@@ -92,11 +108,8 @@ module Vpim
|
|
92
108
|
Vevent.new(fields)
|
93
109
|
end
|
94
110
|
|
95
|
-
|
96
|
-
#
|
97
|
-
# TODO - def dtstart=(start) ... start should be allowed to be Time/Date/DateTime
|
98
|
-
=end
|
99
|
-
|
111
|
+
# In iTIP, whether this event is OPAQUE or TRANSPARENT to scheduling. If
|
112
|
+
# transparency is not explicitly set, it defaults to OPAQUE.
|
100
113
|
def transparency
|
101
114
|
proptoken 'TRANSP', ["OPAQUE", "TRANSPARENT"], "OPAQUE"
|
102
115
|
end
|
@@ -135,6 +148,57 @@ module Vpim
|
|
135
148
|
end
|
136
149
|
end
|
137
150
|
|
151
|
+
# Make a new Vevent, or make changes to an existing Vevent.
|
152
|
+
class Maker
|
153
|
+
# TODO - should I automatically set
|
154
|
+
# #created
|
155
|
+
# #dtstamp
|
156
|
+
# #sequence
|
157
|
+
# ...?
|
158
|
+
#
|
159
|
+
# Many have pretty specific meanings in iTIP, perhaps I should leave
|
160
|
+
# them alone.
|
161
|
+
include Vpim::Icalendar::Set::Util #:nodoc:
|
162
|
+
include Vpim::Icalendar::Set::Common
|
163
|
+
|
164
|
+
# The event that changes are being made to.
|
165
|
+
attr_reader :event
|
166
|
+
|
167
|
+
def initialize(event) #:nodoc:
|
168
|
+
@event = event
|
169
|
+
@comp = event
|
170
|
+
end
|
171
|
+
|
172
|
+
# Make changes to +event+. If +event+ is not specified, creates a new
|
173
|
+
# event. Yields a Vevent::Maker, and returns +event+.
|
174
|
+
def self.make(event = Vpim::Icalendar::Vevent.create) #:yield:maker
|
175
|
+
m = self.new(event)
|
176
|
+
yield m
|
177
|
+
m.event
|
178
|
+
end
|
179
|
+
|
180
|
+
# Set transparency to "OPAQUE" or "TRANSPARENT", see Vpim::Vevent#transparency.
|
181
|
+
def transparency(token)
|
182
|
+
set_token 'TRANSP', ["OPAQUE", "TRANSPARENT"], "OPAQUE", token
|
183
|
+
end
|
184
|
+
|
185
|
+
# Set end for events with fixed durations. +end+ can be a Date or Time
|
186
|
+
def dtend(dtend)
|
187
|
+
set_date_or_datetime 'DTEND', 'DATE-TIME', dtend
|
188
|
+
end
|
189
|
+
|
190
|
+
# Yields a selector that allows the duration to be set.
|
191
|
+
#
|
192
|
+
# TODO - syntax is:
|
193
|
+
# dur-value = (["+"] / "-") "P" (dur-date / dur-time / dur-week)
|
194
|
+
# dur-date = dur-day [ "T" (dur-hour / dur-minute / dur-second) ]
|
195
|
+
# dur-time = "T" (dur-hour / dur-minute / dur-second)
|
196
|
+
# dur-week = 1*DIGIT "W"
|
197
|
+
def duration(dur) #:yield:selector
|
198
|
+
raise Vpim::Unsupported
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
138
202
|
end
|
139
203
|
end
|
140
204
|
end
|
metadata
CHANGED
@@ -3,15 +3,15 @@ rubygems_version: 0.8.11
|
|
3
3
|
specification_version: 1
|
4
4
|
name: vpim
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: "0.
|
7
|
-
date: 2006-
|
8
|
-
summary:
|
6
|
+
version: "0.360"
|
7
|
+
date: 2006-04-02 00:00:00 -08:00
|
8
|
+
summary: iCalendar and vCard support for ruby
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
11
|
email: sroberts@uniserve.com
|
12
12
|
homepage: http://vpim.rubyforge.org
|
13
|
-
rubyforge_project:
|
14
|
-
description:
|
13
|
+
rubyforge_project: vpim
|
14
|
+
description: This is a pure-ruby library for decoding and encoding vCard and iCalendar data ("personal information") called vPim.
|
15
15
|
autorequire: vpim
|
16
16
|
default_executable:
|
17
17
|
bindir: bin
|