vpim 0.357 → 0.360

Sign up to get free protection for your applications and to get access to all the features.
@@ -47,77 +47,31 @@ module Vpim
47
47
  # a field.
48
48
  #
49
49
  # Example:
50
+ #
50
51
  # ORGANIZER;CN="A. Person":mailto:a_person@example.com
52
+ #
51
53
  # ATTENDEE;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION
52
54
  # ;CN="Sam Roberts";RSVP=TRUE:mailto:SRoberts@example.com
53
55
  #
54
56
  class Address
55
57
 
56
- # Create an Address from a DirectoryInfo::Field, +field+.
57
- #
58
- # TODO - make private, and split into the encode/decode/create trinity.
59
- def initialize(field)
60
- unless field.value
61
- raise ArgumentError
62
- end
63
-
64
- @field = field
65
- end
66
-
67
- # Return a representation of this Address as a DirectoryInfo::Field.
68
- def field
69
- @field.copy
70
- end
71
-
72
58
  # Create a copy of Address. If the original Address was frozen, this one
73
59
  # won't be.
74
60
  def copy
75
- Marshal.load(Marshal.dump(self))
61
+ #Marshal.load(Marshal.dump(self))
62
+ self.dup.dirty
76
63
  end
77
64
 
78
- # Addresses in a CAL-ADDRESS are represented as a URI, usually a mailto URI.
79
- def uri
80
- @field.value
81
- end
82
-
83
- # Return true if the +uri+ is == to this address' URI. The comparison
84
- # is case-insensitive.
85
- #
86
- # FIXME - why case insensitive? Email addresses. Should use a URI library
87
- # if I can find one and it knows how to do URI comparisons.
88
- def ==(uri)
89
- Vpim::Methods.casecmp?(self.uri.to_str, uri.to_str)
90
- end
91
-
92
- # The common or displayable name associated with the calendar address,
93
- # or nil if there is none.
94
- def cn
95
- return nil unless n = @field.param('CN')
96
-
97
- # FIXME = the CN param may have no value, which is an error, but don't try
98
- # to decode it, return either nil, or InvalidEncoding
99
- Vpim.decode_text(n.first)
100
- end
101
-
102
- # A string representation of an address, using the common name, and the
103
- # URI. The URI protocol is stripped if it's "mailto:".
104
- #
105
- # TODO - this needs to properly escape the cn string!
106
- def to_s
107
- u = uri
108
- u.gsub!(/^mailto: */i, '')
109
-
110
- if cn
111
- "\"#{cn}\" <#{uri}>"
112
- else
113
- uri
114
- end
115
- end
116
-
117
- def inspect
118
- "#<Vpim::Icalendar::Address:cn=#{cn.inspect} status=#{partstat} rsvp?=#{rsvp} #{uri.inspect}>"
65
+ def dirty #:nodoc:
66
+ @field = nil
67
+ self
119
68
  end
120
69
 
70
+ # Addresses in a CAL-ADDRESS are represented as a URI, usually a mailto URI.
71
+ attr_accessor :uri
72
+ # The common or displayable name associated with the calendar address, or
73
+ # nil if there is none.
74
+ attr_accessor :cn
121
75
  # The participation role for the calendar user specified by the address.
122
76
  #
123
77
  # The standard roles are:
@@ -128,11 +82,7 @@ module Vpim
128
82
  #
129
83
  # The default role is REQ-PARTICIPANT, returned if no ROLE parameter was
130
84
  # specified.
131
- def role
132
- return 'REQ-PARTICIPANT' unless r = @field.param('ROLE')
133
- r.first.upcase
134
- end
135
-
85
+ attr_accessor :role
136
86
  # The participation status for the calendar user specified by the
137
87
  # property PARTSTAT, a String.
138
88
  #
@@ -145,36 +95,124 @@ module Vpim
145
95
  #
146
96
  # Default is NEEDS-ACTION.
147
97
  #
