vpim 0.16 → 0.17

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,5 @@
1
1
  =begin
2
- $Id: vcard.rb,v 1.7 2005/02/04 21:09:34 sam Exp $
3
-
4
- Copyright (C) 2005 Sam Roberts
2
+ Copyright (C) 2006 Sam Roberts
5
3
 
6
4
  This library is free software; you can redistribute it and/or modify it
7
5
  under the same terms as the ruby language itself, see the file COPYING for
@@ -11,49 +9,67 @@
11
9
  require 'vpim/vcard'
12
10
 
13
11
  module Vpim
14
- module Maker
12
+ module Maker #:nodoc:
15
13
  # A helper class to assist in building a vCard.
16
14
  #
17
- # This idea is modelled after ruby 1.8's rss/maker classes. Perhaps all these methods
18
- # should be added to Vpim::Vcard?
15
+ # Examples:
16
+ # - link:ex_mkvcard.txt: example of creating a vCard
17
+ # - link:ex_cpvcard.txt: example of copying and them modifying a vCard
18
+ # - link:ex_mkv21vcard.txt: example of creating version 2.1 vCard
19
19
  class Vcard
20
- # Make a vCard for +full_name+.
20
+ # Make a vCard.
21
+ #
22
+ # Yields +maker+, a Vpim::Maker::Vcard which allows fields to be added to
23
+ # +card+, and returns +card+, a Vpim::Vcard.
21
24
  #
22
- # Yields +card+, a Vpim::Maker::Vcard to which fields can be added, and returns a Vpim::Vcard.
25
+ # If +card+ is nil or not provided a new Vpim::Vcard is created and the
26
+ # fields are added to it.
27
+ #
28
+ # Defaults:
29
+ # - vCards must have both an N: and an FN: field, #make2 will fail if there
30
+ # is no FN: field in the +card+ when your block is finished adding fields.
31
+ # - If there is an FN: field, but no N: field, N: will be set from the information
32
+ # in FN:, see Vcard::Name#preformatted for more information.
33
+ # - vCards must have a VERSION: field. If one does not exist when your block is
34
+ # is finished adding fields then it will be set to 3.0.
35
+ def Vcard.make2(card = Vpim::Vcard.create, &block) # :yields: maker
36
+ new(nil, card).make(&block)
37
+ end
38
+
39
+ # Deprecated, use #make2.
23
40
  #
24
- # Note that calling #add_name is required, all other fields are optional.
25
- def Vcard.make(full_name, &block) # :yields: +card+
26
- new(full_name).make(&block)
41
+ # If set, the FN: field will be set to +full_name+. Otherwise, FN: will
42
+ # be set from the values in #add_name.
43
+ def Vcard.make(full_name = nil, &block) # :yields: maker
44
+ new(full_name, Vpim::Vcard.create).make(&block)
27
45
  end
28
46
 
29
47
  def make # :nodoc:
30
48
  yield self
31
- if !@initialized_N
32
- raise Vpim::InvalidEncodingError, 'It is mandatory to have a N field, see #add_name.'
49
+ unless @card['N']
50
+ raise Vpim::InvalidEncodingError, 'It is mandatory to have a name field, see #add_name.'
51
+ end
52
+ unless @card['FN']
53
+ @card << Vpim::DirectoryInfo::Field.create('FN', Vpim::Vcard::Name.new(@card['N'], '').formatted)
54
+ end
55
+ unless @card['VERSION']
56
+ @card << Vpim::DirectoryInfo::Field.create('VERSION', "3.0")
33
57
  end
34
58
  @card
35
59
  end
36
60
 
37
61
  private
38
62
 
39
- def initialize(full_name) # :nodoc:
40
- @card = Vpim::Vcard::create
41
- @card << Vpim::DirectoryInfo::Field.create('FN', full_name )
42
- @initialized_N = false
43
- # pp @card
63
+ def initialize(full_name, card) # :nodoc:
64
+ @card = card || Vpim::Vcard::create
65
+ if full_name
66
+ @card << Vpim::DirectoryInfo::Field.create('FN', full_name )
67
+ end
44
68
  end
