vpim 0.323 → 0.357

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