vpim 0.323 → 0.357

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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