vpim 0.17 → 0.323

Sign up to get free protection for your applications and to get access to all the features.
data/lib/vpim/dirinfo.rb CHANGED
@@ -20,7 +20,7 @@ module Vpim
20
20
  #
21
21
  # A vCard, for example, is a specialization of a directory info object.
22
22
  #
23
- # [RFC2425] the directory information framework (ftp://ftp.ietf.org/rfc/rfc2425.txt)
23
+ # - [RFC2425] the directory information framework (ftp://ftp.ietf.org/rfc/rfc2425.txt)
24
24
  class DirectoryInfo
25
25
  include Enumerable
26
26
 
@@ -42,7 +42,7 @@ module Vpim
42
42
  # Decode +card+ into a DirectoryInfo object.
43
43
  #
44
44
  # +card+ may either be a something that is convertible to a string using
45
- # #to_str or an array of objects that can be joined into a string using
45
+ # #to_str or an Array of objects that can be joined into a string using
46
46
  # #join("\n"), or an IO object (which will be read to end-of-file).
47
47
  #
48
48
  # The lines in the string may be delimited using IETF (CRLF) or Unix (LF) conventions.
@@ -108,7 +108,13 @@ module Vpim
108
108
  #
109
109
  # TODO - call this #texts(), as in the plural?
110
110
  def text(name)
111
- enum_by_name(name).map { |f| f.to_text }
111
+ accum = []
112
+ each do |f|
113
+ if f.name? name
114
+ accum << f.to_text
115
+ end
116
+ end
117
+ accum
112
118
  end
113
119
 
114
120
  # Array of all the Field#group()s.
@@ -161,9 +167,9 @@ module Vpim
161
167
  # end
162
168
  # end
163
169
  #
164
- # or to get an array of all the fields in group 'agroup', you could do:
170
+ # or to get an array of all the fields in group 'AGROUP', you could do:
165
171
  #
166
- # card.enum_by_group('agroup').to_a
172
+ # card.enum_by_group('AGROUP').to_a
167
173
  def enum_by_group(group)
168
174
  Enumerator.new(self, Proc.new { |field| field.group?(group) })
169
175
  end
data/lib/vpim/field.rb CHANGED
@@ -67,7 +67,7 @@ module Vpim
67
67
  pvalues.each do |pvalue|
68
68
  # check if we need to do any encoding
69
69
  if Vpim::Methods.casecmp?(pname, 'ENCODING') && pvalue == :b64
70
- pvalue = 'b' # the RFC definition of the base64 param value
70
+ pvalue = 'B' # the RFC definition of the base64 param value
71
71
  value = [ value.to_str ].pack('m').gsub("\n", '')
72
72
  end
73
73
 
@@ -139,7 +139,7 @@ module Vpim
139
139
  params = $3
140
140
 
141
141
  # v2.1 params have no '=' sign, figure out what kind of param it
142
- # is (either its a known encoding, or we treat it as a 'type'
142
+ # is (either its a known encoding, or we treat it as a 'TYPE'
143
143
  # param).
144
144
 
145
145
  if $2 == ""
@@ -192,9 +192,9 @@ module Vpim
192
192
  # name (a String) to either a single string or symbol, or an array of
193
193
  # strings and symbols (parameters can be multi-valued).
194
194
  #
195
- # If 'encoding' => :b64 is specified as a parameter, the value will be
195
+ # If 'ENCODING' => :b64 is specified as a parameter, the value will be
196
196
  # base-64 encoded. If it's already base-64 encoded, then use String
197
- # values ('encoding' => 'b'), and no further encoding will be done by
197
+ # values ('ENCODING' => 'B'), and no further encoding will be done by
198
198
  # this routine.
199
199
  #
200
200
  # Currently handled value types are:
@@ -290,16 +290,21 @@ module Vpim
290
290
  # Note: Both the RFC 2425 encoding param ("b", meaning base-64) and the
291
291
  # vCard 2.1 encoding params ("base64", "quoted-printable", "8bit", and
292
292
  # "7bit") are supported.
293
+ #
294
+ # FIXME:
295
+ # - should use the VALUE parameter
296
+ # - should also take a default value type, so it can be converted
297
+ # if VALUE parameter is not present.
293
298
  def value
294
299
  case encoding
295
- when nil, '8bit', '7bit' then @value
300
+ when nil, '8BIT', '7BIT' then @value
296
301
 