45
69
 
46
70
  public
47
71
 
48
- # Add an arbitrary Field, +field+.
49
- def add_field(field)
50
- @card << field
51
- end
52
-
53
- # Add a name field, N.
54
- #
55
- # Warning: This is the only mandatory field, besides the full name, which
56
- # is added from Vcard.make's +full_name+.
72
+ # Add a name field, N:.
57
73
  #
58
74
  # Attributes of N are:
59
75
  # - family: family name
@@ -64,6 +80,10 @@ module Vpim
64
80
  #
65
81
  # All attributes are optional.
66
82
  #
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
+ #
67
87
  # FIXME: is it possible to deduce given/family from the full_name?
68
88
  #
69
89
  # FIXME: Each attribute can currently only have a single String value.
@@ -76,13 +96,23 @@ module Vpim
76
96
  'N',
77
97
  x.map { |s| s ? s.to_str : '' }
78
98
  )
79
- @initialized_N = true
80
99
  self
81
100
  end
82
101
 
83
- # Add a address field, ADR.
102
+ # Add a full name field, FN.
84
103
  #
85
- # Attributes of ADR that describe the address are:
104
+ # Normally the FN field value is derived from the N: field value, but
105
+ # it can be explicitly set.
106
+ def fullname=(fullname)
107
+ if @card.field('FN')
108
+ raise Vpim::InvalidEncodingError, "Not allowed to add more than one FN field to a vCard."
109
+ end
110
+ @card << Vpim::DirectoryInfo::Field.create( 'FN', fullname );
111
+ end
112
+
113
+ # Add a address field, ADR:.
114
+ #
115
+ # Attributes of ADR: that describe the address are:
86
116
  # - pobox: post office box
87
117
  # - extended: seldom used, its not clear what it is for
88
118
  # - street: street address, multiple components should be separated by a comma, ','
@@ -99,9 +129,9 @@ module Vpim
99
129
  # All attributes are optional. #location and #home can be set to arrays of
100
130
  # strings.
101
131
  #
102
- # TODO: Add #label to support LABEL.
132
+ # TODO - Add #label to support LABEL.
103
133
  #
104
- # FIXME: Need to escape specials in the String.
134
+ # FIXME - Need to escape specials in the String.
105
135
  def add_addr # :yield: adr