148
- # TODO - make the default depend on the component type.
149
- def partstat
150
- return 'NEEDS-ACTION' unless r = @field.param('PARTSTAT')
151
- r.first.upcase
98
+ # FIXME - make the default depend on the component type.
99
+ attr_accessor :partstat
100
+ # The value of the RSVP field, either +true+ or +false+. It is used to
101
+ # specify whether there is an expectation of a reply from the calendar
102
+ # user specified by the property value.
103
+ attr_accessor :rsvp
104
+
105
+ def initialize(field=nil) #:nodoc:
106
+ @field = field
107
+ @uri = ''
108
+ @cn = ''
109
+ @role = "REQ-PARTICIPANT"
110
+ @partstat = "NEEDS-ACTION"
111
+ @rsvp = false
152
112
  end
153
113
 
154
- # Set or change the participation status of the address, the PARTSTAT,
155
- # to +status+.
156
- #
157
- # See #partstat.
158
- def partstat=(status)
159
- @field['PARTSTAT'] = status.to_str
160
- status
114
+ # Create a new Address. It will encode as a +name+ property.
115
+ def self.create(uri='')
116
+ adr = new
117
+ adr.uri = uri.to_str
118
+ adr
161
119
  end
162
120
 
163
- # The value of the RSVP field, either +true+ or +false+. It is used to
164
- # specify whether there is an expectation of a favor of a reply from the
165
- # calendar user specified by the property value.
166
- #
167
- # TODO - should be #rsvp?
168
- def rsvp
169
- return false unless r = @field.param('RSVP')
170
- r = r.first
171
- return false unless r
172
- case r
173
- when /TRUE/i then true
174
- when /FALSE/i then false
175
- else raise InvalidEncodingError, "RSVP param value not TRUE/FALSE: #{r}"
121
+ def self.decode(field)
122
+ adr = new(field)
123
+ adr.uri = field.value
124
+
125
+ cn = field.param('CN')
126
+
127
+ if cn
128
+ adr.cn = cn.first
129
+ end
130
+
131
+ role = field.param('ROLE')
132
+
133
+ if role
134
+ adr.role = role.first.strip.upcase
135
+ end
136
+
137
+ partstat = field.param('PARTSTAT')
138
+
139
+ if partstat
140
+ adr.partstat = partstat.first.strip.upcase
141
+ end
142
+
143
+ rsvp = field.param('RSVP')
144
+
145
+ if rsvp
146
+ adr.rsvp = case rsvp.first
147
+ when /TRUE/i then true
148
+ when /FALSE/i then false
149
+ else raise InvalidEncodingError, "RSVP param value not TRUE/FALSE: #{rsvp}"
150
+ end
151
+ end
152
+
153
+ adr.freeze
154
+ end
155
+
156
+ # Return a representation of this Address as a DirectoryInfo::Field.
157
+ def encode(name) #:nodoc:
158
+ if @field
159
+ # FIXME - set the field name, it could be different from cached
160
+ return @field
161
+ end
162
+
163
+ value = uri.to_str.strip
164
+
165
+ if value.empty?
166
+ raise Uencodeable, "Address#uri is zero-length"
167
+ end
168
+
169
+ params = {}
170
+
171
+ if cn.length > 0
172
+ params['CN'] = Vpim::encode_paramvalue(cn)
173
+ end
174
+
175
+ # FIXME - default value is different for non-vEvent
176
+ if role.length > 0 && role != 'REQ-PARTICIPANT'
177
+ params['ROLE'] = Vpim::encode_paramtext(role)
178
+ end
179
+
180
+ # FIXME - default value is different for non-vEvent
181
+ if partstat.length > 0 && partstat != 'NEEDS-ACTION'
182
+ params['PARTSTAT'] = Vpim::encode_paramtext(partstat)
183
+ end
184
+
185
+ if rsvp
186
+ params['RSVP'] = 'true'
187
+ end
188
+
189
+ Vpim::DirectoryInfo::Field.create(name, value, params)
190
+ end
191
+
192
+ # Return true if the +uri+ is == to this address' URI. The comparison
193
+ # is case-insensitive (because email addresses and domain names are).
194
+ def ==(uri)
195
+ # TODO - could I use a URI library?
196
+ Vpim::Methods.casecmp?(self.uri.to_str, uri.to_str)
197
+ end
198
+
199
+ # A string representation of an address, using the common name, and the
200
+ # URI. The URI protocol is stripped if it's "mailto:".
201
+ def to_s
202
+ u = uri
203
+ u = u.gsub(/^mailto: */i, '')
204
+
205
+ if cn.length > 0
206
+ "#{cn.inspect} <#{uri}>"
207
+ else
208
+ uri
176
209
  end