297
302
  # Hack - if the base64 lines started with 2 SPC chars, which is invalid,
298
303
  # there will be extra spaces in @value. Since no SPC chars show up in
299
304
  # b64 encodings, they can be safely stripped out before unpacking.
300
- when 'b', 'base64' then @value.gsub(' ', '').unpack('m*').first
305
+ when 'B', 'BASE64' then @value.gsub(' ', '').unpack('m*').first
301
306
 
302
- when 'quoted-printable' then @value.unpack('M*').first
307
+ when 'QUOTED-PRINTABLE' then @value.unpack('M*').first
303
308
 
304
309
  else
305
310
  raise Vpim::InvalidEncodingError, "unrecognized encoding (#{encoding})"
@@ -346,7 +351,7 @@ module Vpim
346
351
  def type?(type)
347
352
  type = type.to_str
348
353
 
349
- types = param('type')
354
+ types = param('TYPE')
350
355
 
351
356
  if types
352
357
  types = types.detect { |t| Vpim::Methods.casecmp?(t, type) }
@@ -354,18 +359,18 @@ module Vpim
354
359
  end
355
360
 
356
361
  # Is this field marked as preferred? A vCard field is preferred if
357
- # #type?('pref'). This method is not necessarily meaningful for
362
+ # #type?('PREF'). This method is not necessarily meaningful for
358
363
  # non-vCard profiles.
359
364
  def pref?
360
- type? 'pref'
365
+ type? 'PREF'
361
366
  end
362
367
 
363
368
  # Set whether a field is marked as preferred. See #pref?
364
369
  def pref=(ispref)
365
370
  if ispref
366
- pvalue_iadd('type', 'pref')
371
+ pvalue_iadd('TYPE', 'PREF')
367
372
  else
368
- pvalue_idel('type', 'pref')
373
+ pvalue_idel('TYPE', 'PREF')
369
374
  end
370
375
  end
371
376
 
@@ -378,13 +383,13 @@ module Vpim
378
383
  # The value of the ENCODING parameter, if present, or nil if not
379
384
  # present.
380
385
  def encoding
381
- e = param("encoding")
386
+ e = param('ENCODING')
382
387
 
383
388
  if e
384
389
  if e.length > 1
385
- raise Vpim::InvalidEncodingError, "multi-valued param 'encoding' (#{e})"
390
+ raise Vpim::InvalidEncodingError, "multi-valued param 'ENCODING' (#{e})"
386
391
  end
387
- e = e.first.downcase
392
+ e = e.first.upcase
388
393
  end
389
394
  e
390
395
  end
@@ -392,10 +397,10 @@ module Vpim
392
397
  # The type of the value, as specified by the VALUE parameter, nil if
393
398
  # unspecified.
394
399
  def kind
395
- v = param('value')
400
+ v = param('VALUE')
396
401
  if v
397
402
  if v.size > 1
398
- raise InvalidEncodingError, "multi-valued param 'value' (#{values})"
403
+ raise InvalidEncodingError, "multi-valued param 'VALUE' (#{values})"
399
404
  end
400
405
  v = v.first
401
406
  end
@@ -465,7 +470,7 @@ module Vpim
465
470
  # characters, this method will strip them, if present.
466
471
  #
467
472
  # In theory, #value could also do this, but it would need to know that
468
- # the value is of type 'text', and often for text values the 'type'
473
+ # the value is of type 'TEXT', and often for text values the 'VALUE'
469
474
  # parameter is not present, so knowledge of the expected type of the
470
475
  # field is required from the decoder.
471
476
  def to_text
@@ -502,10 +507,10 @@ module Vpim
502
507
  # currently has. See Field.create() for a description of +pvalue+.
503
508
  #
504
509
  # Example:
505
- # if field['type']
506
- # field['type'] << 'home'
510
+ # if field['TYPE']
511
+ # field['TYPE'] << 'HOME'
507
512
  # else
508
- # field['type'] = [ 'home'
513
+ # field['TYPE'] = [ 'HOME' ]
509
514
  # end
510
515
  #
511
516
  # TODO - this could be an alias to #pvalue_set
@@ -12,106 +12,6 @@ require 'vpim/rrule'
12
12
  require 'vpim/vevent'
13
13
  require 'vpim/vpim'
14
14
 
