mumboe-vpim 0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +510 -0
- data/COPYING +58 -0
- data/README +185 -0
- data/lib/vpim/address.rb +219 -0
- data/lib/vpim/agent/atomize.rb +104 -0
- data/lib/vpim/agent/base.rb +73 -0
- data/lib/vpim/agent/calendars.rb +173 -0
- data/lib/vpim/agent/handler.rb +26 -0
- data/lib/vpim/agent/ics.rb +161 -0
- data/lib/vpim/attachment.rb +102 -0
- data/lib/vpim/date.rb +222 -0
- data/lib/vpim/dirinfo.rb +277 -0
- data/lib/vpim/duration.rb +119 -0
- data/lib/vpim/enumerator.rb +32 -0
- data/lib/vpim/field.rb +614 -0
- data/lib/vpim/icalendar.rb +384 -0
- data/lib/vpim/maker/vcard.rb +16 -0
- data/lib/vpim/property/base.rb +193 -0
- data/lib/vpim/property/common.rb +315 -0
- data/lib/vpim/property/location.rb +38 -0
- data/lib/vpim/property/priority.rb +43 -0
- data/lib/vpim/property/recurrence.rb +69 -0
- data/lib/vpim/property/resources.rb +24 -0
- data/lib/vpim/repo.rb +261 -0
- data/lib/vpim/rfc2425.rb +367 -0
- data/lib/vpim/rrule.rb +591 -0
- data/lib/vpim/time.rb +40 -0
- data/lib/vpim/vcard.rb +1456 -0
- data/lib/vpim/version.rb +18 -0
- data/lib/vpim/vevent.rb +187 -0
- data/lib/vpim/view.rb +90 -0
- data/lib/vpim/vjournal.rb +58 -0
- data/lib/vpim/vpim.rb +65 -0
- data/lib/vpim/vtodo.rb +103 -0
- data/lib/vpim.rb +13 -0
- data/samples/README.mutt +93 -0
- data/samples/ab-query.rb +57 -0
- data/samples/agent.ru +10 -0
- data/samples/cmd-itip.rb +156 -0
- data/samples/ex_cpvcard.rb +55 -0
- data/samples/ex_get_vcard_photo.rb +22 -0
- data/samples/ex_mkv21vcard.rb +34 -0
- data/samples/ex_mkvcard.rb +64 -0
- data/samples/ex_mkyourown.rb +29 -0
- data/samples/ics-dump.rb +210 -0
- data/samples/ics-to-rss.rb +84 -0
- data/samples/mutt-aliases-to-vcf.rb +45 -0
- data/samples/osx-wrappers.rb +86 -0
- data/samples/reminder.rb +209 -0
- data/samples/rrule.rb +71 -0
- data/samples/tabbed-file-to-vcf.rb +390 -0
- data/samples/vcf-dump.rb +86 -0
- data/samples/vcf-lines.rb +61 -0
- data/samples/vcf-to-ics.rb +22 -0
- data/samples/vcf-to-mutt.rb +121 -0
- data/test/test_agent_atomize.rb +84 -0
- data/test/test_agent_calendars.rb +128 -0
- data/test/test_agent_ics.rb +96 -0
- data/test/test_all.rb +17 -0
- data/test/test_date.rb +120 -0
- data/test/test_dur.rb +41 -0
- data/test/test_field.rb +156 -0
- data/test/test_ical.rb +437 -0
- data/test/test_misc.rb +13 -0
- data/test/test_repo.rb +129 -0
- data/test/test_rrule.rb +1030 -0
- data/test/test_vcard.rb +973 -0
- data/test/test_view.rb +79 -0
- metadata +140 -0
@@ -0,0 +1,384 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 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 "enumerator"
|
10
|
+
|
11
|
+
require 'vpim/rfc2425'
|
12
|
+
require 'vpim/dirinfo'
|
13
|
+
require 'vpim/rrule'
|
14
|
+
require 'vpim/vevent'
|
15
|
+
require 'vpim/vtodo'
|
16
|
+
require 'vpim/vjournal'
|
17
|
+
require 'vpim/vpim'
|
18
|
+
|
19
|
+
module Vpim
|
20
|
+
# An iCalendar.
|
21
|
+
#
|
22
|
+
# A Calendar is some meta-information followed by a sequence of components.
|
23
|
+
#
|
24
|
+
# Defined components are Event, Todo, Freebusy, Journal, and Timezone, each
|
25
|
+
# of which are represented by their own class, though they share many
|
26
|
+
# properties in common. For example, Event and Todo may both contain
|
27
|
+
# multiple Alarm components.
|
28
|
+
#
|
29
|
+
# = Reference
|
30
|
+
#
|
31
|
+
# The iCalendar format is specified by a series of IETF documents:
|
32
|
+
#
|
33
|
+
# - RFC2445[http://www.ietf.org/rfc/rfc2445.txt]: Internet Calendaring and
|
34
|
+
# Scheduling Core Object Specification
|
35
|
+
# - RFC2446[http://www.ietf.org/rfc/rfc2446.txt]: iCalendar
|
36
|
+
# Transport-Independent Interoperability Protocol (iTIP) Scheduling Events,
|
37
|
+
# BusyTime, To-dos and Journal Entries
|
38
|
+
# - RFC2447[http://www.ietf.org/rfc/rfc2447.txt]: iCalendar Message-Based
|
39
|
+
# Interoperability Protocol
|
40
|
+
#
|
41
|
+
# = iCalendar and vCalendar
|
42
|
+
#
|
43
|
+
# iCalendar files have VERSION:2.0 and vCalendar have VERSION:1.0. iCalendar
|
44
|
+
# (RFC 2445) is based on vCalendar, but is not very compatible. While
|
45
|
+
# much appears to be similar, the recurrence rule syntax is completely
|
46
|
+
# different.
|
47
|
+
#
|
48
|
+
# iCalendars are usually transmitted in files with <code>.ics</code>
|
49
|
+
# extensions.
|
50
|
+
class Icalendar
|
51
|
+
# FIXME do NOT do this!
|
52
|
+
include Vpim
|
53
|
+
|
54
|
+
# Regular expression strings for the EBNF of RFC 2445
|
55
|
+
module Bnf #:nodoc:
|
56
|
+
# dur-value = ["+" / "-"] "P" [ 1*DIGIT "W" ] [ 1*DIGIT "D" ] [ "T" [ 1*DIGIT "H" ] [ 1*DIGIT "M" ] [ 1*DIGIT "S" ] ]
|
57
|
+
DURATION = '([-+])?P(\d+W)?(\d+D)?T?(\d+H)?(\d+M)?(\d+S)?'
|
58
|
+
end
|
59
|
+
|
60
|
+
private_class_method :new
|
61
|
+
|
62
|
+
# Create a new Icalendar object from +fields+, an array of
|
63
|
+
# DirectoryInfo::Field objects.
|
64
|
+
#
|
65
|
+
# When decoding Calendar data, you would usually use Icalendar.decode(),
|
66
|
+
# which decodes the data into the field arrays, and calls this method
|
67
|
+
# for each Calendar it finds.
|
68
|
+
def initialize(fields) #:nodoc:
|
69
|
+
# seperate into the outer-level fields, and the arrays of component
|
70
|
+
# fields
|
71
|
+
outer, inner = Vpim.outer_inner(fields)
|
72
|
+
|
73
|
+
# Make a dirinfo out of outer, and check its an iCalendar
|
74
|
+
@properties = DirectoryInfo.create(outer)
|
75
|
+
@properties.check_begin_end('VCALENDAR')
|
76
|
+
|
77
|
+
@components = []
|
78
|
+
|
79
|
+
# could use #constants instead of this
|
80
|
+
factory = {
|
81
|
+
'VEVENT' => Vevent,
|
82
|
+
'VTODO' => Vtodo,
|
83
|
+
'VJOURNAL' => Vjournal,
|
84
|
+
# TODO - VTIMEZONE
|
85
|
+
}
|
86
|
+
|
87
|
+
inner.each do |component|
|
88
|
+
name = component.first.value
|
89
|
+
|
90
|
+
if klass = factory[name]
|
91
|
+
@components << klass.new(component)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Add an event to this calendar.
|
97
|
+
#
|
98
|
+
# Yields an event maker, Icalendar::Vevent::Maker.
|
99
|
+
def add_event(&block) #:yield:event
|
100
|
+
push Vevent::Maker.make( &block )
|
101
|
+
end
|
102
|
+
|
103
|
+
# TODO add_todo, add_journal
|
104
|
+
|
105
|
+
=begin
|
106
|
+
TODO
|
107
|
+
# Allows customization of calendar creation.
|
108
|
+
class Maker
|
109
|
+
def initialize #:nodoc:
|
110
|
+
@prodid = Vpim::PRODID
|
111
|
+
end
|
112
|
+
|
113
|
+
attr :prodid
|
114
|
+
end
|
115
|
+
=end
|
116
|
+
|
117
|
+
# The producer ID defaults to Vpim::PRODID but you can set it to something
|
118
|
+
# specific to your application.
|
119
|
+
def Icalendar.create2(producer = Vpim::PRODID) #:yield: self
|
120
|
+
# FIXME - make the primary API
|
121
|
+
di = DirectoryInfo.create( [ DirectoryInfo::Field.create('VERSION', '2.0') ], 'VCALENDAR' )
|
122
|
+
|
123
|
+
di.push_unique DirectoryInfo::Field.create('PRODID', producer.to_str)
|
124
|
+
di.push_unique DirectoryInfo::Field.create('CALSCALE', "Gregorian")
|
125
|
+
|
126
|
+
cal = new(di.to_a)
|
127
|
+
|
128
|
+
if block_given?
|
129
|
+
yield cal
|
130
|
+
end
|
131
|
+
|
132
|
+
cal
|
133
|
+
end
|
134
|
+
|
135
|
+
# Create a new Icalendar object with the minimal set of fields for a valid
|
136
|
+
# Calendar. If specified, +fields+ must be an array of
|
137
|
+
# DirectoryInfo::Field objects to add. They can override the the default
|
138
|
+
# Calendar fields, so, for example, this can be used to set a custom PRODID field.
|
139
|
+
def Icalendar.create(fields=[])
|
140
|
+
di = DirectoryInfo.create( [ DirectoryInfo::Field.create('VERSION', '2.0') ], 'VCALENDAR' )
|
141
|
+
|
142
|
+
DirectoryInfo::Field.create_array(fields).each { |f| di.push_unique f }
|
143
|
+
|
144
|
+
di.push_unique DirectoryInfo::Field.create('PRODID', Vpim::PRODID)
|
145
|
+
di.push_unique DirectoryInfo::Field.create('CALSCALE', "Gregorian")
|
146
|
+
|
147
|
+
new(di.to_a)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Create a new Icalendar object with a protocol method of REPLY.
|
151
|
+
#
|
152
|
+
# Meeting requests, and such, are Calendar containers with a protocol
|
153
|
+
# method of REQUEST, and contains some number of Events, Todos, etc.,
|
154
|
+
# that may need replying to. In order to reply to any of these components
|
155
|
+
# of a request, you must first build a Calendar object to hold your reply
|
156
|
+
# components.
|
157
|
+
#
|
158
|
+
# This method builds the reply Calendar, you then will add to it replies
|
159
|
+
# to the specific components of the request Calendar that you are replying
|
160
|
+
# to. If you have any particular fields that you want to be in the
|
161
|
+
# Calendar, other than the defaults, then can be supplied as +fields+, an
|
162
|
+
# array of Field objects.
|
163
|
+
def Icalendar.create_reply(fields=[])
|
164
|
+
fields << DirectoryInfo::Field.create('METHOD', 'REPLY')
|
165
|
+
|
166
|
+
Icalendar.create(fields)
|
167
|
+
end
|
168
|
+
|
169
|
+
# Used during encoding.
|
170
|
+
def fields # :nodoc:
|
171
|
+
f = @properties.to_a
|
172
|
+
last = f.pop
|
173
|
+
# Use of #each means we won't encode components in our View, but also
|
174
|
+
# that we won't encode timezones... but we don't decode/support timezones
|
175
|
+
# anyhow, so fix later.
|
176
|
+
each { |c| f << c.fields }
|
177
|
+
f.push last
|
178
|
+
end
|
179
|
+
|
180
|
+
# Encode the Calendar as a string. The width is the maximum width of the
|
181
|
+
# encoded lines, it can be specified, but is better left to the default.
|
182
|
+
def encode(width=nil)
|
183
|
+
# We concatenate the fields of all objects, create a DirInfo, then
|
184
|
+
# encode it.
|
185
|
+
di = DirectoryInfo.create(self.fields.flatten)
|
186
|
+
di.encode(width)
|
187
|
+
end
|
188
|
+
|
189
|
+
alias to_s encode
|
190
|
+
|
191
|
+
# Push a calendar component onto the calendar.
|
192
|
+
def push(component)
|
193
|
+
case component
|
194
|
+
when Vevent, Vtodo, Vjournal
|
195
|
+
@components << component
|
196
|
+
else
|
197
|
+
raise ArgumentError, "can't add a #{component.type} to a calendar"
|
198
|
+
end
|
199
|
+
self
|
200
|
+
end
|
201
|
+
|
202
|
+
alias :<< :push
|
203
|
+
|
204
|
+
# Check if the protocol method is +method+
|
205
|
+
def protocol?(method)
|
206
|
+
Vpim::Methods.casecmp?(protocol, method)
|
207
|
+
end
|
208
|
+
|
209
|
+
def Icalendar.decode_duration(str) #:nodoc:
|
210
|
+
unless match = %r{\s*#{Bnf::DURATION}\s*}.match(str)
|
211
|
+
raise InvalidEncodingError, "duration not valid (#{str})"
|
212
|
+
end
|
213
|
+
dur = 0
|
214
|
+
|
215
|
+
# Remember: match[0] is the whole match string, match[1] is $1, etc.
|
216
|
+
|
217
|
+
# Week
|
218
|
+
if match[2]
|
219
|
+
dur = match[2].to_i
|
220
|
+
end
|
221
|
+
# Days
|
222
|
+
dur *= 7
|
223
|
+
if match[3]
|
224
|
+
dur += match[3].to_i
|
225
|
+
end
|
226
|
+
# Hours
|
227
|
+
dur *= 24
|
228
|
+
if match[4]
|
229
|
+
dur += match[4].to_i
|
230
|
+
end
|
231
|
+
# Minutes
|
232
|
+
dur *= 60
|
233
|
+
if match[5]
|
234
|
+
dur += match[5].to_i
|
235
|
+
end
|
236
|
+
# Seconds
|
237
|
+
dur *= 60
|
238
|
+
if match[6]
|
239
|
+
dur += match[6].to_i
|
240
|
+
end
|
241
|
+
|
242
|
+
if match[1] && match[1] == '-'
|
243
|
+
dur = -dur
|
244
|
+
end
|
245
|
+
|
246
|
+
dur
|
247
|
+
end
|
248
|
+
|
249
|
+
# Decode iCalendar data into an array of Icalendar objects.
|
250
|
+
#
|
251
|
+
# Since iCalendars are self-delimited (by a BEGIN:VCALENDAR and an
|
252
|
+
# END:VCALENDAR), multiple iCalendars can be concatenated into a single
|
253
|
+
# file.
|
254
|
+
#
|
255
|
+
# cal must be String or IO, or implement #each by returning
|
256
|
+
# each line in the input as those classes do.
|
257
|
+
def Icalendar.decode(cal, e = nil)
|
258
|
+
entities = Vpim.expand(Vpim.decode(cal))
|
259
|
+
|
260
|
+
# Since all iCalendars must have a begin/end, the top-level should
|
261
|
+
# consist entirely of entities/arrays, even if its a single iCalendar.
|
262
|
+
if entities.detect { |e| ! e.kind_of? Array }
|
263
|
+
raise "Not a valid iCalendar"
|
264
|
+
end
|
265
|
+
|
266
|
+
calendars = []
|
267
|
+
|
268
|
+
entities.each do |e|
|
269
|
+
calendars << new(e)
|
270
|
+
end
|
271
|
+
|
272
|
+
calendars
|
273
|
+
end
|
274
|
+
|
275
|
+
# The iCalendar version multiplied by 10 as an Integer. iCalendar must have
|
276
|
+
# a version of 20, and vCalendar must have a version of 10.
|
277
|
+
def version
|
278
|
+
v = @properties['VERSION']
|
279
|
+
|
280
|
+
unless v
|
281
|
+
raise InvalidEncodingError, "Invalid calendar, no version field!"
|
282
|
+
end
|
283
|
+
|
284
|
+
v = v.to_f * 10
|
285
|
+
v = v.to_i
|
286
|
+
end
|
287
|
+
|
288
|
+
# The value of the PRODID field, an unstructured string meant to
|
289
|
+
# identify the software which encoded the Calendar data.
|
290
|
+
def producer
|
291
|
+
#f = @properties.field('PRODID')
|
292
|
+
#f && f.to_text
|
293
|
+
@properties.text('PRODID').first
|
294
|
+
end
|
295
|
+
|
296
|
+
# The value of the METHOD field. Protocol methods are used when iCalendars
|
297
|
+
# are exchanged in a calendar messaging system, such as iTIP or iMIP. When
|
298
|
+
# METHOD is not specified, the Calendar object is merely being used to
|
299
|
+
# transport a snapshot of some calendar information; without the intention
|
300
|
+
# of conveying a scheduling semantic.
|
301
|
+
#
|
302
|
+
# Note that this method can't be called +method+, thats already a method of
|
303
|
+
# Object.
|
304
|
+
def protocol
|
305
|
+
m = @properties['METHOD']
|
306
|
+
m ? m.upcase : m
|
307
|
+
end
|
308
|
+
|
309
|
+
# The value of the CALSCALE: property, or "GREGORIAN" if CALSCALE: is not
|
310
|
+
# present.
|
311
|
+
#
|
312
|
+
# This is of academic interest only. There aren't any other calendar scales
|
313
|
+
# defined, and given that its hard enough just dealing with Gregorian
|
314
|
+
# calendars, there probably won't be.
|
315
|
+
def calscale
|
316
|
+
(@properties['CALSCALE'] || 'GREGORIAN').upcase
|
317
|
+
end
|
318
|
+
|
319
|
+
# The array of all supported calendar components. If a class is provided,
|
320
|
+
# return only the components of that class.
|
321
|
+
#
|
322
|
+
# If a block is provided, yield the components instead of returning them.
|
323
|
+
#
|
324
|
+
# Examples:
|
325
|
+
# calendar.components(Vpim::Icalendar::Vevent)
|
326
|
+
# => array of all calendar components
|
327
|
+
#
|
328
|
+
# calendar.components(Vpim::Icalendar::Vtodo) {|c| c... }
|
329
|
+
# => yield all todo components
|
330
|
+
#
|
331
|
+
# calendar.components {|c| c... }
|
332
|
+
# => yield all components
|
333
|
+
#
|
334
|
+
# Note - use of this is mildly deprecated in favour of #each, #events,
|
335
|
+
# #todos, #journals because those won't return timezones, and will return
|
336
|
+
# Enumerators if called without a block.
|
337
|
+
def components(klass=Object) #:yields:component
|
338
|
+
klass ||= Object
|
339
|
+
|
340
|
+
unless block_given?
|
341
|
+
return @components.select{|c| klass === c}.freeze
|
342
|
+
end
|
343
|
+
|
344
|
+
@components.each do |c|
|
345
|
+
if klass === c
|
346
|
+
yield c
|
347
|
+
end
|
348
|
+
end
|
349
|
+
self
|
350
|
+
end
|
351
|
+
|
352
|
+
include Enumerable
|
353
|
+
|
354
|
+
# Enumerate the top-level calendar components. Yields them if a block
|
355
|
+
# is provided, otherwise returns an Enumerator.
|
356
|
+
#
|
357
|
+
# This skips components that are only internally meaningful to iCalendar,
|
358
|
+
# such as timezone definitions.
|
359
|
+
def each(klass=nil, &block) # :yield: component
|
360
|
+
unless block
|
361
|
+
return Enumerable::Enumerator.new(self, :each, klass)
|
362
|
+
end
|
363
|
+
components(klass, &block)
|
364
|
+
end
|
365
|
+
|
366
|
+
# Short-hand for #each(Icalendar::Vevent).
|
367
|
+
def events(&block) #:yield: Vevent
|
368
|
+
each(Icalendar::Vevent, &block)
|
369
|
+
end
|
370
|
+
|
371
|
+
# Short-hand for #each(Icalendar::Vtodo).
|
372
|
+
def todos(&block) #:yield: Vtodo
|
373
|
+
each(Icalendar::Vtodo, &block)
|
374
|
+
end
|
375
|
+
|
376
|
+
# Short-hand for #each(Icalendar::Vjournal).
|
377
|
+
def journals(&block) #:yield: Vjournal
|
378
|
+
each(Icalendar::Vjournal, &block)
|
379
|
+
end
|
380
|
+
|
381
|
+
end
|
382
|
+
|
383
|
+
end
|
384
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 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/vcard'
|
10
|
+
|
11
|
+
module Vpim
|
12
|
+
module Maker #:nodoc:backwards compat
|
13
|
+
Vcard = Vpim::Vcard::Maker #:nodoc:backwards compat
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,193 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 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
|
+
module Vpim
|
10
|
+
class Icalendar
|
11
|
+
module Set #:nodoc:
|
12
|
+
module Util #:nodoc:
|
13
|
+
# TODO - rename module to Private?
|
14
|
+
|
15
|
+
def rm_all(name)
|
16
|
+
rm = @comp.properties.select { |f| f.name? name }
|
17
|
+
rm.each { |f| @comp.properties.delete(f) }
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_token(name, allowed, default, value) #:nodoc:
|
21
|
+
value = value.to_str
|
22
|
+
unless allowed.include?(value)
|
23
|
+
raise Vpim::Unencodeable, "Invalid #{name} value '#{value}'"
|
24
|
+
end
|
25
|
+
rm_all(name)
|
26
|
+
unless value == default
|
27
|
+
@comp.properties.push Vpim::DirectoryInfo::Field.create(name, value)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def field_create(name, value, default_value_type = nil, value_type = nil, params = {})
|
32
|
+
if value_type && value_type != default_value_type
|
33
|
+
params['VALUE'] = value_type
|
34
|
+
end
|
35
|
+
Vpim::DirectoryInfo::Field.create(name, value, params)
|
36
|
+
end
|
37
|
+
|
38
|
+
def set_date_or_datetime(name, default, value)
|
39
|
+
f = nil
|
40
|
+
case value
|
41
|
+
when Date
|
42
|
+
f = field_create(name, Vpim.encode_date(value), default, 'DATE')
|
43
|
+
when Time
|
44
|
+
f = field_create(name, Vpim.encode_date_time(value), default, 'DATE-TIME')
|
45
|
+
else
|
46
|
+
raise Vpim::Unencodeable, "Invalid #{name} value #{value.inspect}"
|
47
|
+
end
|
48
|
+
rm_all(name)
|
49
|
+
@comp.properties.push(f)
|
50
|
+
end
|
51
|
+
|
52
|
+
def set_datetime(name, value)
|
53
|
+
f = field_create(name, Vpim.encode_date_time(value))
|
54
|
+
rm_all(name)
|
55
|
+
@comp.properties.push(f)
|
56
|
+
end
|
57
|
+
|
58
|
+
def set_text(name, value)
|
59
|
+
f = field_create(name, Vpim.encode_text(value))
|
60
|
+
rm_all(name)
|
61
|
+
@comp.properties.push(f)
|
62
|
+
end
|
63
|
+
|
64
|
+
def set_text_list(name, value)
|
65
|
+
f = field_create(name, Vpim.encode_text_list(value))
|
66
|
+
rm_all(name)
|
67
|
+
@comp.properties.push(f)
|
68
|
+
end
|
69
|
+
|
70
|
+
def set_integer(name, value)
|
71
|
+
value = value.to_int.to_s
|
72
|
+
f = field_create(name, value)
|
73
|
+
rm_all(name)
|
74
|
+
@comp.properties.push(f)
|
75
|
+
end
|
76
|
+
|
77
|
+
def add_address(name, value)
|
78
|
+
f = value.encode(name)
|
79
|
+
@comp.properties.push(f)
|
80
|
+
end
|
81
|
+
|
82
|
+
def set_address(name, value)
|
83
|
+
rm_all(name)
|
84
|
+
add_address(name, value)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
module Property #:nodoc:
|
91
|
+
|
92
|
+
# FIXME - rename Base to Util
|
93
|
+
module Base #:nodoc:
|
94
|
+
# Value of first property with name +name+
|
95
|
+
def propvalue(name) #:nodoc:
|
96
|
+
prop = @properties.detect { |f| f.name? name }
|
97
|
+
if prop
|
98
|
+
prop = prop.value
|
99
|
+
end
|
100
|
+
prop
|
101
|
+
end
|
102
|
+
|
103
|
+
# Array of values of all properties with name +name+
|
104
|
+
def propvaluearray(name) #:nodoc:
|
105
|
+
@properties.select{ |f| f.name? name }.map{ |p| p.value }
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
def propinteger(name) #:nodoc:
|
110
|
+
prop = @properties.detect { |f| f.name? name }
|
111
|
+
if prop
|
112
|
+
prop = Vpim.decode_integer(prop.value)
|
113
|
+
end
|
114
|
+
prop
|
115
|
+
end
|
116
|
+
|
117
|
+
def proptoken(name, allowed, default_token = nil) #:nodoc:
|
118
|
+
prop = propvalue(name)
|
119
|
+
|
120
|
+
if prop
|
121
|
+
prop = prop.to_str.upcase
|
122
|
+
unless allowed.include?(prop)
|
123
|
+
raise Vpim::InvalidEncodingError, "Invalid #{name} value '#{prop}'"
|
124
|
+
end
|
125
|
+
else
|
126
|
+
prop = default_token
|
127
|
+
end
|
128
|
+
|
129
|
+
prop
|
130
|
+
end
|
131
|
+
|
132
|
+
# Value as DATE-TIME or DATE of object of first property with name +name+
|
133
|
+
def proptime(name) #:nodoc:
|
134
|
+
prop = @properties.detect { |f| f.name? name }
|
135
|
+
if prop
|
136
|
+
prop = prop.to_time.first
|
137
|
+
end
|
138
|
+
prop
|
139
|
+
end
|
140
|
+
|
141
|
+
# Value as TEXT of first property with name +name+
|
142
|
+
def proptext(name) #:nodoc:
|
143
|
+
prop = @properties.detect { |f| f.name? name }
|
144
|
+
if prop
|
145
|
+
prop = prop.to_text
|
146
|
+
end
|
147
|
+
prop
|
148
|
+
end
|
149
|
+
|
150
|
+
# Array of values as TEXT of all properties with name +name+
|
151
|
+
def proptextarray(name) #:nodoc:
|
152
|
+
@properties.select{ |f| f.name? name }.map{ |p| p.to_text }
|
153
|
+
end
|
154
|
+
|
155
|
+
# Array of values as TEXT list of all properties with name +name+
|
156
|
+
def proptextlistarray(name) #:nodoc:
|
157
|
+
@properties.select{ |f| f.name? name }.map{ |p| Vpim.decode_text_list(p.value_raw) }.flatten
|
158
|
+
end
|
159
|
+
|
160
|
+
# Duration has "almost" the same definition for Event and Todo
|
161
|
+
def propduration(endfield)
|
162
|
+
dur = @properties.field 'DURATION'
|
163
|
+
dte = @properties.field endfield
|
164
|
+
|
165
|
+
if !dur
|
166
|
+
return nil unless dte
|
167
|
+
|
168
|
+
b = dtstart
|
169
|
+
e = send(endfield.downcase.to_sym)
|
170
|
+
|
171
|
+
return (e - b).to_i
|
172
|
+
end
|
173
|
+
|
174
|
+
Icalendar.decode_duration(dur.value_raw)
|
175
|
+
end
|
176
|
+
|
177
|
+
def propend(endfield)
|
178
|
+
dte = @properties.field endfield.to_s.upcase
|
179
|
+
if dte
|
180
|
+
dte.to_time.first
|
181
|
+
elsif duration
|
182
|
+
dtstart + duration
|
183
|
+
else
|
184
|
+
nil
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|