177
210
  end
211
+
212
+ def inspect #:nodoc:
213
+ "#<Vpim::Icalendar::Address:cn=#{cn.inspect} status=#{partstat} rsvp=#{rsvp} #{uri.inspect}>"
214
+ end
215
+
178
216
  end
179
217
  end
180
218
  end
@@ -33,10 +33,12 @@ module Vpim
33
33
  # (iTIP) Scheduling Events, BusyTime, To-dos and Journal Entries
34
34
  # - link:rfc2447.txt: iCalendar Message-Based Interoperability Protocol
35
35
  #
36
- # iCalendar (RFC 2445) is based on vCalendar, but does not appear to be
37
- # altogether compatible. iCalendar files have VERSION:2.0 and vCalendar have
38
- # VERSION:1.0. While much appears to be similar, the recurrence rule syntax,
39
- # at least, is completely different.
36
+ # = iCalendar and vCalendar
37
+ #
38
+ # iCalendar files have VERSION:2.0 and vCalendar have VERSION:1.0. iCalendar
39
+ # (RFC 2445) is based on vCalendar, but but is not very compatible. While
40
+ # much appears to be similar, the recurrence rule syntax is completely
41
+ # different.
40
42
  #
41
43
  # iCalendars are usually transmitted in files with <code>.ics</code>
42
44
  # extensions.
@@ -88,18 +90,37 @@ module Vpim
88
90
  end
89
91
  end
90
92
 
93
+ # Add and event to this calendar.
94
+ #
95
+ # Yields an event maker, Icalendar::Vevent::Maker.
96
+ def add_event(&block) #:yield:event
97
+ push Vevent::Maker.make( &block )
98
+ end
99
+
100
+ # FIXME - could take mandatory fields as an arguments
101
+ # FIXME - args: support PRODID
102
+ # FIXME - yield an Icalendar::Maker if block provided
103
+ # FIXME - maker#prodid=
104
+ def Icalendar.create2(args = nil)
105
+ # FIXME - make the primary API
106
+ di = DirectoryInfo.create( [ DirectoryInfo::Field.create('VERSION', '2.0') ], 'VCALENDAR' )
107
+
108
+ di.push_unique DirectoryInfo::Field.create('PRODID', Vpim::PRODID)
109
+ di.push_unique DirectoryInfo::Field.create('CALSCALE', "Gregorian")
110
+
111
+ new(di.to_a)
112
+ end
113
+
91
114
  # Create a new Icalendar object with the minimal set of fields for a valid
92
115
  # Calendar. If specified, +fields+ must be an array of
93
116
  # DirectoryInfo::Field objects to add. They can override the the default
94
117
  # Calendar fields, so, for example, this can be used to set a custom PRODID field.
95
- #
96
- # TODO - allow hash args like Vevent.create
97
118
  def Icalendar.create(fields=[])
98
119
  di = DirectoryInfo.create( [ DirectoryInfo::Field.create('VERSION', '2.0') ], 'VCALENDAR' )
99
120
 
100
121
  DirectoryInfo::Field.create_array(fields).each { |f| di.push_unique f }
101
122
 
102
- di.push_unique DirectoryInfo::Field.create('PRODID', "-//Ensemble Independant//vPim #{Vpim.version}//EN")
123
+ di.push_unique DirectoryInfo::Field.create('PRODID', Vpim::PRODID)
103
124
  di.push_unique DirectoryInfo::Field.create('CALSCALE', "Gregorian")
104
125
 
105
126
  new(di.to_a)
@@ -124,10 +145,16 @@ module Vpim
124
145
  Icalendar.create(fields)
125
146
  end
126
147
 