15
- =begin
16
-
17
- ... ; y/n (whether I've seen it in Apple's calendars)
18
- name
19
- section
20
- type/comments
21
-
22
-
23
- icalbody = icalprops component
24
-
25
- icalprops =
26
- prodid / ; y PRODID 4.7.3 required, TEXT
27
- version / ; y 4.7.4 required, TEXT, "2.0"
28
- calscal / ; y 4.7.1 only defined value is GREGORIAN
29
- method / ; n METHOD 4.7.2 used with transport protocols
30
-
31
- component =
32
- eventc / ; y VEVENT 4.6.1
33
- todoc / ; y VTODO 4.6.2
34
- journalc / ; n
35
- freebusyc / ; n
36
- timezonec / ; n
37
-
38
- alarmc ; y VALARM 4.6.6 occurs inside a VEVENT or a VTODO
39
-
40
- class ; y CLASS 4.8.1.3 private/public/confidentical/... (default=public)
41
-
42
- comment ; n 4.8.1.4 TEXT
43
- description ; y 4.8.1.5 TEXT
44
- summary ; y 4.8.1.12 TEXT
45
- location ; y 4.8.1.7 TEXT intended venue
46
-
47
- priority ; n why? 4.8.1.9 INTEGER, why isn't this seen for my TODO items?
48
-
49
- status ; y 4.8.1.11 TEXT, different values defined for event, todo, journal
50
-
51
- Event: TENTATIVE, CONFIRMED, CANCELLED
52
-
53
- Todo: NEEDS-ACTION, COMPLETED, IN-PROCESS, CANCELLED
54
-
55
- Journal: DRAFT, FINAL, CANCELLED
56
-
57
- dtstart ; y DTSTART 4.8.2.4 DATE-TIME is default, value=date can be set
58
- dtend ; y DTEND 4.8.2.2 Unless it has Z (UTC), or a tzid, then it is local-time.
59
-
60
- dtstamp ; y DTSTAMP 4.8.7.2 DATE-TIME, creation time, inclusion is mandatory, but what does
61
- it mean? It seems to be when the icalendar was actually created (as opposed to when the user entered
62
- the information into the calendar database, for example), but in that case my Apple icalendars should
63
- have all components having the same DTSTAMP, but they don't!
64
-
65
- duration ; y DURATION 4.8.2.5 dur-value
66
-
67
- dur-value = (["+"] / "-") "P" (dur-date / dur-time / dur-week)
68
-
69
- dur-date = dur-day [dur-time]
70
- = 1*DIGIT "D" [ dur-time ]
71
-
72
- dur-time = "T" (dur-hour / dur-minute / dur-second)
73
- = "T" (
74
- 1*DIGIT "H" [ 1*DIGIT "M" [ 1*DIGIT "S" ] ] /
75
- 1*DIGIT "M" [ 1*DIGIT "S" ] /
76
- 1*DIGIT "S"
77
- )
78
-
79
- dur-week = 1*DIGIT "W"
80
- dur-day = 1*DIGIT "D"
81
- dur-hour = 1*DIGIT "H" [dur-minute]
82
- dur-minute = 1*DIGIT "M" [dur-second]
83
- dur-second = 1*DIGIT "S"
84
-
85
- The EBNF is complicated, because they want to say that /some/ component
86
- must be present, and that if you have a "T", you need a time after it,
87
- and that you can't have an hour followed by seconds with no intervening
88
- minutes... but we don't care about that during decoding, so we rewrite
89
- the EBNF as:
90
-
91
- dur-value = ["+" / "-"] "P" [ 1*DIGIT "W" ] [ 1*DIGIT "D" ] [ "T" [ 1*DIGIT "H" ] [ 1*DIGIT "M" ] [ 1*DIGIT "S" ] ]
92
-
93
- dtdue ; n DTDUE 4.8.2.3
94
-
95
- uid ; y UID 4.8.4.7 TEXT, recommended to generate them in RFC822 form
96
-
97
- rrule ; y RRULE 4.8.5.4 RECUR, can occur multiple times!
98
-
99
-
100
-
101
- VEVENT Ifx:
102
-
103
- TEXT: summary, description, comment, location, uid
104
-
105
- think about: uid
106
-
107
- Vevent#status -> The status, upper-case.
108
- Vevent#status= -> set a new status, only allow the defined statuses!
109
- Vevent#status?(s) -> check if the status is s
110
-
111
- can contain alarms... should it include an alarms module?
112
-
113
- =end
114
-
115
15
  module Vpim