106
136
  x = Struct.new(
107
137
  :location, :preferred, :delivery,
@@ -124,12 +154,12 @@ module Vpim
124
154
  self
125
155
  end
126
156
 
127
- # Add a telephone number field, TEL.
157
+ # Add a telephone number field, TEL:.
128
158
  #
129
159
  # +number+ is supposed to be a "X.500 Telephone Number" according to RFC 2426, if you happen
130
160
  # to be familiar with that. Otherwise, anything that looks like a phone number should be OK.
131
161
  #
132
- # Attributes of TEL are:
162
+ # Attributes of TEL: are:
133
163
  # - location: home, work, msg, cell, car, pager - often used, can be set to other values
134
164
  # - preferred: true - often used, set if this is the preferred telephone number
135
165
  # - capability: voice,fax,video,bbs,modem,isdn,pcs - fax is useful, the others are rarely used
@@ -153,9 +183,9 @@ module Vpim
153
183
  self
154
184
  end
155
185
 
156
- # Add a email address field, EMAIL.
186
+ # Add a email address field, EMAIL:.
157
187
  #
158
- # Attributes of EMAIL are:
188
+ # Attributes of EMAIL: are:
159
189
  # - location: home, work - often used, can be set to other values
160
190
  # - preferred: true - often used, set if this is the preferred email address
161
191
  # - protocol: internet,x400 - internet is the default, set this for other kinds
@@ -179,12 +209,12 @@ module Vpim
179
209
  self
180
210
  end
181
211
 
182
- # Add a nickname field, NICKNAME.
212
+ # Add a nickname field, NICKNAME:.
183
213
  def nickname=(nickname)
184
214
  @card << Vpim::DirectoryInfo::Field.create( 'NICKNAME', nickname );
185
215
  end
186
216
 
187
- # Add a birthday field, BDAY.
217
+ # Add a birthday field, BDAY:.
188
218
  #
189
219
  # +birthday+ must be a time or date object.
190
220
  #
@@ -205,10 +235,10 @@ TODO - need text=() implemented in Field
205
235
  end
206
236
  =end
207
237
 
208
- # Add an instant-messaging/point of presence address field, IMPP. The address
238
+ # Add an instant-messaging/point of presence address field, IMPP:. The address
209
239
  # is a URL, with the syntax depending on the protocol.
210
240
  #
211
- # Attributes of IMPP are:
241
+ # Attributes of IMPP: are:
212
242
  # - preferred: true - set if this is the preferred address
213
243
  # - location: home, work, mobile - location of address
214
244
  # - purpose: personal,business - purpose of communications
@@ -220,7 +250,7 @@ TODO - need text=() implemented in Field
220
250
  # the user to know the URL for their own address, hopefully not too much
221
251
  # of a burden.
222
252
  #
223
- # IMPP is defined in draft-jennings-impp-vcard-04.txt. It refers to the
253
+ # IMPP: is defined in draft-jennings-impp-vcard-04.txt. It refers to the
224
254
  # URI scheme of a number of messaging protocols, but doesn't give
225
255
  # references to all of them:
226
256
  # - "xmpp" indicates to use XMPP, draft-saintandre-xmpp-uri-06.txt
@@ -250,13 +280,13 @@ TODO - need text=() implemented in Field
250
280
  self
251
281
  end
252
282
 
253
- # Add an Apple style AIM account name, +xaim+ is an AIM screen name.
283
+ # Add an X-AIM: account name where +xaim+ is an AIM screen name.
254
284
  #
255
285
  # I don't know if this is conventional, or supported by anything other
256
286
  # than AddressBook.app, but an example is:
257
287
  # X-AIM;type=HOME;type=pref:exampleaccount
258
288
  #
259
- # Attributes of X-AIM are:
289
+ # Attributes of X-AIM: are:
260
290
  # - preferred: true - set if this is the preferred address
261
291
  # - location: home, work, mobile - location of address
262
292
  #
@@ -281,10 +311,10 @@ TODO - need text=() implemented in Field
281
311
  end
282
312
 
283
313
 
284
- # Add a photo field, PHOTO.
314
+ # Add a photo field, PHOTO:.
285
315
  #
286
- # Attributes of PHOTO are:
287
- # - image: set to image data to inclue inline
316
+ # Attributes of PHOTO: are:
317
+ # - image: set to image data to include inline
288
318
  # - link: set to the URL of the image data
289
319
  # - type: string identifying the image type, supposed to be an "IANA registered image format",
290
320
  # or a non-registered image format (usually these start with an x-)
@@ -331,6 +361,54 @@ TODO - need text=() implemented in Field
331
361
  self
332
362
  end
333
363
 
364
+ # Add a Field, +field+.
365
+ def add_field(field)
366
+ fieldname = field.name.upcase
367
+ case
368
+ when [ 'BEGIN', 'END' ].include?(fieldname)
369
+ raise Vpim::InvalidEncodingError, "Not allowed to manually add #{field.name} to a vCard."
370
+
371
+ when [ 'VERSION', 'N', 'FN' ].include?(fieldname)
372
+ if @card.field(fieldname)
373
+ raise Vpim::InvalidEncodingError, "Not allowed to add more than one #{fieldname} to a vCard."
374
+ end
375
+ @card << field
376
+
377
+ else
378
+ @card << field
379
+ end
380
+ end
381
+
382
+ # Copy the fields from +card+ into self using #add_field. If a block is
383
+ # provided, each Field from +card+ is yielded. The block should return a
384
+ # Field to add, or nil. The Field doesn't have to be the one yielded,
385
+ # allowing the field to be copied and modified (see Field#copy) before adding, or
386
+ # not added at all if the block yields nil.
387
+ #
388
+ # The vCard fields BEGIN: and END: aren't copied, and VERSION:, N:, and FN: are copied
389
+ # only if the card doesn't have them already.
390
+ def copy(card) # :yields: Field
391
+ card.each do |field|
392
+ fieldname = field.name.upcase
393
+ case
394
+ when [ 'BEGIN', 'END' ].include?(fieldname)
395
+ # Never copy these
396
+
397
+ when [ 'VERSION', 'N', 'FN' ].include?(fieldname) && @card.field(fieldname)
398
+ # Copy these only if they don't already exist.
399
+
400
+ else
401
+ if block_given?
402
+ field = yield field
403
+ end
404
+
405
+ if field
406
+ add_field(field)
407
+ end
408
+ end
409
+ end
410
+ end
411
+
334
412
  end
335
413
  end
336
414
  end
@@ -0,0 +1,382 @@
1
+ =begin
2
+ $Id: vcard.rb,v 1.7 2005/02/04 21:09:34 sam Exp $
3
+
4
+ Copyright (C) 2006 Sam Roberts
5
+
6
+ This library is free software; you can redistribute it and/or modify it
7
+ under the same terms as the ruby language itself, see the file COPYING for
8
+ details.
9
+ =end
10
+
11
+ require 'vpim/vcard'
12
+
13
+ module Vpim
14
+ module Maker
15
+ # A helper class to assist in building a vCard.
16
+ #
17
+ # Examples:
18
+ # - link:ex_mkvcard.txt: an example of making a vCard
19
+ # - link:ex_cpvcard.txt: an example of copying and modifying a vCard
20
+ #
21
+ # Note - The Maker module is modelled after rss/maker, but if a Vcard was
22
+ # mutable these methods could be added to Vpim::Vcard. Hm.
23
+ class Vcard
24
+ # Make a vCard for +full_name+.
25
+ #
26
+ # Yields +card+, a Vpim::Maker::Vcard to which fields can be added, and returns a Vpim::Vcard.
27
+ #
28
+ # Note that calling #add_name is required, all other fields are optional.
29
+ def Vcard.make(full_name, &block) # :yields: +card+
30
+ new(full_name).make(&block)
31
+ end
32
+
33
+ def make # :nodoc:
34
+ yield self
35
+ unless @card['N']
36
+ raise Vpim::InvalidEncodingError, 'It is mandatory to have a name field, see #add_name.'
37
+ end
38
+ @card
39
+ end
40
+
41
+ private
42
+
43
+ def initialize(full_name) # :nodoc:
44
+ @card = Vpim::Vcard::create
45
+ @card << Vpim::DirectoryInfo::Field.create('FN', full_name )
46
+ # pp @card
47
+ end
48
+
49
+ public
50
+
51
+ # Add an arbitrary Field, +field+.
52
+ def add_field(field)
53
+ fieldname = field.name.upcase
54
+ case
55
+ when [ 'BEGIN', 'END', 'VERSION' ].include?(fieldname)
56
+ raise Vpim::InvalidEncodingError, "Not allowed to manually add #{field.name} to a vCard."
57
+
58
+ when [ 'N', 'FN' ].include?(fieldname)
59
+ if @card.field(fieldname)
60
+ raise Vpim::InvalidEncodingError, "Not allowed to add more than one #{fieldname} to a vCard."
61
+ end
62
+ @card << field
63
+
64
+ else
65
+ @card << field
66
+ end
67
+ end
68
+
69
+ # Copy the fields from +card+ into self using #add_field. If a block is
70
+ # provided, each Field from +card+ is yielded. The block should return a
71
+ # Field to add, or nil. The Field doesn't have to be the one yielded,
72
+ # this allows modified fields to be copied in (use Field#copy), or fields
73
+ # to be filtered out (if the block yields nil).
74
+ #
75
+ # The vCard fields BEGIN, END, VERSION aren't copied, and N and FN aren't copied
76
+ # if the target already has them
77
+ def copy(card) # :yields: Field
78
+ card.each do |field|
79
+ fieldname = field.name.upcase
80
+ case
81
+ when [ 'BEGIN', 'END', 'VERSION' ].include?(fieldname)
82
+ # Never copy these
83
+
84
+ when [ 'N', 'FN' ].include?(fieldname) && @card.field(fieldname)
85
+ # Copy these only if they don't already exist.
86
+
87
+ else
88
+ if block_given?
89
+ field = yield field
90
+ end
91
+
92
+ if field
93
+ add_field(field)
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ # Add a name field, N.
100
+ #
101
+ # Warning: This is the only mandatory field, besides the full name, which
102
+ # is added from Vcard.make's +full_name+.
103
+ #
104
+ # Attributes of N are:
105
+ # - family: family name
106
+ # - given: given name
107
+ # - additional: additional names
108
+ # - prefix: such as "Ms." or "Dr."
109
+ # - suffix: such as "BFA", or "Sensei"
110
+ #
111
+ # All attributes are optional.
112
+ #
113
+ # FIXME: is it possible to deduce given/family from the full_name?
114
+ #
115
+ # FIXME: Each attribute can currently only have a single String value.
116
+ #
117
+ # FIXME: Need to escape specials in the String.
118
+ def add_name # :yield: n
119
+ x = Struct.new(:family, :given, :additional, :prefix, :suffix).new
120
+ yield x
121
+ @card << Vpim::DirectoryInfo::Field.create(
122
+ 'N',
123
+ x.map { |s| s ? s.to_str : '' }
124
+ )
125
+ self
126
+ end
127
+
128
+ # Add a address field, ADR.
129
+ #
130
+ # Attributes of ADR that describe the address are:
131
+ # - pobox: post office box
132
+ # - extended: seldom used, its not clear what it is for
133
+ # - street: street address, multiple components should be separated by a comma, ','
134
+ # - locality: usually the city
135
+ # - region: usually the province or state
136
+ # - postalcode: postal code
137
+ # - country: country name, no standard for country naming is specified
138
+ #
139
+ # Attributes that describe how the address is used, and customary values, are:
140
+ # - location: home, work - often used, can be set to other values
141
+ # - preferred: true - often used, set if this is the preferred address
142
+ # - delivery: postal, parcel, dom (domestic), intl (international) - rarely used
143
+ #
144
+ # All attributes are optional. #location and #home can be set to arrays of
145
+ # strings.
146
+ #
147
+ # TODO: Add #label to support LABEL.
148
+ #
149
+ # FIXME: Need to escape specials in the String.
150
+ def add_addr # :yield: adr
151
+ x = Struct.new(
152
+ :location, :preferred, :delivery,
153
+ :pobox, :extended, :street, :locality, :region, :postalcode, :country
154
+ ).new
155
+ yield x
156
+
157
+ values = x.to_a[3, 7].map { |s| s ? s.to_str : '' }
158
+
159
+ # All these attributes go into the TYPE parameter.
160
+ params = [ x[:location], x[:delivery] ]
161
+ params << 'pref' if x[:preferred]
162
+ params = params.flatten.uniq.compact.map { |s| s.to_str }
163
+
164
+ paramshash = {}
165
+
166
+ paramshash['type'] = params if params.first
167
+
168
+ @card << Vpim::DirectoryInfo::Field.create( 'ADR', values, paramshash)
169
+ self
170
+ end
171
+
172
+ # Add a telephone number field, TEL.
173
+ #
174
+ # +number+ is supposed to be a "X.500 Telephone Number" according to RFC 2426, if you happen
175
+ # to be familiar with that. Otherwise, anything that looks like a phone number should be OK.
176
+ #
177
+ # Attributes of TEL are:
178
+ # - location: home, work, msg, cell, car, pager - often used, can be set to other values
179
+ # - preferred: true - often used, set if this is the preferred telephone number
180
+ # - capability: voice,fax,video,bbs,modem,isdn,pcs - fax is useful, the others are rarely used
181
+ #
182
+ # All attributes are optional, and so is the block.
183
+ def add_tel(number) # :yield: tel
184
+ params = {}
185
+ if block_given?
186
+ x = Struct.new( :location, :preferred, :capability ).new
187
+
188
+ yield x
189
+
190
+ x[:preferred] = 'pref' if x[:preferred]
191
+
192
+ types = x.to_a.flatten.uniq.compact.map { |s| s.to_str }
193
+
194
+ params['type'] = types if types.first
195
+ end
196
+
197
+ @card << Vpim::DirectoryInfo::Field.create( 'TEL', number, params)
198
+ self
199
+ end
200
+
201
+ # Add a email address field, EMAIL.
202
+ #
203
+ # Attributes of EMAIL are:
204
+ # - location: home, work - often used, can be set to other values
205
+ # - preferred: true - often used, set if this is the preferred email address
206
+ # - protocol: internet,x400 - internet is the default, set this for other kinds
207
+ #
208
+ # All attributes are optional, and so is the block.
209
+ def add_email(email) # :yield: email
210
+ params = {}
211
+ if block_given?
212
+ x = Struct.new( :location, :preferred, :protocol ).new
213
+
214
+ yield x
215
+
216
+ x[:preferred] = 'pref' if x[:preferred]
217
+
218
+ types = x.to_a.flatten.uniq.compact.map { |s| s.to_str }
219
+
220
+ params['type'] = types if types.first
221
+ end
222
+
223
+ @card << Vpim::DirectoryInfo::Field.create( 'EMAIL', email, params)
224
+ self
225
+ end
226
+
227
+ # Add a nickname field, NICKNAME.
228
+ def nickname=(nickname)
229
+ @card << Vpim::DirectoryInfo::Field.create( 'NICKNAME', nickname );
230
+ end
231
+
232
+ # Add a birthday field, BDAY.
233
+ #
234
+ # +birthday+ must be a time or date object.
235
+ #
236
+ # Warning: It may confuse both humans and software if you add multiple
237
+ # birthdays.
238
+ def birthday=(birthday)
239
+ if !birthday.respond_to? :month
240
+ raise Vpim::InvalidEncodingError, 'birthday doesn\'t have #month, so it is not a date or time object.'
241
+ end
242
+ @card << Vpim::DirectoryInfo::Field.create( 'BDAY', birthday );
243
+ end
244
+ =begin
245
+ TODO - need text=() implemented in Field
246
+
247
+ # Add a note field, NOTE. It can contain newlines, they will be escaped.
248
+ def note=(note)
249
+ @card << Vpim::DirectoryInfo::Field.create( 'NOTE', note );
250
+ end
251
+ =end
252
+
253
+ # Add an instant-messaging/point of presence address field, IMPP. The address
254
+ # is a URL, with the syntax depending on the protocol.
255
+ #
256
+ # Attributes of IMPP are:
257
+ # - preferred: true - set if this is the preferred address
258
+ # - location: home, work, mobile - location of address
259
+ # - purpose: personal,business - purpose of communications
260
+ #
261
+ # All attributes are optional, and so is the block.
262
+ #
263
+ # The URL syntaxes for the messaging schemes is fairly complicated, so I
264
+ # don't try and build the URLs here, maybe in the future. This forces
265
+ # the user to know the URL for their own address, hopefully not too much
266
+ # of a burden.
267
+ #
268
+ # IMPP is defined in draft-jennings-impp-vcard-04.txt. It refers to the
269
+ # URI scheme of a number of messaging protocols, but doesn't give
270
+ # references to all of them:
271
+ # - "xmpp" indicates to use XMPP, draft-saintandre-xmpp-uri-06.txt
272
+ # - "irc" or "ircs" indicates to use IRC, draft-butcher-irc-url-04.txt
273
+ # - "sip" indicates to use SIP/SIMPLE, RFC 3261
274
+ # - "im" or "pres" indicates to use a CPIM or CPP gateway, RFC 3860 and RFC 3859
275
+ # - "ymsgr" indicates to use yahoo
276
+ # - "msn" might indicate to use Microsoft messenger
277
+ # - "aim" indicates to use AOL
278
+ #
279
+ def add_impp(url) # :yield: impp
280
+ params = {}
281
+
282
+ if block_given?
283
+ x = Struct.new( :location, :preferred, :purpose ).new
284
+
285
+ yield x
286
+
287
+ x[:preferred] = 'pref' if x[:preferred]
288
+
289
+ types = x.to_a.flatten.uniq.compact.map { |s| s.to_str }
290
+
291
+ params['type'] = types if types.first
292
+ end
293
+
294
+ @card << Vpim::DirectoryInfo::Field.create( 'IMPP', url, params)
295
+ self
296
+ end
297
+
298
+ # Add an Apple style AIM account name, +xaim+ is an AIM screen name.
299
+ #
300
+ # I don't know if this is conventional, or supported by anything other
301
+ # than AddressBook.app, but an example is:
302
+ # X-AIM;type=HOME;type=pref:exampleaccount
303
+ #
304
+ # Attributes of X-AIM are:
305
+ # - preferred: true - set if this is the preferred address
306
+ # - location: home, work, mobile - location of address
307
+ #
308
+ # All attributes are optional, and so is the block.
309
+ def add_x_aim(xaim) # :yield: xaim
310
+ params = {}
311
+
312
+ if block_given?
313
+ x = Struct.new( :location, :preferred ).new
314
+
315
+ yield x
316
+
317
+ x[:preferred] = 'pref' if x[:preferred]
318
+
319
+ types = x.to_a.flatten.uniq.compact.map { |s| s.to_str }
320
+
321
+ params['type'] = types if types.first
322
+ end
323
+
324
+ @card << Vpim::DirectoryInfo::Field.create( 'X-AIM', xaim, params)
325
+ self
326
+ end
327
+
328
+
329
+ # Add a photo field, PHOTO.
330
+ #
331
+ # Attributes of PHOTO are:
332
+ # - image: set to image data to inclue inline
333
+ # - link: set to the URL of the image data
334
+ # - type: string identifying the image type, supposed to be an "IANA registered image format",
335
+ # or a non-registered image format (usually these start with an x-)
336
+ #
337
+ # An error will be raised if neither image or link is set, or if both image
338
+ # and link is set.
339
+ #
340
+ # Setting type is optional for a link image, because either the URL, the
341
+ # image file extension, or a HTTP Content-Type may specify the type. If
342
+ # it's not a link, setting type is mandatory, though it can be set to an
343
+ # empty string, <code>''</code>, if the type is unknown.
344
+ #
345
+ # TODO - I'm not sure about this API. I'm thinking maybe it should be
346
+ # #add_photo(image, type), and that I should detect when the image is a
347
+ # URL, and make type mandatory if it wasn't a URL.
348
+ def add_photo # :yield: photo
349
+ x = Struct.new(:image, :link, :type).new
350
+ yield x
351
+ if x[:image] && x[:link]
352
+ raise Vpim::InvalidEncodingError, 'Image is not allowed to be both inline and a link.'
353
+ end
354
+
355
+ value = x[:image] || x[:link]
356
+
357
+ if !value
358
+ raise Vpim::InvalidEncodingError, 'A image link or inline data must be provided.'
359
+ end
360
+
361
+ params = {}
362
+
363
+ # Don't set type to the empty string.
364
+ params['type'] = x[:type] if( x[:type] && x[:type].length > 0 )
365
+
366
+ if x[:link]
367
+ params['value'] = 'uri'
368
+ else # it's inline, base-64 encode it
369
+ params['encoding'] = :b64
370
+ if !x[:type]
371
+ raise Vpim::InvalidEncodingError, 'Inline image data must have it\'s type set.'
372
+ end
373
+ end
374
+
375
+ @card << Vpim::DirectoryInfo::Field.create( 'PHOTO', value, params )
376
+ self
377
+ end
378
+
379
+ end
380
+ end
381
+ end
382
+