148
+ # Used during encoding.
149
+ def fields # :nodoc:
150
+ f = @properties.to_a
151
+ last = f.pop
152
+ @components.each { |c| f << c.fields }
153
+ f.push last
154
+ end
155
+
127
156
  # Encode the Calendar as a string. The width is the maximum width of the
128
157
  # encoded lines, it can be specified, but is better left to the default.
129
- #
130
- # TODO - only does top-level now, needs to add the events/todos/etc.
131
158
  def encode(width=nil)
132
159
  # We concatenate the fields of all objects, create a DirInfo, then
133
160
  # encode it.
@@ -135,17 +162,6 @@ module Vpim
135
162
  di.encode(width)
136
163
  end
137
164
 
138
- # Used during encoding.
139
- def fields # :nodoc:
140
- fields = @properties.to_a
141
-
142
- last = fields.pop
143
-
144
- @components.each { |c| fields << c.fields }
145
-
146
- fields << last
147
- end
148
-
149
165
  alias to_s encode
150
166
 
151
167
  # Push a calendar component onto the calendar.
@@ -156,6 +172,7 @@ module Vpim
156
172
  else
157
173
  raise ArgumentError, "can't add a #{component.type} to a calendar"
158
174
  end
175
+ self
159
176
  end
160
177
 
161
178
  # Check if the protocol method is +method+
@@ -8,10 +8,88 @@
8
8
 
9
9
  module Vpim
10
10
  class Icalendar
11
+ module Set #:nodoc:
12
+ module Util #:nodoc:
13
+
14
+ def rm_all(name)
15
+ rm = @comp.properties.select { |f| f.name? name }
16
+ rm.each { |f| @comp.properties.delete(f) }
17
+ end
18
+
19
+ def set_token(name, allowed, default, value) #:nodoc:
20
+ value = value.to_str
21
+ unless allowed.include?(value)
22
+ raise Vpim::Unencodeable, "Invalid #{name} value '#{value}'"
23
+ end
24
+ rm_all(name)
25
+ unless value == default
26
+ @comp.properties.push Vpim::DirectoryInfo::Field.create(name, value)
27
+ end
28
+ end
29
+
30
+ def field_create(name, value, default_value_type = nil, value_type = nil, params = {})
31
+ if value_type && value_type != default_value_type
32
+ params['VALUE'] = value_type
33
+ end
34
+ Vpim::DirectoryInfo::Field.create(name, value, params)
35
+ end
36
+
37
+ def set_date_or_datetime(name, default, value)
38
+ f = nil
39
+ case value
40
+ when Date
41
+ f = field_create(name, Vpim.encode_date(value), default, 'DATE')
42
+ when Time
43
+ f = field_create(name, Vpim.encode_date_time(value), default, 'DATE-TIME')
44
+ else
45
+ raise Vpim::Unencodeable, "Invalid #{name} value #{value.inspect}"
46
+ end
47
+ rm_all(name)
48
+ @comp.properties.push(f)
49
+ end
50
+
51
+ def set_datetime(name, value)
52
+ f = field_create(name, Vpim.encode_date_time(value))
53
+ rm_all(name)
54
+ @comp.properties.push(f)
55
+ end
56
+
57
+ def set_text(name, value)
58
+ f = field_create(name, Vpim.encode_text(value))
59
+ rm_all(name)
60
+ @comp.properties.push(f)
61
+ end
62
+
63
+ def set_text_list(name, value)
64
+ f = field_create(name, Vpim.encode_text_list(value))
65
+ rm_all(name)
66
+ @comp.properties.push(f)
67
+ end
68
+
69
+ def set_integer(name, value)
70
+ value = value.to_int.to_s
71
+ f = field_create(name, value)
72
+ rm_all(name)
73
+ @comp.properties.push(f)
74
+ end
75
+
76
+ def add_address(name, value)
77
+ f = value.encode(name)
78
+ @comp.properties.push(f)
79
+ end
80
+
81
+ def set_address(name, value)
82
+ rm_all(name)
83
+ add_address(name, value)
84
+ end
85
+
86
+ end
87
+ end
88
+
11
89
  module Property #:nodoc:
12
90
 