116
16
  # An iCalendar.
117
17
  #
@@ -167,12 +67,13 @@ module Vpim
167
67
  # Categorize the components
168
68
  @vevents = []
169
69
  @vtodos = []
70
+ @vjournals = []
170
71
  @others = []
171
72
 
172
73
  inner.each do |component|
173
74
  # First field in every component should be a "BEGIN:".
174
75
  name = component.first
175
- if ! name.name? 'begin'
76
+ if ! name.name? 'BEGIN'
176
77
  raise InvalidEncodingError, "calendar component begins with #{name.name}, instead of BEGIN!"
177
78
  end
178
79
 
@@ -181,6 +82,7 @@ module Vpim
181
82
  case name
182
83
  when 'VEVENT' then @vevents << Vevent.new(component)
183
84
  when 'VTODO' then @vtodos << Vtodo.new(component)
85
+ when 'VJOURNAL' then @vjournals << Vjournal.new(component)
184
86
  else @others << component
185
87
  end
186
88
  end
@@ -255,6 +157,8 @@ module Vpim
255
157
  @vevents << component
256
158
  when Vtodo
257
159
  @vtodos << component
160
+ when Vjournal
161
+ @vjournals << component
258
162
  else
259
163
  raise ArgumentError, "can't add component type #{component.type} to a calendar"
260
164
  end
@@ -359,23 +263,29 @@ module Vpim
359
263
  # transport a snapshot of some calendar information; without the intention
360
264
  # of conveying a scheduling semantic.
361
265
  #
362
- # Note that this can't be called 'method', that name is reserved.
266
+ # Note that this method can't be called +method+, thats already a method of
267
+ # Object.
363
268
  def protocol
364
269
  m = @properties['METHOD']
365
270
  m ? m.upcase : m
366
271
  end
367
272
 
368
- # The array of all calendar events (each is a Vevent).
273
+ # The array of all calendar event components (each is a Vevent).
369
274
  #
370
275
  # TODO - should this take an interval: t0,t1?
371
276
  def events
372
277
  @vevents
373
278
  end
374
279
 
375
- # The array of all calendar todos (each is a Vtodo).
280
+ # The array of all calendar todo components (each is a Vtodo).
376
281
  def todos
377
282
  @vtodos
378
283
  end
284
+
285
+ # The array of all calendar journal components (each is a Vjournal).
286
+ def journals
287
+ @vjournals
288
+ end
379
289
  end
380
290
 
381
291
  end
@@ -488,6 +398,10 @@ module Vpim
488
398
  end
489
399
  end
490
400
 
401
+ def inspect
402
+ "#<Vpim::Icalendar::Address:cn=#{cn.inspect} status=#{partstat} rsvp?=#{rsvp} #{uri.inspect}>"
403
+ end
404
+
491
405
  # The participation role for the calendar user specified by the address.
492
406
  #
493
407
  # The standard roles are:
@@ -526,13 +440,15 @@ module Vpim
526
440
  #
527
441
  # See #partstat.
528
442
  def partstat=(status)
529
- @field['partstat'] = status.to_str
443
+ @field['PARTSTAT'] = status.to_str
530
444
  status
531
445
  end
532
446
 
533
447
  # The value of the RSVP field, either +true+ or +false+. It is used to
534
448
  # specify whether there is an expectation of a favor of a reply from the
535
449
  # calendar user specified by the property value.
450
+ #
451
+ # TODO - should be #rsvp?
536
452
  def rsvp
537
453
  return false unless r = @field.param('RSVP')
538
454
  r = r.first
@@ -16,6 +16,7 @@ module Vpim
16
16
  # - link:ex_mkvcard.txt: example of creating a vCard
17
17
  # - link:ex_cpvcard.txt: example of copying and them modifying a vCard
18
18
  # - link:ex_mkv21vcard.txt: example of creating version 2.1 vCard
19
+ # - link:ex_mkyourown.txt: example of adding support for new fields to Maker::Vcard
19
20
  class Vcard
20
21
  # Make a vCard.
21
22
  #
@@ -80,16 +81,13 @@ module Vpim
80
81
  #
81
82
  # All attributes are optional.
82
83
  #
