agcaldav 0.2.1 → 0.2.2
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/README.md +29 -5
- data/lib/agcaldav/client.rb +153 -146
- data/lib/agcaldav/event.rb +121 -0
- data/lib/agcaldav/format.rb +51 -55
- data/lib/agcaldav/version.rb +1 -1
- data/lib/agcaldav.rb +1 -9
- metadata +2 -1
data/README.md
CHANGED
@@ -27,23 +27,47 @@ Alternatively, the proxy parameters can be specified:
|
|
27
27
|
|
28
28
|
result = cal.create_event(:start => "2012-12-29 10:00", :end => "2012-12-30 12:00", :title => "12345", :description => "sdkvjsdf sdkf sdkfj sdkf dsfj")
|
29
29
|
|
30
|
-
|
30
|
+
Analyze result:
|
31
|
+
|
32
|
+
>> result.class
|
33
|
+
=> Icalendar::Calendar
|
34
|
+
|
35
|
+
>> result.events.count
|
36
|
+
=> 1
|
37
|
+
|
38
|
+
>> result.events.first
|
39
|
+
=> #<Icalendar::Event:0x007ff653b47520 @name="VEVENT", @components={}, @properties={"sequence"=>0, "dtstamp"=>#<DateTime: 2012-12-30T19:59:04+00:00 (26527957193/10800,0/1,2299161)>, "description"=>"sdkvjsdf sdkf sdkfj sdkf dsfj", "dtend"=>#<DateTime: 2012-12-30T12:00:00+00:00 (2456292/1,0/1,2299161)>, "dtstart"=>#<DateTime: 2012-12-29T10:00:00+00:00 (29475491/12,0/1,2299161)>, "summary"=>"12345", "uid"=>"e795c480-34e0-0130-7d1d-109add70606c", "x-radicale_name"=>"e795c480-34e0-0130-7d1d-109add70606c.ics"}>
|
31
40
|
|
32
|
-
|
41
|
+
get UID of this Event:
|
42
|
+
>> result.events.first.uid
|
43
|
+
=> "e795c480-34e0-0130-7d1d-109add70606c"
|
33
44
|
|
34
45
|
|
35
46
|
Find Event:
|
36
47
|
|
37
|
-
r = cal.find_event(
|
48
|
+
r = cal.find_event("e795c480-34e0-0130-7d1d-109add70606c")
|
49
|
+
|
50
|
+
>> result.class
|
51
|
+
=> Icalendar::Calendar
|
38
52
|
|
39
53
|
|
40
54
|
Find Events within time interval:
|
41
55
|
|
42
56
|
result = cal.find_events(:start => "2012-10-01 08:00", :end => "2013-01-01")
|
43
|
-
#TODO.... (no XML -> Icalendar with multiple events....)
|
44
57
|
|
45
58
|
|
59
|
+
####TODO's
|
60
|
+
|
61
|
+
###############################################################
|
62
|
+
# #
|
63
|
+
# TODO : #
|
64
|
+
# 1. find and notify if overlapping events #
|
65
|
+
# 2. "create_event" check for UUID is really unique #
|
66
|
+
# 3. errorhandling & code cleanup #
|
67
|
+
# #
|
68
|
+
###############################################################
|
46
69
|
|
47
|
-
... next tomorrow ...
|
48
70
|
|
71
|
+
####Licence
|
49
72
|
|
73
|
+
-> MIT
|
data/lib/agcaldav/client.rb
CHANGED
@@ -1,152 +1,159 @@
|
|
1
1
|
module AgCalDAV
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
def todo
|
127
|
-
res = nil
|
128
|
-
__create_http.start {|http|
|
129
|
-
req = Net::HTTP::Report.new(@url, initheader = {'Content-Type'=>'application/xml'} )
|
130
|
-
req.basic_auth @user, @password
|
131
|
-
req.body = AgCalDAV::Request::ReportVTODO.new.to_xml
|
132
|
-
res = http.request( req )
|
133
|
-
}
|
134
|
-
# FIXME: process HTTP code
|
135
|
-
format.parse_todo( res.body )
|
136
|
-
end
|
137
|
-
|
138
|
-
def filterTimezone( vcal )
|
139
|
-
data = ""
|
140
|
-
inTZ = false
|
141
|
-
vcal.split("\n").each{ |l|
|
142
|
-
inTZ = true if l.index("BEGIN:VTIMEZONE")
|
143
|
-
data << l+"\n" unless inTZ
|
144
|
-
inTZ = false if l.index("END:VTIMEZONE")
|
145
|
-
}
|
146
|
-
return data
|
147
|
-
end
|
2
|
+
class Client
|
3
|
+
include Icalendar
|
4
|
+
attr_accessor :host, :port, :url, :user, :password, :ssl
|
5
|
+
|
6
|
+
def format=( fmt )
|
7
|
+
@format = fmt
|
8
|
+
end
|
9
|
+
|
10
|
+
def format
|
11
|
+
@format ||= Format::Debug.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize( data )
|
15
|
+
unless data[:proxy_uri].nil?
|
16
|
+
proxy_uri = URI(data[:proxy_uri])
|
17
|
+
@proxy_host = proxy_uri.host
|
18
|
+
@proxy_port = proxy_uri.port.to_i
|
19
|
+
end
|
20
|
+
uri = URI(data[:uri])
|
21
|
+
@host = uri.host
|
22
|
+
@port = uri.port.to_i
|
23
|
+
@url = uri.path
|
24
|
+
@user = data[:user]
|
25
|
+
@password = data[:password]
|
26
|
+
@ssl = uri.scheme == 'https'
|
27
|
+
end
|
28
|
+
|
29
|
+
def __create_http
|
30
|
+
if @proxy_uri.nil?
|
31
|
+
http = Net::HTTP.new(@host, @port)
|
32
|
+
else
|
33
|
+
http = Net::HTTP.new(@host, @port, @proxy_host, @proxy_port)
|
34
|
+
end
|
35
|
+
if @ssl
|
36
|
+
http.use_ssl = @ssl
|
37
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
38
|
+
end
|
39
|
+
http
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_events data
|
43
|
+
result = ""
|
44
|
+
res = nil
|
45
|
+
__create_http.start {|http|
|
46
|
+
req = Net::HTTP::Report.new(@url, initheader = {'Content-Type'=>'application/xml'} )
|
47
|
+
req.basic_auth @user, @password
|
48
|
+
req.body = AgCalDAV::Request::ReportVEVENT.new(DateTime.parse(data[:start]).strftime("%Y%m%dT%H%M"),
|
49
|
+
DateTime.parse(data[:end]).strftime("%Y%m%dT%H%M") ).to_xml
|
50
|
+
res = http.request(req)
|
51
|
+
s = res.body
|
52
|
+
result = ""
|
53
|
+
xml = REXML::Document.new(s)
|
54
|
+
REXML::XPath.each( xml, '//c:calendar-data/', {"c"=>"urn:ietf:params:xml:ns:caldav"} ){|c| result << c.text}
|
55
|
+
r = Icalendar.parse(result)
|
56
|
+
r.first
|
57
|
+
|
58
|
+
}
|
59
|
+
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
def find_event uuid
|
64
|
+
res = nil
|
65
|
+
__create_http.start {|http|
|
66
|
+
req = Net::HTTP::Get.new("#{@url}/#{uuid}.ics")
|
67
|
+
req.basic_auth @user, @password
|
68
|
+
res = http.request( req )
|
69
|
+
}
|
70
|
+
raise AuthenticationError if res.code.to_i == 401
|
71
|
+
raise APIError if res.code.to_i >= 500
|
72
|
+
r = Icalendar.parse(res.body)
|
73
|
+
r.first
|
74
|
+
end
|
75
|
+
|
76
|
+
def delete_event uuid
|
77
|
+
__create_http.start {|http|
|
78
|
+
req = Net::HTTP::Delete.new("#{@url}/#{uuid}.ics")
|
79
|
+
req.basic_auth @user, @password
|
80
|
+
res = http.request( req )
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
def create_event event
|
85
|
+
c = Calendar.new
|
86
|
+
uuid = UUID.new.generate
|
87
|
+
c.event do
|
88
|
+
uid uuid # still a BUG
|
89
|
+
dtstart DateTime.parse(event[:start])
|
90
|
+
dtend DateTime.parse(event[:end])
|
91
|
+
duration event[:duration]
|
92
|
+
summary event[:title]
|
93
|
+
description event[:description]
|
94
|
+
klass event[:accessibility] #PUBLIC, PRIVATE, CONFIDENTIAL
|
95
|
+
location event[:location]
|
96
|
+
geo_location event[:geo_location]
|
97
|
+
status event[:status]
|
98
|
+
end
|
99
|
+
c.publish
|
100
|
+
c.event.uid = uuid
|
101
|
+
cstring = c.to_ical
|
102
|
+
res = nil
|
103
|
+
http = Net::HTTP.new(@host, @port)
|
104
|
+
__create_http.start { |http|
|
105
|
+
req = Net::HTTP::Put.new("#{@url}/#{uuid}.ics")
|
106
|
+
req['Content-Type'] = 'text/calendar'
|
107
|
+
req.basic_auth @user, @password
|
108
|
+
req.body = cstring
|
109
|
+
res = http.request( req )
|
110
|
+
}
|
111
|
+
raise AuthenticationError if res.code.to_i == 401
|
112
|
+
raise APIError if res.code.to_i >= 500
|
113
|
+
find_event uuid
|
114
|
+
#{:uid => uuid, :cal => c, :cal_string => cstring, :response_code => res.code} #TODO
|
115
|
+
end
|
116
|
+
|
117
|
+
def add_alarm tevent, altCal="Calendar"
|
118
|
+
# FIXME create icalendar event -> cal.event.new (tevent)
|
119
|
+
|
120
|
+
# TODO
|
121
|
+
|
122
|
+
|
123
|
+
|
124
|
+
|
148
125
|
end
|
149
126
|
|
127
|
+
def update event
|
128
|
+
# FIXME old one not neat
|
129
|
+
|
130
|
+
# TODO
|
131
|
+
end
|
132
|
+
|
133
|
+
def todo
|
134
|
+
res = nil
|
135
|
+
__create_http.start {|http|
|
136
|
+
req = Net::HTTP::Report.new(@url, initheader = {'Content-Type'=>'application/xml'} )
|
137
|
+
req.basic_auth @user, @password
|
138
|
+
req.body = AgCalDAV::Request::ReportVTODO.new.to_xml
|
139
|
+
res = http.request( req )
|
140
|
+
}
|
141
|
+
# FIXME: process HTTP code
|
142
|
+
format.parse_todo( res.body )
|
143
|
+
end
|
144
|
+
|
145
|
+
def filterTimezone( vcal )
|
146
|
+
data = ""
|
147
|
+
inTZ = false
|
148
|
+
vcal.split("\n").each{ |l|
|
149
|
+
inTZ = true if l.index("BEGIN:VTIMEZONE")
|
150
|
+
data << l+"\n" unless inTZ
|
151
|
+
inTZ = false if l.index("END:VTIMEZONE")
|
152
|
+
}
|
153
|
+
return data
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
150
157
|
class AgCalDAVError < StandardError
|
151
158
|
end
|
152
159
|
class AuthenticationError < AgCalDAVError; end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Icalendar
|
4
|
+
# A Event calendar component is a grouping of component
|
5
|
+
# properties, and possibly including Alarm calendar components, that
|
6
|
+
# represents a scheduled amount of time on a calendar. For example, it
|
7
|
+
# can be an activity; such as a one-hour long, department meeting from
|
8
|
+
# 8:00 AM to 9:00 AM, tomorrow. Generally, an event will take up time
|
9
|
+
# on an individual calendar.
|
10
|
+
class Event < Component
|
11
|
+
ical_component :alarms
|
12
|
+
|
13
|
+
## Single instance properties
|
14
|
+
|
15
|
+
# Access classification (PUBLIC, PRIVATE, CONFIDENTIAL...)
|
16
|
+
ical_property :ip_class, :klass
|
17
|
+
|
18
|
+
# Date & time of creation
|
19
|
+
ical_property :created
|
20
|
+
|
21
|
+
# Complete description of the calendar component
|
22
|
+
ical_property :description
|
23
|
+
|
24
|
+
attr_accessor :tzid
|
25
|
+
|
26
|
+
# Specifies date-time when calendar component begins
|
27
|
+
ical_property :dtstart, :start
|
28
|
+
|
29
|
+
# Latitude & longitude for specified activity
|
30
|
+
ical_property :geo, :geo_location
|
31
|
+
|
32
|
+
# Date & time this item was last modified
|
33
|
+
ical_property :last_modified
|
34
|
+
|
35
|
+
# Specifies the intended venue for this activity
|
36
|
+
ical_property :location
|
37
|
+
|
38
|
+
# Defines organizer of this item
|
39
|
+
ical_property :organizer
|
40
|
+
|
41
|
+
# Defines relative priority for this item (1-9... 1 = best)
|
42
|
+
ical_property :priority
|
43
|
+
|
44
|
+
# Indicate date & time when this item was created
|
45
|
+
ical_property :dtstamp, :timestamp
|
46
|
+
|
47
|
+
# Revision sequence number for this item
|
48
|
+
ical_property :sequence, :seq
|
49
|
+
|
50
|
+
# Defines overall status or confirmation of this item
|
51
|
+
ical_property :status
|
52
|
+
ical_property :summary
|
53
|
+
ical_property :transp, :transparency
|
54
|
+
|
55
|
+
# Defines a persistent, globally unique id for this item
|
56
|
+
ical_property :uid, :unique_id
|
57
|
+
|
58
|
+
# Defines a URL associated with this item
|
59
|
+
ical_property :url
|
60
|
+
ical_property :recurrence_id, :recurid
|
61
|
+
|
62
|
+
## Single but mutually exclusive properties (Not testing though)
|
63
|
+
|
64
|
+
# Specifies a date and time that this item ends
|
65
|
+
ical_property :dtend, :end
|
66
|
+
|
67
|
+
# Specifies a positive duration time
|
68
|
+
ical_property :duration
|
69
|
+
|
70
|
+
## Multi-instance properties
|
71
|
+
|
72
|
+
# Associates a URI or binary blob with this item
|
73
|
+
ical_multi_property :attach, :attachment, :attachments
|
74
|
+
|
75
|
+
# Defines an attendee for this calendar item
|
76
|
+
ical_multiline_property :attendee, :attendee, :attendees
|
77
|
+
|
78
|
+
# Defines the categories for a calendar component (school, work...)
|
79
|
+
ical_multi_property :categories, :category, :categories
|
80
|
+
|
81
|
+
# Simple comment for the calendar user.
|
82
|
+
ical_multi_property :comment, :comment, :comments
|
83
|
+
|
84
|
+
# Contact information associated with this item.
|
85
|
+
ical_multi_property :contact, :contact, :contacts
|
86
|
+
ical_multi_property :exdate, :exception_date, :exception_dates
|
87
|
+
ical_multi_property :exrule, :exception_rule, :exception_rules
|
88
|
+
ical_multi_property :rstatus, :request_status, :request_statuses
|
89
|
+
|
90
|
+
# Used to represent a relationship between two calendar items
|
91
|
+
ical_multi_property :related_to, :related_to, :related_tos
|
92
|
+
ical_multi_property :resources, :resource, :resources
|
93
|
+
|
94
|
+
# Used with the UID & SEQUENCE to identify a specific instance of a
|
95
|
+
# recurring calendar item.
|
96
|
+
ical_multi_property :rdate, :recurrence_date, :recurrence_dates
|
97
|
+
ical_multi_property :rrule, :recurrence_rule, :recurrence_rules
|
98
|
+
|
99
|
+
def initialize()
|
100
|
+
super("VEVENT")
|
101
|
+
|
102
|
+
# Now doing some basic initialization
|
103
|
+
sequence 0
|
104
|
+
timestamp DateTime.now
|
105
|
+
end
|
106
|
+
|
107
|
+
def alarm(&block)
|
108
|
+
a = Alarm.new
|
109
|
+
self.add a
|
110
|
+
|
111
|
+
a.instance_eval(&block) if block
|
112
|
+
|
113
|
+
a
|
114
|
+
end
|
115
|
+
|
116
|
+
def occurrences_starting(time)
|
117
|
+
recurrence_rules.first.occurrences_of_event_starting(self, time)
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
data/lib/agcaldav/format.rb
CHANGED
@@ -1,64 +1,60 @@
|
|
1
1
|
module AgCalDAV
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
2
|
+
module Format
|
3
|
+
class Raw
|
4
|
+
def method_missing(m, *args, &block)
|
5
|
+
return *args
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Debug < Raw
|
10
|
+
end
|
8
11
|
|
9
|
-
|
10
|
-
|
12
|
+
class Pretty < Raw
|
13
|
+
def parse_calendar(s)
|
14
|
+
result = ""
|
15
|
+
xml = REXML::Document.new(s)
|
16
|
+
puts "-"*10
|
17
|
+
p s
|
18
|
+
puts "-"*10
|
19
|
+
REXML::XPath.each( xml, '//c:calendar-data/', {"c"=>"urn:ietf:params:xml:ns:caldav"} ){|c| result << c.text}
|
20
|
+
r = Icalendar.parse(result)
|
11
21
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
result += parse_events( c.text )
|
18
|
-
}
|
19
|
-
return result
|
20
|
-
end
|
22
|
+
puts "."*10
|
23
|
+
p r
|
24
|
+
puts "."*10
|
25
|
+
r
|
26
|
+
end
|
21
27
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
def parse_todo( body )
|
29
|
+
result = []
|
30
|
+
xml = REXML::Document.new( body )
|
31
|
+
REXML::XPath.each( xml, '//c:calendar-data/', { "c"=>"urn:ietf:params:xml:ns:caldav"} ){ |c|
|
32
|
+
p c.text
|
33
|
+
p parse_tasks( c.text )
|
34
|
+
result += parse_tasks( c.text )
|
35
|
+
}
|
36
|
+
return result
|
37
|
+
end
|
32
38
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
39
|
+
def parse_tasks( vcal )
|
40
|
+
return_tasks = Array.new
|
41
|
+
cals = Icalendar.parse(vcal)
|
42
|
+
cals.each { |tcal|
|
43
|
+
tcal.todos.each { |ttask| # FIXME
|
44
|
+
return_tasks << ttask
|
45
|
+
}
|
46
|
+
}
|
47
|
+
return return_tasks
|
48
|
+
end
|
43
49
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
cals.each { |tcal|
|
48
|
-
tcal.events.each { |tevent|
|
49
|
-
if tevent.recurrence_id.to_s.empty? # skip recurring events
|
50
|
-
return_events << tevent
|
51
|
-
end
|
52
|
-
}
|
53
|
-
}
|
54
|
-
return return_events
|
55
|
-
end
|
50
|
+
def parse_events( vcal )
|
51
|
+
Icalendar.parse(vcal)
|
52
|
+
end
|
56
53
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
end
|
54
|
+
def parse_single( body )
|
55
|
+
# FIXME: parse event/todo/vcard
|
56
|
+
parse_events( body )
|
57
|
+
end
|
62
58
|
end
|
59
|
+
end
|
63
60
|
end
|
64
|
-
|
data/lib/agcaldav/version.rb
CHANGED
data/lib/agcaldav.rb
CHANGED
@@ -6,14 +6,6 @@ require 'icalendar'
|
|
6
6
|
require 'time'
|
7
7
|
require 'date'
|
8
8
|
|
9
|
-
['client.rb', 'request.rb', 'net.rb', 'query.rb', 'filter.rb', 'format.rb'].each do |f|
|
9
|
+
['client.rb', 'request.rb', 'net.rb', 'query.rb', 'filter.rb', 'event.rb', 'format.rb'].each do |f|
|
10
10
|
require File.join( File.dirname(__FILE__), 'agcaldav', f )
|
11
|
-
end
|
12
|
-
|
13
|
-
class Event
|
14
|
-
attr_accessor :uid, :created, :dtstart, :dtend, :lastmodified, :summary, :description, :name, :action, :to_ical
|
15
|
-
end
|
16
|
-
|
17
|
-
class Todo
|
18
|
-
attr_accessor :uid, :created, :summary, :dtstart, :status, :completed
|
19
11
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: agcaldav
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -86,6 +86,7 @@ files:
|
|
86
86
|
- agcaldav.gemspec
|
87
87
|
- lib/agcaldav.rb
|
88
88
|
- lib/agcaldav/client.rb
|
89
|
+
- lib/agcaldav/event.rb
|
89
90
|
- lib/agcaldav/filter.rb
|
90
91
|
- lib/agcaldav/format.rb
|
91
92
|
- lib/agcaldav/net.rb
|