13
- # FIXME - these should be part of Dirinfo
14
- module Base
91
+ # FIXME - rename Base to Util
92
+ module Base #:nodoc:
15
93
  # Value of first property with name +name+
16
94
  def propvalue(name) #:nodoc:
17
95
  prop = @properties.detect { |f| f.name? name }
@@ -78,7 +78,7 @@ module Vpim
78
78
  organizer = @properties.field('ORGANIZER')
79
79
 
80
80
  if organizer
81
- organizer = Icalendar::Address.new(organizer)
81
+ organizer = Icalendar::Address.decode(organizer)
82
82
  end
83
83
 
84
84
  organizer.freeze
@@ -128,7 +128,7 @@ seq
128
128
  # attendees are objects of Icalendar::Address. If +uri+ is specified
129
129
  # only the return the attendees with this +uri+.
130
130
  def attendees(uri = nil)
131
- attendees = @properties.enum_by_name('ATTENDEE').map { |a| Icalendar::Address.new(a).freeze }
131
+ attendees = @properties.enum_by_name('ATTENDEE').map { |a| Icalendar::Address.decode(a) }
132
132
  attendees.freeze
133
133
  if uri
134
134
  attendees.select { |a| a == uri }
@@ -174,6 +174,141 @@ seq
174
174
  end
175
175
 
176
176
  end
177
+
178
+ module Set
179
+
180
+ # Properties common to Vevent, Vtodo, and Vjournal.
181
+ module Common
182
+
183
+ # Set the access class of the component, see Icalendar::Get::Common#access_class.
184
+ def access_class(token)
185
+ set_token 'CLASS', ["PUBLIC", "PRIVATE", "CONFIDENTIAL"], "PUBLIC", token
186
+ end
187
+
188
+ # Set the creation time, see Icalendar::Get::Common#created
189
+ def created(time)
190
+ set_datetime 'CREATED', time
191
+ end
192
+
193
+ # Set the description, see Icalendar::Get::Common#description.
194
+ def description(text)
195
+ set_text 'DESCRIPTION', text
196
+ end
197
+
198
+ # Set the sequence number, see Icalendar::Get::Common#sequence.
199
+ # is no SEQUENCE; property.
200
+ def sequence(int)
201
+ set_integer 'SEQUENCE', int
202
+ end
203
+
204
+ # Set the timestamp, see Icalendar::Get::Common#timestamp.
205
+ def dtstamp(time)
206
+ set_datetime 'DTSTAMP', time
207
+ self
208
+ end
209
+
210
+ # The start time or date, see Icalendar::Get::Common#dtstart.
211
+ def dtstart(start)
212
+ set_date_or_datetime 'DTSTART', 'DATE-TIME', start
213
+ self
214
+ end
215
+
216
+ # Set the last modification time, see Icalendar::Get::Common#lastmod.
217
+ def lastmod(time)
218
+ set_datetime 'LAST-MODIFIED', time
219
+ self
220
+ end
221
+
222
+ # Set the event organizer, an Icalendar::Address, see Icalendar::Get::Common#organizer.
223
+ #
224
+ # Without an +adr+ it yields an Icalendar::Address that is a copy of
225
+ # the current organizer (if any), allowing it to be modified.
226
+ def organizer(adr=nil) #:yield: organizer
227
+ unless adr
228
+ adr = @comp.organizer
229
+ if adr
230
+ adr = adr.copy
231
+ else
232
+ adr = Icalendar::Address.create
233
+ end
234
+ yield adr
235
+ end
236
+ set_address('ORGANIZER', adr)
237
+ self
238
+ end
239
+
240
+ =begin
241
+ # Status values are not rejected during decoding. However, if the
242
+ # status is requested, and it's value is not one of the defined
243
+ # allowable values, an exception is raised.
244
+ def status
245
+ case self
246
+ when Vpim::Icalendar::Vevent
247
+ proptoken 'STATUS', ['TENTATIVE', 'CONFIRMED', 'CANCELLED']
248
+
249
+ when Vpim::Icalendar::Vtodo
250
+ proptoken 'STATUS', ['NEEDS-ACTION', 'COMPLETED', 'IN-PROCESS', 'CANCELLED']
251
+
252
+ when Vpim::Icalendar::Vevent
253
+ proptoken 'STATUS', ['DRAFT', 'FINAL', 'CANCELLED']
254
+ end
255
+ end
256
+ =end
257
+
258
+ # Set summary description of component, see Icalendar::Get::Common#summary.
259
+ def summary(text)
260
+ set_text 'SUMMARY', text
261
+ end
262
+
263
+ # Set the unique identifier of this calendar component, see Icalendar::Get::Common#uid.
264
+ def uid(uid)
265
+ set_text 'UID', uid
266
+ end
267
+
268
+ def url(url)
269
+ set_text 'URL', url
270
+ end
271
+
272
+ # Add an attendee Address, see Icalendar::Get::Common#attendees.
273
+ def add_attendee(adr)
274
+ add_address('ATTENDEE', adr)
275
+ end
276
+
277
+ # Set the categories, see Icalendar::Get::Common#attendees.
278
+ #
279
+ # If +cats+ is provided, the categories are set to cats, either a
280
+ # String or an Array of String. Otherwise, and array of the existing
281
+ # category strings is yielded, and it can be modified.
282
+ def categories(cats = nil) #:yield: categories
283
+ unless cats
284
+ cats = @comp.categories
285
+ yield cats
286
+ end
287
+ # TODO - strip the strings
288
+ set_text_list('CATEGORIES', cats)
289
+ end
290
+
291
+ # Set the comment, see Icalendar::Get::Common#comment.
292
+ def comment(value)
293
+ set_text 'COMMENT', value
294
+ end
295
+
296
+ =begin
297
+ def contacts
298
+ proptextarray 'CONTACT'
299
+ end
300
+
301
+ # An Array of attachments, see Attachment for more information.
302
+ def attachments
303
+ @properties.enum_by_name('ATTACH').map do |f|
304
+ attachment = Attachment.decode(f, 'uri', 'FMTTYPE')
305
+ end
306
+ end
307
+ =end
308
+
309
+ end
310
+
311
+ end
177
312
  end