83
- # Warning: This is the only mandatory field besides the full name, FN,
84
- # but FN: can be set in #make, and if not set will be constucted as the
85
- # string "#{prefix} #{given} #{additional} #{family}, #{suffix}".
86
- #
87
- # FIXME: is it possible to deduce given/family from the full_name?
88
- #
89
- # FIXME: Each attribute can currently only have a single String value.
90
- #
91
- # FIXME: Need to escape specials in the String.
84
+ # Warning: This is the only mandatory field besides the full name, FN.
85
+ # FN: can be set in #make, or by #fullname=, and if not set will be
86
+ # constucted as the string "#{prefix} #{given} #{additional} #{family},
87
+ # #{suffix}".
92
88
  def add_name # :yield: n
89
+ # FIXME: Each attribute can currently only have a single String value.
90
+ # FIXME: Need to escape specials in the String.
93
91
  x = Struct.new(:family, :given, :additional, :prefix, :suffix).new
94
92
  yield x
95
93
  @card << Vpim::DirectoryInfo::Field.create(
@@ -101,8 +99,8 @@ module Vpim
101
99
 
102
100
  # Add a full name field, FN.
103
101
  #
104
- # Normally the FN field value is derived from the N: field value, but
105
- # it can be explicitly set.
102
+ # Normally the FN field value is derived from the N: field value, see
103
+ # #add_name, but it can be explicitly set.
106
104
  def fullname=(fullname)
107
105
  if @card.field('FN')
108
106
  raise Vpim::InvalidEncodingError, "Not allowed to add more than one FN field to a vCard."
@@ -130,9 +128,8 @@ module Vpim
130
128
  # strings.
131
129
  #
132
130
  # TODO - Add #label to support LABEL.
133
- #
134
- # FIXME - Need to escape specials in the String.
135
131
  def add_addr # :yield: adr
132
+ # FIXME - Need to escape specials in the String.
136
133
  x = Struct.new(
137
134
  :location, :preferred, :delivery,
138
135
  :pobox, :extended, :street, :locality, :region, :postalcode, :country
@@ -143,12 +140,12 @@ module Vpim
143
140
 
144
141
  # All these attributes go into the TYPE parameter.
145
142
  params = [ x[:location], x[:delivery] ]
146
- params << 'pref' if x[:preferred]
143
+ params << 'PREF' if x[:preferred]
147
144
  params = params.flatten.uniq.compact.map { |s| s.to_str }
148
145
 
149
146
  paramshash = {}
150
147
 
151
- paramshash['type'] = params if params.first
148
+ paramshash['TYPE'] = params if params.first
152
149
 
153
150
  @card << Vpim::DirectoryInfo::Field.create( 'ADR', values, paramshash)
154
151
  self
@@ -172,11 +169,11 @@ module Vpim
172
169
 
173
170
  yield x
174
171
 
175
- x[:preferred] = 'pref' if x[:preferred]
172
+ x[:preferred] = 'PREF' if x[:preferred]
176
173
 
177
174
  types = x.to_a.flatten.uniq.compact.map { |s| s.to_str }
178
175
 
179
- params['type'] = types if types.first
176
+ params['TYPE'] = types if types.first
180
177
  end
181
178
 
182
179
  @card << Vpim::DirectoryInfo::Field.create( 'TEL', number, params)
@@ -198,11 +195,11 @@ module Vpim
198
195
 
199
196
  yield x
200
197
 
201
- x[:preferred] = 'pref' if x[:preferred]
198
+ x[:preferred] = 'PREF' if x[:preferred]
202
199
 
203
200
  types = x.to_a.flatten.uniq.compact.map { |s| s.to_str }
204
201
 
205
- params['type'] = types if types.first
202
+ params['TYPE'] = types if types.first
206
203
  end
207
204
 
208
205
  @card << Vpim::DirectoryInfo::Field.create( 'EMAIL', email, params)
@@ -226,6 +223,7 @@ module Vpim
226
223
  end
227
224
  @card << Vpim::DirectoryInfo::Field.create( 'BDAY', birthday );
228
225
  end
226
+
229
227
  =begin
230
228
  TODO - need text=() implemented in Field
231
229
 
@@ -269,11 +267,11 @@ TODO - need text=() implemented in Field
269
267
 
270
268
  yield x
271
269
 
272
- x[:preferred] = 'pref' if x[:preferred]
270
+ x[:preferred] = 'PREF' if x[:preferred]
273
271
 
274
- types = x.to_a.flatten.uniq.compact.map { |s| s.to_str }
272
+ types = x.to_a.flatten.uniq.compact.map { |s| s.upcase }
275
273
 
276
- params['type'] = types if types.first
274
+ params['TYPE'] = types if types.first
277
275
  end
278
276
 
279
277
  @card << Vpim::DirectoryInfo::Field.create( 'IMPP', url, params)
@@ -299,11 +297,11 @@ TODO - need text=() implemented in Field
299
297
 
300
298
  yield x
301
299
 
302
- x[:preferred] = 'pref' if x[:preferred]
300
+ x[:preferred] = 'PREF' if x[:preferred]
303
301
 
304
- types = x.to_a.flatten.uniq.compact.map { |s| s.to_str }
302
+ types = x.to_a.flatten.uniq.compact.map { |s| s.upcase }
305
303
 
306
- params['type'] = types if types.first
304
+ params['TYPE'] = types if types.first
307
305
  end
308
306
 
309
307
  @card << Vpim::DirectoryInfo::Field.create( 'X-AIM', xaim, params)
@@ -346,12 +344,12 @@ TODO - need text=() implemented in Field
346
344
  params = {}
347
345
 
348
346
  # Don't set type to the empty string.
349
- params['type'] = x[:type] if( x[:type] && x[:type].length > 0 )
347
+ params['TYPE'] = x[:type] if( x[:type] && x[:type].length > 0 )
350
348
 
351
349
  if x[:link]
352
- params['value'] = 'uri'
350
+ params['VALUE'] = 'URI'
353
351
  else # it's inline, base-64 encode it
354
- params['encoding'] = :b64
352
+ params['ENCODING'] = :b64
355
353
  if !x[:type]
356
354
  raise Vpim::InvalidEncodingError, 'Inline image data must have it\'s type set.'
357
355
  end
@@ -361,6 +359,11 @@ TODO - need text=() implemented in Field
361
359
  self
362
360
  end
363
361
 
362
+ # Add a URL field, URL:.
363
+ def add_url(url)
364
+ @card << Vpim::DirectoryInfo::Field.create( 'URL', url.to_str );
365
+ end
366
+
364
367
  # Add a Field, +field+.
365
368
  def add_field(field)
366
369
  fieldname = field.name.upcase
@@ -0,0 +1,63 @@
1
+ module Vpim
2
+ class Icalendar
3
+ module Property #:nodoc:
4
+
5
+ # FIXME - these should be part of Dirinfo
6
+ module Base
7
+ # Value of first property with name +name+
8
+ def propvalue(name) #:nodoc:
9
+ prop = @properties.detect { |f| f.name? name }
10
+ if prop
11
+ prop = prop.value
12
+ end
13
+ prop
14
+ end
15
+
16
+ def proptoken(name, allowed, default_token = nil) #:nodoc:
17
+ prop = propvalue name
18
+
19
+ if prop
20
+ prop = prop.to_str.upcase
21
+ unless allowed.include?(prop)
22
+ raise Vpim::InvalidEncodingError, "Invalid #{name} value '#{prop}'"
23
+ end
24
+ else
25
+ prop = default_token
26
+ end
27
+
28
+ prop
29
+ end
30
+
31
+ # Value as DATE-TIME or DATE of object of first property with name +name+
32
+ def proptime(name) #:nodoc:
33
+ prop = @properties.detect { |f| f.name? name }
34
+ if prop
35
+ prop = prop.to_time.first
36
+ end
37
+ prop
38
+ end
39
+
40
+ # Value as TEXT of first property with name +name+
41
+ def proptext(name) #:nodoc:
42
+ prop = @properties.detect { |f| f.name? name }
43
+ if prop
44
+ prop = prop.to_text
45
+ end
46
+ prop
47
+ end
48
+
49
+ # Array of values as TEXT of all properties with name +name+
50
+ def proptextarray(name) #:nodoc:
51
+ @properties.select{ |f| f.name? name }.map{ |p| p.to_text }
52
+ end
53
+
54
+ # Array of values as TEXT list of all properties with name +name+
55
+ def proptextlistarray(name) #:nodoc:
56
+ @properties.select{ |f| f.name? name }.map{ |p| Vpim.decode_text_list(p.value_raw) }.flatten
57
+ end
58
+
59
+ end
60
+ end
61
+ end
62
+ end
63
+