mumboe-vpim 0.7
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.
- data/CHANGES +510 -0
- data/COPYING +58 -0
- data/README +185 -0
- data/lib/vpim/address.rb +219 -0
- data/lib/vpim/agent/atomize.rb +104 -0
- data/lib/vpim/agent/base.rb +73 -0
- data/lib/vpim/agent/calendars.rb +173 -0
- data/lib/vpim/agent/handler.rb +26 -0
- data/lib/vpim/agent/ics.rb +161 -0
- data/lib/vpim/attachment.rb +102 -0
- data/lib/vpim/date.rb +222 -0
- data/lib/vpim/dirinfo.rb +277 -0
- data/lib/vpim/duration.rb +119 -0
- data/lib/vpim/enumerator.rb +32 -0
- data/lib/vpim/field.rb +614 -0
- data/lib/vpim/icalendar.rb +384 -0
- data/lib/vpim/maker/vcard.rb +16 -0
- data/lib/vpim/property/base.rb +193 -0
- data/lib/vpim/property/common.rb +315 -0
- data/lib/vpim/property/location.rb +38 -0
- data/lib/vpim/property/priority.rb +43 -0
- data/lib/vpim/property/recurrence.rb +69 -0
- data/lib/vpim/property/resources.rb +24 -0
- data/lib/vpim/repo.rb +261 -0
- data/lib/vpim/rfc2425.rb +367 -0
- data/lib/vpim/rrule.rb +591 -0
- data/lib/vpim/time.rb +40 -0
- data/lib/vpim/vcard.rb +1456 -0
- data/lib/vpim/version.rb +18 -0
- data/lib/vpim/vevent.rb +187 -0
- data/lib/vpim/view.rb +90 -0
- data/lib/vpim/vjournal.rb +58 -0
- data/lib/vpim/vpim.rb +65 -0
- data/lib/vpim/vtodo.rb +103 -0
- data/lib/vpim.rb +13 -0
- data/samples/README.mutt +93 -0
- data/samples/ab-query.rb +57 -0
- data/samples/agent.ru +10 -0
- data/samples/cmd-itip.rb +156 -0
- data/samples/ex_cpvcard.rb +55 -0
- data/samples/ex_get_vcard_photo.rb +22 -0
- data/samples/ex_mkv21vcard.rb +34 -0
- data/samples/ex_mkvcard.rb +64 -0
- data/samples/ex_mkyourown.rb +29 -0
- data/samples/ics-dump.rb +210 -0
- data/samples/ics-to-rss.rb +84 -0
- data/samples/mutt-aliases-to-vcf.rb +45 -0
- data/samples/osx-wrappers.rb +86 -0
- data/samples/reminder.rb +209 -0
- data/samples/rrule.rb +71 -0
- data/samples/tabbed-file-to-vcf.rb +390 -0
- data/samples/vcf-dump.rb +86 -0
- data/samples/vcf-lines.rb +61 -0
- data/samples/vcf-to-ics.rb +22 -0
- data/samples/vcf-to-mutt.rb +121 -0
- data/test/test_agent_atomize.rb +84 -0
- data/test/test_agent_calendars.rb +128 -0
- data/test/test_agent_ics.rb +96 -0
- data/test/test_all.rb +17 -0
- data/test/test_date.rb +120 -0
- data/test/test_dur.rb +41 -0
- data/test/test_field.rb +156 -0
- data/test/test_ical.rb +437 -0
- data/test/test_misc.rb +13 -0
- data/test/test_repo.rb +129 -0
- data/test/test_rrule.rb +1030 -0
- data/test/test_vcard.rb +973 -0
- data/test/test_view.rb +79 -0
- metadata +140 -0
data/lib/vpim/version.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 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
|
+
module Vpim
|
10
|
+
PRODID = '-//Octet Cloud//vPim 9.3.15//EN'
|
11
|
+
|
12
|
+
VERSION = '9.3.15'
|
13
|
+
|
14
|
+
# Return the API version as a string.
|
15
|
+
def Vpim.version
|
16
|
+
VERSION
|
17
|
+
end
|
18
|
+
end
|
data/lib/vpim/vevent.rb
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 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/dirinfo'
|
10
|
+
require 'vpim/field'
|
11
|
+
require 'vpim/rfc2425'
|
12
|
+
require 'vpim/rrule'
|
13
|
+
require 'vpim/vpim'
|
14
|
+
require 'vpim/property/base'
|
15
|
+
require 'vpim/property/common'
|
16
|
+
require 'vpim/property/priority'
|
17
|
+
require 'vpim/property/location'
|
18
|
+
require 'vpim/property/resources'
|
19
|
+
require 'vpim/property/recurrence'
|
20
|
+
|
21
|
+
module Vpim
|
22
|
+
class Icalendar
|
23
|
+
class Vevent
|
24
|
+
|
25
|
+
include Vpim::Icalendar::Property::Base
|
26
|
+
include Vpim::Icalendar::Property::Common
|
27
|
+
include Vpim::Icalendar::Property::Priority
|
28
|
+
include Vpim::Icalendar::Property::Location
|
29
|
+
include Vpim::Icalendar::Property::Resources
|
30
|
+
include Vpim::Icalendar::Property::Recurrence
|
31
|
+
|
32
|
+
def initialize(fields) #:nodoc:
|
33
|
+
outer, inner = Vpim.outer_inner(fields)
|
34
|
+
|
35
|
+
@properties = Vpim::DirectoryInfo.create(outer)
|
36
|
+
|
37
|
+
@elements = inner
|
38
|
+
|
39
|
+
# See "TODO - fields" in dirinfo.rb
|
40
|
+
end
|
41
|
+
|
42
|
+
# TODO - derive everything from Icalendar::Component to get rid of this kind of stuff?
|
43
|
+
def fields #:nodoc:
|
44
|
+
f = @properties.to_a
|
45
|
+
last = f.pop
|
46
|
+
f.push @elements
|
47
|
+
f.push last
|
48
|
+
end
|
49
|
+
|
50
|
+
def properties #:nodoc:
|
51
|
+
@properties
|
52
|
+
end
|
53
|
+
|
54
|
+
# Create a new Vevent object. All events must have a DTSTART field,
|
55
|
+
# specify it as either a Time or a Date in +start+, it defaults to "now"
|
56
|
+
#
|
57
|
+
# If specified, +fields+ must be either an array of Field objects to
|
58
|
+
# add, or a Hash of String names to values that will be used to build
|
59
|
+
# Field objects. The latter is a convenient short-cut allowing the Field
|
60
|
+
# objects to be created for you when called like:
|
61
|
+
#
|
62
|
+
# Vevent.create(Date.today, 'SUMMARY' => "today's event")
|
63
|
+
#
|
64
|
+
def Vevent.create(start = Time.now, fields=[])
|
65
|
+
# TODO
|
66
|
+
# - maybe events are usually created in a particular way? With a
|
67
|
+
# start/duration or a start/end? Maybe I can make it easier. Ideally, I
|
68
|
+
# would like to make it hard to encode an invalid Event.
|
69
|
+
# - I don't think its useful to have a default dtstart for events
|
70
|
+
# - also, I don't think dstart is mandatory
|
71
|
+
dtstart = DirectoryInfo::Field.create('DTSTART', start)
|
72
|
+
di = DirectoryInfo.create([ dtstart ], 'VEVENT')
|
73
|
+
|
74
|
+
Vpim::DirectoryInfo::Field.create_array(fields).each { |f| di.push_unique f }
|
75
|
+
|
76
|
+
new(di.to_a)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Creates a yearly repeating event, such as for a birthday.
|
80
|
+
def Vevent.create_yearly(date, summary)
|
81
|
+
create(
|
82
|
+
date,
|
83
|
+
'SUMMARY' => summary.to_str,
|
84
|
+
'RRULE' => 'FREQ=YEARLY'
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Accept an event invitation. The +invitee+ is the Address that wishes
|
89
|
+
# to accept the event invitation as confirmed.
|
90
|
+
#
|
91
|
+
# The event created is identical to this one, but
|
92
|
+
# - without the attendees
|
93
|
+
# - with the invitee added with a PARTSTAT of ACCEPTED
|
94
|
+
def accept(invitee)
|
95
|
+
# FIXME - move to Vpim::Itip.
|
96
|
+
invitee = invitee.copy
|
97
|
+
invitee.partstat = 'ACCEPTED'
|
98
|
+
|
99
|
+
fields = []
|
100
|
+
|
101
|
+
@properties.each_with_index do
|
102
|
+
|f,i|
|
103
|
+
|
104
|
+
# put invitee in as field[1]
|
105
|
+
fields << invitee.encode('ATTENDEE') if i == 1
|
106
|
+
|
107
|
+
fields << f unless f.name? 'ATTENDEE'
|
108
|
+
end
|
109
|
+
|
110
|
+
Vevent.new(fields)
|
111
|
+
end
|
112
|
+
|
113
|
+
# In iTIP, whether this event is OPAQUE or TRANSPARENT to scheduling. If
|
114
|
+
# transparency is not explicitly set, it defaults to OPAQUE.
|
115
|
+
def transparency
|
116
|
+
proptoken 'TRANSP', ["OPAQUE", "TRANSPARENT"], "OPAQUE"
|
117
|
+
end
|
118
|
+
|
119
|
+
# The duration in seconds of an Event, or nil if unspecified. If the
|
120
|
+
# DURATION field is not present, but the DTEND field is, the duration is
|
121
|
+
# calculated from DTSTART and DTEND. Durations of zero seconds are
|
122
|
+
# possible.
|
123
|
+
def duration
|
124
|
+
propduration 'DTEND'
|
125
|
+
end
|
126
|
+
|
127
|
+
# The end time for this Event. If the DTEND field is not present, but the
|
128
|
+
# DURATION field is, the end will be calculated from DTSTART and
|
129
|
+
# DURATION.
|
130
|
+
def dtend
|
131
|
+
propend 'DTEND'
|
132
|
+
end
|
133
|
+
|
134
|
+
# Make a new Vevent, or make changes to an existing Vevent.
|
135
|
+
class Maker
|
136
|
+
include Vpim::Icalendar::Set::Util #:nodoc:
|
137
|
+
include Vpim::Icalendar::Set::Common
|
138
|
+
|
139
|
+
# The event that changes are being made to.
|
140
|
+
attr_reader :event
|
141
|
+
|
142
|
+
def initialize(event) #:nodoc:
|
143
|
+
@event = event
|
144
|
+
@comp = event
|
145
|
+
end
|
146
|
+
|
147
|
+
# Make changes to +event+. If +event+ is not specified, creates a new
|
148
|
+
# event. Yields a Vevent::Maker, and returns +event+.
|
149
|
+
def self.make(event = Vpim::Icalendar::Vevent.create) #:yield:maker
|
150
|
+
m = self.new(event)
|
151
|
+
yield m
|
152
|
+
m.event
|
153
|
+
end
|
154
|
+
|
155
|
+
# Set transparency to "OPAQUE" or "TRANSPARENT", see Vpim::Vevent#transparency.
|
156
|
+
def transparency(token)
|
157
|
+
set_token 'TRANSP', ["OPAQUE", "TRANSPARENT"], "OPAQUE", token
|
158
|
+
end
|
159
|
+
|
160
|
+
# Set end for events with fixed durations. +end+ can be a Date or Time
|
161
|
+
def dtend(dtend)
|
162
|
+
set_date_or_datetime 'DTEND', 'DATE-TIME', dtend
|
163
|
+
end
|
164
|
+
|
165
|
+
# Add a RRULE to this event. The rule can be provided as a pre-build
|
166
|
+
# RRULE value, or the RRULE maker can be used.
|
167
|
+
def add_rrule(rule = nil, &block) #:yield: Rrule::Maker
|
168
|
+
# TODO - should be in Property::Reccurrence::Set
|
169
|
+
unless rule
|
170
|
+
rule = Rrule::Maker.new(&block).encode
|
171
|
+
end
|
172
|
+
@comp.properties.push(Vpim::DirectoryInfo::Field.create("RRULE", rule))
|
173
|
+
self
|
174
|
+
end
|
175
|
+
|
176
|
+
# Set the RRULE for this event. See #add_rrule
|
177
|
+
def set_rrule(rule = nil, &block) #:yield: Rrule::Maker
|
178
|
+
rm_all("RRULE")
|
179
|
+
add_rrule(rule, &block)
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
data/lib/vpim/view.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 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 "enumerator"
|
10
|
+
|
11
|
+
module Vpim
|
12
|
+
module View
|
13
|
+
|
14
|
+
SECSPERDAY = 24 * 60 * 60
|
15
|
+
|
16
|
+
# View only events occuring in the next week.
|
17
|
+
module Week
|
18
|
+
def each(klass = nil) #:nodoc:
|
19
|
+
unless block_given?
|
20
|
+
return Enumerable::Enumerator.new(self, :each, klass)
|
21
|
+
end
|
22
|
+
|
23
|
+
t0 = Time.new.to_a
|
24
|
+
t0[0] = t0[1] = t0[2] = 0 # sec,min,hour = 0
|
25
|
+
t0 = Time.local(*t0)
|
26
|
+
t1 = t0 + 7 * SECSPERDAY
|
27
|
+
|
28
|
+
# Need to filter occurrences, too. Create modules for this on the fly.
|
29
|
+
occurrences = Module.new
|
30
|
+
# I'm passing state into the module's instance methods by doing string
|
31
|
+
# evaluation... which sucks, but I don't think I can get this closure in
|
32
|
+
# there.
|
33
|
+
occurrences.module_eval(<<"__", __FILE__, __LINE__+1)
|
34
|
+
def occurrences(dountil=nil)
|
35
|
+
unless block_given?
|
36
|
+
return Enumerable::Enumerator.new(self, :occurrences, dountil)
|
37
|
+
end
|
38
|
+
super(dountil) do |t|
|
39
|
+
t0 = Time.at(#{t0.to_i})
|
40
|
+
t1 = Time.at(#{t1.to_i})
|
41
|
+
break if t >= t1
|
42
|
+
tend = t
|
43
|
+
if respond_to? :duration
|
44
|
+
tend += duration || 0
|
45
|
+
end
|
46
|
+
if tend >= t0
|
47
|
+
yield t
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
__
|
52
|
+
=begin
|
53
|
+
block = lambda do |dountil|
|
54
|
+
unless block_given?
|
55
|
+
return Enumerable::Enumerator.new(self, :occurrences, dountil)
|
56
|
+
end
|
57
|
+
super(dountil) do |t|
|
58
|
+
break if t >= t1
|
59
|
+
yield t
|
60
|
+
end
|
61
|
+
end
|
62
|
+
occurrences.send(:define_method, :occurrences, block)
|
63
|
+
=end
|
64
|
+
super do |ve|
|
65
|
+
if ve.occurs_in?(t0, t1)
|
66
|
+
if ve.respond_to? :occurrences
|
67
|
+
ve.extend occurrences
|
68
|
+
end
|
69
|
+
yield ve
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Return a calendar view for the next week.
|
76
|
+
def self.week(cal)
|
77
|
+
cal.clone.extend Week.dup
|
78
|
+
end
|
79
|
+
|
80
|
+
module Todo
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return a calendar view of only todos (optionally, include todos that
|
84
|
+
# are done).
|
85
|
+
def self.todos(cal, withdone=false)
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 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/dirinfo'
|
10
|
+
require 'vpim/field'
|
11
|
+
require 'vpim/rfc2425'
|
12
|
+
require 'vpim/vpim'
|
13
|
+
require 'vpim/property/base'
|
14
|
+
require 'vpim/property/common'
|
15
|
+
require 'vpim/property/recurrence'
|
16
|
+
|
17
|
+
module Vpim
|
18
|
+
class Icalendar
|
19
|
+
|
20
|
+
class Vjournal
|
21
|
+
include Vpim::Icalendar::Property::Base
|
22
|
+
include Vpim::Icalendar::Property::Common
|
23
|
+
include Vpim::Icalendar::Property::Recurrence
|
24
|
+
|
25
|
+
def initialize(fields) #:nodoc:
|
26
|
+
outer, inner = Vpim.outer_inner(fields)
|
27
|
+
|
28
|
+
@properties = Vpim::DirectoryInfo.create(outer)
|
29
|
+
|
30
|
+
@elements = inner
|
31
|
+
end
|
32
|
+
|
33
|
+
# TODO - derive everything from Icalendar::Component to get rid of this kind of stuff?
|
34
|
+
def fields #:nodoc:
|
35
|
+
f = properties.to_a
|
36
|
+
last = f.pop
|
37
|
+
f.push @elements
|
38
|
+
f.push last
|
39
|
+
end
|
40
|
+
|
41
|
+
def properties #:nodoc:
|
42
|
+
@properties
|
43
|
+
end
|
44
|
+
|
45
|
+
# Create a Vjournal component.
|
46
|
+
def self.create(fields=[])
|
47
|
+
di = DirectoryInfo.create([], 'VJOURNAL')
|
48
|
+
|
49
|
+
Vpim::DirectoryInfo::Field.create_array(fields).each { |f| di.push_unique f }
|
50
|
+
|
51
|
+
new(di.to_a)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
data/lib/vpim/vpim.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 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/version'
|
10
|
+
|
11
|
+
#:main:README
|
12
|
+
#:title:vPim - vCard and iCalendar support for Ruby
|
13
|
+
module Vpim
|
14
|
+
# Exception used to indicate that data being decoded is invalid, the message
|
15
|
+
# should describe what is invalid.
|
16
|
+
class InvalidEncodingError < StandardError; end
|
17
|
+
|
18
|
+
# Exception used to indicate that data being decoded is unsupported, the message
|
19
|
+
# should describe what is unsupported.
|
20
|
+
#
|
21
|
+
# If its unsupported, its likely because I didn't anticipate it being useful
|
22
|
+
# to support this, and it likely it could be supported on request.
|
23
|
+
class UnsupportedError < StandardError; end
|
24
|
+
|
25
|
+
# Exception used to indicate that encoding failed, probably because the
|
26
|
+
# object would not result in validly encoded data. The message should
|
27
|
+
# describe what is unsupported.
|
28
|
+
class Unencodeable < StandardError; end
|
29
|
+
end
|
30
|
+
|
31
|
+
module Vpim::Methods #:nodoc:
|
32
|
+
module_function
|
33
|
+
|
34
|
+
# Case-insensitive comparison of +str0+ to +str1+, returns true or false.
|
35
|
+
# Either argument can be nil, where nil compares not equal to anything other
|
36
|
+
# than nil.
|
37
|
+
#
|
38
|
+
# This is available both as a module function:
|
39
|
+
# Vpim::Methods.casecmp?("yes", "YES")
|
40
|
+
# and an instance method:
|
41
|
+
# include Vpim::Methods
|
42
|
+
# casecmp?("yes", "YES")
|
43
|
+
#
|
44
|
+
# Will work with ruby1.6 and ruby 1.8.
|
45
|
+
#
|
46
|
+
# TODO - could make this be more efficient, but I'm supporting 1.6, not
|
47
|
+
# optimizing for it.
|
48
|
+
def casecmp?(str0, str1)
|
49
|
+
if str0 == nil
|
50
|
+
if str1 == nil
|
51
|
+
return true
|
52
|
+
else
|
53
|
+
return false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
begin
|
58
|
+
str0.casecmp(str1) == 0
|
59
|
+
rescue NoMethodError
|
60
|
+
str0.downcase == str1.downcase
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
data/lib/vpim/vtodo.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 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/dirinfo'
|
10
|
+
require 'vpim/field'
|
11
|
+
require 'vpim/rfc2425'
|
12
|
+
require 'vpim/vpim'
|
13
|
+
require 'vpim/property/base'
|
14
|
+
require 'vpim/property/common'
|
15
|
+
require 'vpim/property/priority'
|
16
|
+
require 'vpim/property/location'
|
17
|
+
require 'vpim/property/resources'
|
18
|
+
require 'vpim/property/recurrence'
|
19
|
+
|
20
|
+
module Vpim
|
21
|
+
class Icalendar
|
22
|
+
|
23
|
+
class Vtodo
|
24
|
+
include Vpim::Icalendar::Property::Base
|
25
|
+
include Vpim::Icalendar::Property::Common
|
26
|
+
include Vpim::Icalendar::Property::Priority
|
27
|
+
include Vpim::Icalendar::Property::Location
|
28
|
+
include Vpim::Icalendar::Property::Resources
|
29
|
+
include Vpim::Icalendar::Property::Recurrence
|
30
|
+
|
31
|
+
def initialize(fields) #:nodoc:
|
32
|
+
outer, inner = Vpim.outer_inner(fields)
|
33
|
+
|
34
|
+
@properties = Vpim::DirectoryInfo.create(outer)
|
35
|
+
|
36
|
+
@elements = inner
|
37
|
+
end
|
38
|
+
|
39
|
+
# TODO - derive everything from Icalendar::Component to get this kind of stuff?
|
40
|
+
def fields #:nodoc:
|
41
|
+
f = @properties.to_a
|
42
|
+
last = f.pop
|
43
|
+
f.push @elements
|
44
|
+
f.push last
|
45
|
+
end
|
46
|
+
|
47
|
+
def properties #:nodoc:
|
48
|
+
@properties
|
49
|
+
end
|
50
|
+
|
51
|
+
# Create a new Vtodo object.
|
52
|
+
#
|
53
|
+
# If specified, +fields+ must be either an array of Field objects to
|
54
|
+
# add, or a Hash of String names to values that will be used to build
|
55
|
+
# Field objects. The latter is a convenient short-cut allowing the Field
|
56
|
+
# objects to be created for you when called like:
|
57
|
+
#
|
58
|
+
# Vtodo.create('SUMMARY' => "buy mangos")
|
59
|
+
#
|
60
|
+
# TODO - maybe todos are usually created in a particular way? I can
|
61
|
+
# make it easier. Ideally, I would like to make it hard to encode an invalid
|
62
|
+
# Event.
|
63
|
+
def Vtodo.create(fields=[])
|
64
|
+
di = DirectoryInfo.create([], 'VTODO')
|
65
|
+
|
66
|
+
Vpim::DirectoryInfo::Field.create_array(fields).each { |f| di.push_unique f }
|
67
|
+
|
68
|
+
new(di.to_a)
|
69
|
+
end
|
70
|
+
|
71
|
+
# The duration in seconds of a Todo, or nil if unspecified. If the
|
72
|
+
# DURATION field is not present, but the DUE field is, the duration is
|
73
|
+
# calculated from DTSTART and DUE. Durations of zero seconds are
|
74
|
+
# possible.
|
75
|
+
def duration
|
76
|
+
propduration 'DUE'
|
77
|
+
end
|
78
|
+
|
79
|
+
# The time at which this Todo is due to be completed. If the DUE field is not present,
|
80
|
+
# but the DURATION field is, due will be calculated from DTSTART and DURATION.
|
81
|
+
def due
|
82
|
+
propend 'DUE'
|
83
|
+
end
|
84
|
+
|
85
|
+
# The date and time that a to-do was actually completed, a Time.
|
86
|
+
def completed
|
87
|
+
proptime 'COMPLETED'
|
88
|
+
end
|
89
|
+
|
90
|
+
# The percentage completetion of the to-do, between 0 and 100. 0 means it hasn't
|
91
|
+
# started, 100 that it has been completed.
|
92
|
+
#
|
93
|
+
# TODO - the handling of this property isn't tied to either COMPLETED: or
|
94
|
+
# STATUS:, but perhaps it should be?
|
95
|
+
def percent_complete
|
96
|
+
propinteger 'PERCENT-COMPLETE'
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
data/lib/vpim.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 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
|
+
# the existence of this file is a hack to support users or rubygems
|
10
|
+
|
11
|
+
require 'vpim/icalendar'
|
12
|
+
require 'vpim/vcard'
|
13
|
+
|
data/samples/README.mutt
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
|
2
|
+
** cmd-itip.rb
|
3
|
+
|
4
|
+
This script pretty-prints iTIP calendar invitations, often sent by email using iMIP
|
5
|
+
as text/calendar objects.
|
6
|
+
|
7
|
+
Download the latest vPim from:
|
8
|
+
|
9
|
+
http://rubyforge.org/projects/vpim/
|
10
|
+
|
11
|
+
It requires Ruby to be installed.
|
12
|
+
|
13
|
+
Install vpim:
|
14
|
+
|
15
|
+
tar -xzf vpim-XX.tgz
|
16
|
+
cd vpim-XX
|
17
|
+
ruby install.rb config
|
18
|
+
ruby install.rb setup
|
19
|
+
sudo ruby install.rb install
|
20
|
+
|
21
|
+
Install cmd-itip.rb into your path, perhaps without the extension.
|
22
|
+
|
23
|
+
cp samples/cmd-itip.rb ~/bin/cmd-itip
|
24
|
+
chmod +x ~/bin/cmd-itip
|
25
|
+
|
26
|
+
Modify your ~/.mailcap or /etc/mailcap files to call cmd-itip, add a line like:
|
27
|
+
|
28
|
+
text/calendar; cmd-itip --myaddr "sroberts@" %s; copiousoutput
|
29
|
+
|
30
|
+
If you give a REGEX to --myaddr to tell cmd-itip your email addresses, cmd-itip
|
31
|
+
will avoid printing some information on the attendees to an invitation.
|
32
|
+
|
33
|
+
Modify muttrc to autoview calendars with a command like:
|
34
|
+
|
35
|
+
auto_view text/calendar
|
36
|
+
|
37
|
+
Notes on Notes;
|
38
|
+
|
39
|
+
Because Domino sends a close-to-unreadable text/plain attachment along with the
|
40
|
+
text/calendar in a multipart/alternative, and the text/plain is first in the
|
41
|
+
alternatives, the garbage will be at the top, and the nicely printed calendar
|
42
|
+
at the bottom. Because of this, I reorder the view preference so the calendar
|
43
|
+
invitation is clearly printed at the top of the message with a muttrc command
|
44
|
+
like:
|
45
|
+
|
46
|
+
alternative_order text/calendar text/plain
|
47
|
+
|
48
|
+
Domino also includes the calendar twice in the mail message, so you'll see it
|
49
|
+
twice, I don't know what to do about that.
|
50
|
+
|
51
|
+
Notes on application/octet-stream:
|
52
|
+
|
53
|
+
Some calendar programs, such as Apple's Mail.app, wrongly send iCalendar attachments
|
54
|
+
with a content-type of application/octet-stream. In order to be processed correctly, use
|
55
|
+
the mutt 1.5 or later capability to lookup the correct MIME content-type based on the
|
56
|
+
file extension. Put this in your muttrc file:
|
57
|
+
|
58
|
+
mime_lookup application/octet-stream
|
59
|
+
|
60
|
+
and ensure /etc/mime.types or ~/.mime.types contains:
|
61
|
+
|
62
|
+
text/calendar ics
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
** vcf-to-mutt.rb
|
67
|
+
|
68
|
+
This script searches a set of vCards can output the results as a Mutt query response,
|
69
|
+
or a Mutt aliases file.
|
70
|
+
|
71
|
+
It used to support querying the OS X Address Book, but that is better done with lbdb, see
|
72
|
+
http://www.spinnaker.de/lbdb/.
|
73
|
+
|
74
|
+
To install, you must:
|
75
|
+
|
76
|
+
1 - install vPim (see README)
|
77
|
+
|
78
|
+
3 - copy vcf-to-mutt into a directory in your path, such
|
79
|
+
as ~/bin, and chmod +x vcf-to-mutt.rb to make it executable.
|
80
|
+
|
81
|
+
4 - Put in your muttrc file (either ~/.muttrc or ~/.mutt/muttrc) a line such as:
|
82
|
+
|
83
|
+
set query_command = "vcf-to-mutt.rb '%s'"
|
84
|
+
|
85
|
+
5 - The query command ("Q") will query the address book, control-t will give you auto-completion
|
86
|
+
of email addresses, see the Mutt manual page.
|
87
|
+
|
88
|
+
|
89
|
+
** mutt-aliases-to-vcf.rb
|
90
|
+
|
91
|
+
This script converts a mutt aliases file into a vCard file.
|
92
|
+
|
93
|
+
|
data/samples/ab-query.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$-w = true
|
4
|
+
$:.unshift File.dirname($0) + '/../lib'
|
5
|
+
|
6
|
+
require 'osx-wrappers'
|
7
|
+
|
8
|
+
require 'getoptlong'
|
9
|
+
require 'vpim/vcard'
|
10
|
+
require 'osx-wrappers'
|
11
|
+
|
12
|
+
HELP =<<EOF
|
13
|
+
Usage: ab-query.rb [--me] [--all]
|
14
|
+
|
15
|
+
Queries the OS X Address Book for vCards.
|
16
|
+
|
17
|
+
-h, --help print this helpful message
|
18
|
+
-m, --me list my vCard
|
19
|
+
-a, --all list all vCards
|
20
|
+
EOF
|
21
|
+
|
22
|
+
opts = GetoptLong.new(
|
23
|
+
[ "--help", "-h", GetoptLong::NO_ARGUMENT ],
|
24
|
+
[ "--me", "-m", GetoptLong::NO_ARGUMENT ],
|
25
|
+
[ "--all", "-a", GetoptLong::NO_ARGUMENT ]
|
26
|
+
)
|
27
|
+
|
28
|
+
abook = nil
|
29
|
+
|
30
|
+
opts.each do |opt, arg|
|
31
|
+
case opt
|
32
|
+
when "--help" then
|
33
|
+
puts HELP
|
34
|
+
exit 0
|
35
|
+
|
36
|
+
when "--all" then
|
37
|
+
abook = OSX::ABAddressBook.sharedAddressBook unless abook
|
38
|
+
|
39
|
+
abook.people.to_a.each {
|
40
|
+
|person|
|
41
|
+
|
42
|
+
puts person.vCard
|
43
|
+
}
|
44
|
+
|
45
|
+
when "--me" then
|
46
|
+
abook = OSX::ABAddressBook.sharedAddressBook unless abook
|
47
|
+
|
48
|
+
puts abook.me.vCard
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
unless abook
|
54
|
+
puts HELP
|
55
|
+
exit 1
|
56
|
+
end
|
57
|
+
|