178
313
  end
179
314
 
@@ -56,6 +56,14 @@ module Vpim
56
56
 
57
57
  # integer = (["+"] / "-") 1*DIGIT
58
58
  INTEGER = '[-+]?\d+'
59
+
60
+ # QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-US-ASCII
61
+ # ; Any character except CTLs and DQUOTE
62
+ QSAFECHAR = '[ \t\x21\x23-\x7e\x80-\xff]'
63
+
64
+ # SAFE-CHAR = WSP / %x21 / %x23-2B / %x2D-39 / %x3C-7E / NON-US-ASCII
65
+ # ; Any character except CTLs, DQUOTE, ";", ":", ","
66
+ SAFECHAR = '[ \t\x21\x23-\x2b\x2d-\x39\x3c-\x7e\x80-\xff]'
59
67
  end
60
68
  end
61
69
 
@@ -232,8 +240,13 @@ module Vpim
232
240
  end
233
241
  end
234
242
 
243
+ # v is an Array of String, or just a single String
235
244
  def Vpim.encode_text_list(v, sep = ",") #:nodoc:
236
- v.to_ary.map{ |t| Vpim.encode_text(t) }.join(sep)
245
+ begin
246
+ v.to_ary.map{ |t| Vpim.encode_text(t) }.join(sep)
247
+ rescue
248
+ Vpim.encode_text(v)
249
+ end
237
250
  end
238
251
 
239
252
  # Convert a +sep+-seperated list of TEXT values into an array of values.
@@ -248,6 +261,29 @@ module Vpim
248
261
  list
249
262
  end
250
263
 
264
+ # param-value = paramtext / quoted-string
265
+ # paramtext = *SAFE-CHAR
266
+ # quoted-string = DQUOTE *QSAFE-CHAR DQUOTE
267
+ def Vpim.encode_paramtext(value)
268
+ case value
269
+ when %r{\A#{Bnf::SAFECHAR}*\z}
270
+ value
271
+ else
272
+ raise Vpim::Unencodable, "paramtext #{value.inspect}"
273
+ end
274
+ end
275
+
276
+ def Vpim.encode_paramvalue(value)
277
+ case value
278
+ when %r{\A#{Bnf::SAFECHAR}*\z}
279
+ value
280
+ when %r{\A#{Bnf::QSAFECHAR}*\z}
281
+ '"' + value + '"'
282
+ else
283
+ raise Vpim::Unencodable, "param-value #{value.inspect}"
284
+ end
285
+ end
286
+
251
287
 
252
288
  # Unfold the lines in +card+, then return an array of one Field object per
253
289
  # line.
@@ -1078,19 +1078,12 @@ module Vpim
1078
1078
 
1079
1079
  public
1080
1080
 
1081
- # def add_name # :yield: n
1082
- # # FIXME: Each attribute can currently only have a single String value.
1083
- # # FIXME: Need to escape specials in the String.
1084
- # x = Struct.new(:family, :given, :additional, :prefix, :suffix).new
1085
- # yield x
1086
- # @card << Vpim::DirectoryInfo::Field.create(
1087
- # 'N',
1088
- # x.map { |s| s ? s.to_str : '' }
1089
- # )
1090
- # self
1091
- # end
1092
- # Set with #name now.
1093
- # Use m.name do |n| n.fullname = foo end
1081
+ # Deprecated, see #name.
1082
+ #
1083
+ # Use
1084
+ # maker.name do |n| n.fullname = "foo" end
1085
+ # to set just fullname, or set the other fields to set fullname and the
1086
+ # name.
1094
1087
  def fullname=(fullname) #:nodoc: bacwards compat
1095
1088
  if @card.field('FN')
1096
1089
  raise Vpim::InvalidEncodingError, "Not allowed to add more than one FN field to a vCard."
@@ -7,9 +7,9 @@
7
7
  =end
8
8
 
9
9
  module Vpim
10
- PRODID = '-//Ensemble Independent//vPim 0.357//EN'
10
+ PRODID = '-//Ensemble Independent//vPim 0.360//EN'
11
11
 
12
- VERSION = '0.357'
12
+ VERSION = '0.360'
13
13
 
14
14
  # Return the API version as a string.
15
15
  def Vpim.version
@@ -20,6 +20,7 @@ require 'vpim/property/recurrence'
20
20
  module Vpim
21
21
  class Icalendar
22
22
  class Vevent
23
+
23
24
  include Vpim::Icalendar::Property::Base
24
25
  include Vpim::Icalendar::Property::Common
25
26
  include Vpim::Icalendar::Property::Priority
@@ -37,6 +38,19 @@ module Vpim
37
38
  # See "TODO - fields" in dirinfo.rb
38
39
  end
39
40
 
41
+ # TODO - derive everything from Icalendar::Component to get this kind of stuff?
42
+ def fields #:nodoc:
43
+ f = @properties.to_a
44
+ last = f.pop
45
+ f.push @elements
46
+ f.push last
47
+ end
48
+
49
+ def properties #:nodoc:
50
+ @properties
51
+ end
52
+
53
+
40
54
  # Create a new Vevent object. All events must have a DTSTART field,
41
55
  # specify it as either a Time or a Date in +start+, it defaults to "now"
42
56
  # (is this useful?).
@@ -71,10 +85,12 @@ module Vpim
71
85
 
72
86
  # Accept an event invitation. The +invitee+ is the Address that wishes
73
87
  # to accept the event invitation as confirmed.
88
+ #
89
+ # The event created is identical to this one, but
90
+ # - without the attendees
91
+ # - with the invitee added with a PARTSTAT of ACCEPTED
74
92
  def accept(invitee)
75
- # The event created is identical to this one, but
76
- # - without the attendees
77
- # - with the invitee added with a PARTSTAT of ACCEPTED
93
+ # FIXME - move to Vpim::Itip.
78
94
  invitee = invitee.copy
79
95
  invitee.partstat = 'ACCEPTED'
80
96
 
@@ -84,7 +100,7 @@ module Vpim
84
100
  |f,i|
85
101
 
86
102
  # put invitee in as field[1]
87
- fields << invitee.field if i == 1
103
+ fields << invitee.encode('ATTENDEE') if i == 1
88
104
 
89
105
  fields << f unless f.name? 'ATTENDEE'
90
106
  end
@@ -92,11 +108,8 @@ module Vpim
92
108
  Vevent.new(fields)
93
109
  end
94
110
 
95
- =begin
96
- # Set the start time for the event to +start+, a Time object.
97
- # TODO - def dtstart=(start) ... start should be allowed to be Time/Date/DateTime
98
- =end
99
-
111
+ # In iTIP, whether this event is OPAQUE or TRANSPARENT to scheduling. If
112
+ # transparency is not explicitly set, it defaults to OPAQUE.
100
113
  def transparency
101
114
  proptoken 'TRANSP', ["OPAQUE", "TRANSPARENT"], "OPAQUE"
102
115
  end
@@ -135,6 +148,57 @@ module Vpim
135
148
  end
136
149
  end
137
150
 
151
+ # Make a new Vevent, or make changes to an existing Vevent.
152
+ class Maker
153
+ # TODO - should I automatically set
154
+ # #created
155
+ # #dtstamp
156
+ # #sequence
157
+ # ...?
158
+ #
159
+ # Many have pretty specific meanings in iTIP, perhaps I should leave
160
+ # them alone.
161
+ include Vpim::Icalendar::Set::Util #:nodoc:
162
+ include Vpim::Icalendar::Set::Common
163
+
164
+ # The event that changes are being made to.
165
+ attr_reader :event
166
+
167
+ def initialize(event) #:nodoc:
168
+ @event = event
169
+ @comp = event
170
+ end
171
+
172
+ # Make changes to +event+. If +event+ is not specified, creates a new
173
+ # event. Yields a Vevent::Maker, and returns +event+.
174
+ def self.make(event = Vpim::Icalendar::Vevent.create) #:yield:maker
175
+ m = self.new(event)
176
+ yield m
177
+ m.event
178
+ end
179
+
180
+ # Set transparency to "OPAQUE" or "TRANSPARENT", see Vpim::Vevent#transparency.
181
+ def transparency(token)
182
+ set_token 'TRANSP', ["OPAQUE", "TRANSPARENT"], "OPAQUE", token
183
+ end
184
+
185
+ # Set end for events with fixed durations. +end+ can be a Date or Time
186
+ def dtend(dtend)
187
+ set_date_or_datetime 'DTEND', 'DATE-TIME', dtend
188
+ end
189
+
190
+ # Yields a selector that allows the duration to be set.
191
+ #
192
+ # TODO - syntax is:
193
+ # dur-value = (["+"] / "-") "P" (dur-date / dur-time / dur-week)
194
+ # dur-date = dur-day [ "T" (dur-hour / dur-minute / dur-second) ]
195
+ # dur-time = "T" (dur-hour / dur-minute / dur-second)
196
+ # dur-week = 1*DIGIT "W"
197
+ def duration(dur) #:yield:selector
198
+ raise Vpim::Unsupported
199
+ end
200
+ end
201
+
138
202
  end
139
203
  end
140
204
  end
metadata CHANGED
@@ -3,15 +3,15 @@ rubygems_version: 0.8.11
3
3
  specification_version: 1
4
4
  name: vpim
5
5
  version: !ruby/object:Gem::Version
6
- version: "0.357"
7
- date: 2006-03-31 00:00:00 -08:00
8
- summary: a library to manipulate vCards and iCalendars
6
+ version: "0.360"
7
+ date: 2006-04-02 00:00:00 -08:00
8
+ summary: iCalendar and vCard support for ruby
9
9
  require_paths:
10
10
  - lib
11
11
  email: sroberts@uniserve.com
12
12
  homepage: http://vpim.rubyforge.org
13
- rubyforge_project:
14
- description:
13
+ rubyforge_project: vpim
14
+ description: This is a pure-ruby library for decoding and encoding vCard and iCalendar data ("personal information") called vPim.
15
15
  autorequire: vpim
16
16
  default_executable:
17
17
  bindir: bin