vpim 0.658 → 0.695
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +6 -0
- data/bin/reminder +17 -11
- data/bin/rrule +0 -0
- data/lib/vpim/agent/app.rb +194 -0
- data/lib/vpim/agent/atomize.rb +101 -0
- data/lib/vpim/agent/calendars.rb +173 -0
- data/lib/vpim/agent/main.rb +327 -0
- data/lib/vpim/agent/scraps.rb +292 -0
- data/lib/vpim/repo.rb +81 -1
- data/lib/vpim/rrule.rb +0 -0
- data/lib/vpim/version.rb +2 -2
- data/samples/cmd-itip.rb +0 -0
- data/samples/ics-dump.rb +0 -0
- data/samples/ics-to-rss.rb +0 -0
- data/samples/mutt-aliases-to-vcf.rb +0 -0
- data/samples/reminder.rb +17 -11
- data/samples/rrule.rb +0 -0
- data/samples/vcf-dump.rb +0 -0
- data/samples/vcf-lines.rb +0 -0
- data/samples/vcf-to-ics.rb +0 -0
- data/samples/vcf-to-mutt.rb +0 -0
- data/test/test_agent_app.rb +74 -0
- data/test/test_agent_atomize.rb +84 -0
- data/test/test_agent_calendars.rb +128 -0
- data/test/test_all.rb +1 -1
- data/test/test_date.rb +0 -0
- data/test/test_dur.rb +0 -0
- data/test/test_field.rb +0 -0
- data/test/test_ical.rb +22 -0
- data/test/test_repo.rb +44 -92
- data/test/test_rrule.rb +0 -0
- data/test/test_vcard.rb +0 -0
- metadata +14 -10
- data/lib/atom.rb +0 -728
- data/lib/atom/pub.rb +0 -206
- data/lib/atom/version.rb +0 -9
- data/lib/atom/xml/parser.rb +0 -305
- data/lib/plist.rb +0 -22
- data/lib/plist/generator.rb +0 -224
- data/lib/plist/parser.rb +0 -225
data/CHANGES
CHANGED
data/bin/reminder
CHANGED
@@ -18,8 +18,8 @@ Usage: #{$0} [where]
|
|
18
18
|
|
19
19
|
Shows events and todos occuring soon.
|
20
20
|
|
21
|
-
By default, the Apple iCal v3 calendars are used, but if a location where
|
22
|
-
|
21
|
+
By default, the Apple iCal v3 calendars are used, but if a location where .ics
|
22
|
+
files can be found is specified, any calendars found there will be used.
|
23
23
|
|
24
24
|
Options
|
25
25
|
-h,--help Print this helpful message.
|
@@ -29,6 +29,7 @@ Options
|
|
29
29
|
EOF
|
30
30
|
|
31
31
|
opt_debug = nil
|
32
|
+
opt_dump = nil
|
32
33
|
opt_verbose = nil
|
33
34
|
opt_days = 7
|
34
35
|
|
@@ -53,6 +54,9 @@ opts.each do |opt, arg|
|
|
53
54
|
|
54
55
|
when "--debug" then
|
55
56
|
opt_verbose = true
|
57
|
+
if opt_debug
|
58
|
+
opt_dump = true
|
59
|
+
end
|
56
60
|
opt_debug = true
|
57
61
|
end
|
58
62
|
end
|
@@ -64,12 +68,12 @@ if ARGV.length > 0
|
|
64
68
|
calendars << cal
|
65
69
|
end
|
66
70
|
else
|
67
|
-
Vpim::Repo::
|
71
|
+
Vpim::Repo::Apple3.new.each() do |cal|
|
68
72
|
calendars << cal
|
69
73
|
end
|
70
74
|
end
|
71
75
|
|
72
|
-
if
|
76
|
+
if opt_dump
|
73
77
|
pp ARGV
|
74
78
|
pp calendars
|
75
79
|
end
|
@@ -81,7 +85,7 @@ t0[0] = t0[1] = t0[2] = 0 # sec,min,hour = 0
|
|
81
85
|
t0 = Time.local(*t0)
|
82
86
|
t1 = t0 + opt_days * SECSPERDAY
|
83
87
|
|
84
|
-
if
|
88
|
+
if opt_dump
|
85
89
|
puts "to: #{t0}"
|
86
90
|
puts "t1: #{t1}"
|
87
91
|
end
|
@@ -95,12 +99,13 @@ all_events = []
|
|
95
99
|
all_todos = []
|
96
100
|
|
97
101
|
calendars.each do |cal|
|
98
|
-
if opt_debug; puts cal.name; end
|
102
|
+
if opt_debug; puts "Calendar: #{cal.name}"; end
|
99
103
|
|
104
|
+
# TODO - mv collection algorithm to library
|
100
105
|
begin
|
101
106
|
cal.events.each do |e|
|
102
107
|
begin
|
103
|
-
if
|
108
|
+
if opt_dump; pp e; end
|
104
109
|
if e.occurs_in?(t0, t1)
|
105
110
|
if e.summary
|
106
111
|
all_events.push(e)
|
@@ -111,12 +116,13 @@ calendars.each do |cal|
|
|
111
116
|
end
|
112
117
|
end
|
113
118
|
|
114
|
-
all_todos.concat(cal.todos)
|
119
|
+
all_todos.concat(cal.todos.to_a)
|
115
120
|
end
|
116
121
|
end
|
117
122
|
|
118
123
|
puts
|
119
124
|
|
125
|
+
# TODO - mv sorting algorithm to library
|
120
126
|
def start_of_first_occurrence(t0, t1, e)
|
121
127
|
e.occurrences(t1) do |t|
|
122
128
|
# An event might start before t0, but end after it..., in which case
|
@@ -137,7 +143,7 @@ all_events.each do |e|
|
|
137
143
|
|
138
144
|
if opt_verbose
|
139
145
|
if e.description; puts " description=#{e.description}"; end
|
140
|
-
if e.comments
|
146
|
+
if e.comments.any?; puts " comment=#{e.comments.first}"; end
|
141
147
|
if e.location; puts " location=#{e.location}"; end
|
142
148
|
if e.status; puts " status=#{e.status}"; end
|
143
149
|
if e.dtstart; puts " dtstart=#{e.dtstart}"; end
|
@@ -145,15 +151,15 @@ all_events.each do |e|
|
|
145
151
|
end
|
146
152
|
|
147
153
|
i = 1
|
148
|
-
e.occurrences
|
154
|
+
e.occurrences(t1) do |t|
|
149
155
|
# An event might start before t0, but end after it..., in which case
|
150
156
|
# we are still interested.
|
151
157
|
dstr = ''
|
152
158
|
if e.duration
|
153
|
-
d = e.duration
|
154
159
|
dstr = " for #{Vpim::Duration.new(e.duration).to_s}"
|
155
160
|
end
|
156
161
|
|
162
|
+
# TODO - mv to library, as variant of occurs_in?
|
157
163
|
if (t + (e.duration || 0)) >= t0
|
158
164
|
puts " ##{i} on #{t}#{dstr}"
|
159
165
|
i += 1
|
data/bin/rrule
CHANGED
File without changes
|
@@ -0,0 +1,194 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2009 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 'sinatra'
|
10
|
+
require 'vpim/agent/atomize'
|
11
|
+
require 'vpim/repo'
|
12
|
+
require 'vpim/view'
|
13
|
+
|
14
|
+
require 'cgi'
|
15
|
+
|
16
|
+
configure do
|
17
|
+
server = Sinatra::Application.server
|
18
|
+
set :server, Proc.new {
|
19
|
+
if ENV.include?("PHP_FCGI_CHILDREN")
|
20
|
+
break "fastcgi" # Must NOT be the correct class name!
|
21
|
+
elsif ENV.include?("REQUEST_METHOD")
|
22
|
+
break "cgi" # Must NOT be the correct class name!
|
23
|
+
else
|
24
|
+
# Fall back on whatever it was going to be.
|
25
|
+
server
|
26
|
+
end
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
# I could wrap the Repo/Calendar/Atomize in a small class that would memoize
|
31
|
+
# ical data and atom output. Maybe even do an HTTP head for fast detection of
|
32
|
+
# change? Does a calendar have updated information? Can we memoize atom when
|
33
|
+
# ics doesn't change?
|
34
|
+
|
35
|
+
module Vpim
|
36
|
+
module Agent
|
37
|
+
module App
|
38
|
+
def self.atomize(caluri, feeduri)
|
39
|
+
repo = Vpim::Repo::Uri.new(caluri)
|
40
|
+
cal = repo.find{true}
|
41
|
+
cal = View.week(cal)
|
42
|
+
feed = Agent::Atomize.calendar(cal, feeduri, caluri, cal.name)
|
43
|
+
return feed.to_xml, Agent::Atomize::MIME
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
get '/ics' do
|
50
|
+
from = env['QUERY_STRING']
|
51
|
+
|
52
|
+
url = URI.parse(request.url)
|
53
|
+
url.query = nil
|
54
|
+
url_base = url.to_s
|
55
|
+
url_atom = nil
|
56
|
+
|
57
|
+
@url_ics = from # ics from here
|
58
|
+
@url_atom = nil
|
59
|
+
|
60
|
+
if not from.empty?
|
61
|
+
# Error out if we can't atomize the feed
|
62
|
+
Vpim::Agent::App.atomize(from, "http://example.com")
|
63
|
+
|
64
|
+
url = URI.parse(request.url)
|
65
|
+
url.path << "/atom"
|
66
|
+
url_atom = url.to_s
|
67
|
+
end
|
68
|
+
|
69
|
+
@url_base = url_base # clean input form
|
70
|
+
@url_atom = url_atom # atomized ics from here
|
71
|
+
|
72
|
+
haml :"ics.haml"
|
73
|
+
end
|
74
|
+
|
75
|
+
post '/ics' do
|
76
|
+
from = params[:url]
|
77
|
+
url = URI.parse(request.url)
|
78
|
+
url.query = from
|
79
|
+
redirect url.to_s
|
80
|
+
end
|
81
|
+
|
82
|
+
# When we support other forms..
|
83
|
+
#get '/ics/:form' do
|
84
|
+
# form = params[:form]
|
85
|
+
get '/ics/atom' do
|
86
|
+
from = env['QUERY_STRING']
|
87
|
+
port = env["SERVER_PORT"].to_i
|
88
|
+
here = request.url
|
89
|
+
|
90
|
+
if from.empty?
|
91
|
+
url = URI.parse(here)
|
92
|
+
url.path.sub(/atom$/, "")
|
93
|
+
redirect here.to_s
|
94
|
+
end
|
95
|
+
|
96
|
+
xml, xmltype = Vpim::Agent::App.atomize(from, here)
|
97
|
+
|
98
|
+
content_type xmltype
|
99
|
+
body xml
|
100
|
+
end
|
101
|
+
|
102
|
+
get '/ics/style.css' do
|
103
|
+
content_type 'text/css'
|
104
|
+
sass :"ics.sass"
|
105
|
+
end
|
106
|
+
|
107
|
+
error do
|
108
|
+
@url_error = CGI.escapeHTML(env['sinatra.error'].inspect)
|
109
|
+
haml :"ics.haml"
|
110
|
+
end
|
111
|
+
|
112
|
+
use_in_file_templates!
|
113
|
+
|
114
|
+
# FIXME - hard-coded :action paths below, bad!
|
115
|
+
|
116
|
+
__END__
|
117
|
+
@@ics.sass
|
118
|
+
body
|
119
|
+
:background-color gray
|
120
|
+
:font-size medium
|
121
|
+
a
|
122
|
+
:color black
|
123
|
+
:font-style italic
|
124
|
+
a:hover
|
125
|
+
:color darkred
|
126
|
+
|
127
|
+
#header
|
128
|
+
:border-bottom 3px solid darkred
|
129
|
+
#title
|
130
|
+
:color black
|
131
|
+
:font-size large
|
132
|
+
|
133
|
+
.text
|
134
|
+
:width 80%
|
135
|
+
-#:color yellow
|
136
|
+
|
137
|
+
#submit
|
138
|
+
:margin-top 30px
|
139
|
+
:margin-left 5%
|
140
|
+
#form
|
141
|
+
:padding
|
142
|
+
:top 10px
|
143
|
+
:bottom 10px
|
144
|
+
:left 10px
|
145
|
+
:right 10px
|
146
|
+
:text-align left
|
147
|
+
#url
|
148
|
+
:width 80%
|
149
|
+
#button
|
150
|
+
:font-weight bold
|
151
|
+
:text-align center
|
152
|
+
|
153
|
+
#subscribe
|
154
|
+
:margin-left 5%
|
155
|
+
-#.feed
|
156
|
+
-# :margin-left 10%
|
157
|
+
|
158
|
+
#footer
|
159
|
+
:border-top 3px solid darkred
|
160
|
+
:margin-top 20px
|
161
|
+
@@ics.haml
|
162
|
+
%html
|
163
|
+
%head
|
164
|
+
%title Subscribe to calendar feeds as atom feeds
|
165
|
+
%link{:href => '/ics/style.css', :media => 'screen', :type => 'text/css'}
|
166
|
+
%body
|
167
|
+
#header
|
168
|
+
%span#title Subscribe to calendar feeds as atom feeds
|
169
|
+
#submit
|
170
|
+
.text Calendar feeds are great, but sometimes all you want is an atom feed of what's coming up in the next week.
|
171
|
+
.text Paste the URL of the calendar below, submit it, and subscribe.
|
172
|
+
%form#form{:method => 'POST', :action => '/ics'}
|
173
|
+
%input#url{:name => 'url', :value => params[:url]}
|
174
|
+
%input#button{:type => 'submit', :value => 'Submit'}
|
175
|
+
- if @url_atom
|
176
|
+
#subscribe
|
177
|
+
.text
|
178
|
+
Subscribe to
|
179
|
+
%a{:href => @url_ics}= @url_ics
|
180
|
+
as:
|
181
|
+
%ul.feed
|
182
|
+
%a{:href => @url_atom}= @url_atom
|
183
|
+
(atom feed)
|
184
|
+
- if @url_error
|
185
|
+
#error.text
|
186
|
+
#preamble Sorry, trying to access:
|
187
|
+
#source= @url_ics
|
188
|
+
#transition resulted in:
|
189
|
+
#destination= @url_error
|
190
|
+
#footer
|
191
|
+
.text
|
192
|
+
:textile
|
193
|
+
Coming from the "Octet Cloud":http://octetcloud.com/ using "vPim":http://vpim.rubyforge.org/, piloted by cloud monkey "Sam Roberts":mailto:vieuxtech@gmail.com
|
194
|
+
|
@@ -0,0 +1,101 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2009 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 "atom"
|
10
|
+
|
11
|
+
module Vpim
|
12
|
+
module Agent
|
13
|
+
|
14
|
+
module Atomize
|
15
|
+
MIME = "application/atom+xml"
|
16
|
+
|
17
|
+
# +ical+, an icalendar, or at least a Repo calendar's subset of an Icalendar
|
18
|
+
# +feeduri+, the atom xml should know the URI of where the feed is available from.
|
19
|
+
# +caluri+, optionally, the URI of the calendar its converted from.
|
20
|
+
#
|
21
|
+
# TODO - and the URI of an alternative/html representation of this feed?
|
22
|
+
def self.calendar(ical, feeduri, caluri = nil, calname = nil)
|
23
|
+
mime = MIME
|
24
|
+
|
25
|
+
feeduri = feeduri.to_str
|
26
|
+
caluri = caluri
|
27
|
+
calname = (calname or caluri or "Unknown").to_str
|
28
|
+
|
29
|
+
f = Atom::Feed.new
|
30
|
+
# Mandatory attributes:
|
31
|
+
# For ID, we should use http://.../ics/atom?....., or just the URL of the ics?
|
32
|
+
# I think it can be a full URI... or maybe a sha-1 of our full URI?
|
33
|
+
# or like gmail, no id for feed,
|
34
|
+
# <id>tag:gmail.google.com,2004:1295062805013769502</id>
|
35
|
+
#
|
36
|
+
f.id = feeduri
|
37
|
+
f.title = calname
|
38
|
+
f.updated = Time.now
|
39
|
+
f.authors << Atom::Person.new(:name => (caluri or calname))
|
40
|
+
f.generator = Atom::Generator.new do |g|
|
41
|
+
g.name = Vpim::PRODID
|
42
|
+
g.uri = "http://vpim.rubyforge.org"
|
43
|
+
g.version = Vpim::VERSION
|
44
|
+
end
|
45
|
+
|
46
|
+
f.links << Atom::Link.new do |l|
|
47
|
+
l.href = feeduri
|
48
|
+
l.type = mime
|
49
|
+
l.rel = :self
|
50
|
+
end
|
51
|
+
|
52
|
+
if caluri
|
53
|
+
# This is maybe better described as :via, but with :alternate being
|
54
|
+
# an html view of this feed.
|
55
|
+
f.links << Atom::Link.new do |l|
|
56
|
+
l.href = caluri
|
57
|
+
l.type = "text/calendar"
|
58
|
+
l.rel = :alternate
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# .icon = uri to the vAgent icon
|
63
|
+
entry_id = 0
|
64
|
+
ical.events do |ve|
|
65
|
+
# TODO - infinite?
|
66
|
+
ve.occurrences do |t|
|
67
|
+
f.entries << Atom::Entry.new do |e|
|
68
|
+
# iCalendar -> atom
|
69
|
+
# -----------------
|
70
|
+
# summary -> title
|
71
|
+
# description -> text/content
|
72
|
+
# uid -> id
|
73
|
+
# created -> published?
|
74
|
+
# organizer -> author?
|
75
|
+
# contact -> author?
|
76
|
+
# last-mod -> semantically, this is updated, but atom doesn't
|
77
|
+
# have the notion that an entry has a relationship to a time,
|
78
|
+
# other than the time the entry itself was published, and when
|
79
|
+
# the entry gets updated. We'll abuse updated for the event's time.
|
80
|
+
# categories -> where do "tags" go in atom, if anywhere?
|
81
|
+
# attachment -> into a link?
|
82
|
+
e.title = ve.summary if ve.summary
|
83
|
+
e.content = Atom::Content::Text.new(ve.description) if ve.description
|
84
|
+
e.updated = t
|
85
|
+
|
86
|
+
# Use "tag:", as defined by RFC4151, and use event UID if possible. Otherwise,
|
87
|
+
# construct something. Maybe I should mix something in to make it unique for
|
88
|
+
# each time a feed is generated for the calendar?
|
89
|
+
entry_id += 1
|
90
|
+
tag = ve.uid || "#{entry_id}@#{feeduri}"
|
91
|
+
e.id = "tag:vpim.rubyforge.org,2009:#{tag}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
return f
|
96
|
+
end
|
97
|
+
end # Atomize
|
98
|
+
|
99
|
+
end # Agent
|
100
|
+
end # Vpim
|
101
|
+
|
@@ -0,0 +1,173 @@
|
|
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 "cgi"
|
10
|
+
require "uri"
|
11
|
+
|
12
|
+
require "vpim/repo"
|
13
|
+
require "vpim/agent/atomize"
|
14
|
+
|
15
|
+
module Vpim
|
16
|
+
module Agent
|
17
|
+
# On failure, raise this with an error message. text/plain for now,
|
18
|
+
# text/html later. Will convert to a 404 and a message.
|
19
|
+
class NotFound < Exception
|
20
|
+
def initialize(name, path)
|
21
|
+
super %{Resource "#{name}" under "#{path.prefix}" was not found!}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class Path
|
26
|
+
def self.split_path(path)
|
27
|
+
begin
|
28
|
+
path = path.to_ary
|
29
|
+
rescue NameError
|
30
|
+
path = path.split("/")
|
31
|
+
end
|
32
|
+
path.map{|w| CGI.unescape(w)}
|
33
|
+
end
|
34
|
+
|
35
|
+
# URI is the uri being queried, base is where this path is mounted under?
|
36
|
+
def initialize(uri, base = "")
|
37
|
+
@uri = URI.parse(uri.to_s)
|
38
|
+
#pp [uri, base, @uri]
|
39
|
+
if @uri.path.size == 0
|
40
|
+
@uri.path = "/"
|
41
|
+
end
|
42
|
+
@path = Path.split_path(@uri.path)
|
43
|
+
@base = base.to_str
|
44
|
+
@mark = 0
|
45
|
+
|
46
|
+
@base.split.size.times{ shift }
|
47
|
+
end
|
48
|
+
|
49
|
+
def uri
|
50
|
+
@uri.to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_path
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
# TODO - call this #next
|
58
|
+
def shift
|
59
|
+
if @path[@mark]
|
60
|
+
@path[@mark += 1]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def append(name, scheme = nil)
|
65
|
+
uri = @uri.dup
|
66
|
+
uri.path += "/" + CGI.escape(name)
|
67
|
+
if scheme
|
68
|
+
uri.scheme = scheme
|
69
|
+
end
|
70
|
+
uri
|
71
|
+
end
|
72
|
+
|
73
|
+
def prefix(len = nil)
|
74
|
+
len ||= @mark
|
75
|
+
@path[0, len].map{|p| CGI.escape(p)}.join("/") + "/"
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
module Form
|
81
|
+
ATOM = Atomize::MIME
|
82
|
+
HTML = "text/html"
|
83
|
+
ICS = "text/calendar"
|
84
|
+
PLAIN = "text/plain"
|
85
|
+
VCF = "text/directory"
|
86
|
+
end
|
87
|
+
|
88
|
+
# Return an HTML description of a list of resources accessible under this
|
89
|
+
# path.
|
90
|
+
class ResourceList
|
91
|
+
def initialize(description, items)
|
92
|
+
@description = description
|
93
|
+
@items = items
|
94
|
+
end
|
95
|
+
|
96
|
+
def get(path)
|
97
|
+
return <<__, Form::HTML
|
98
|
+
<html><body>
|
99
|
+
#{@description}
|
100
|
+
<ul>
|
101
|
+
#{
|
102
|
+
@items.map do |name,description,scheme|
|
103
|
+
"<li><a href=\"#{path.append(name,scheme)}\">#{description || name}</a></li>\n"
|
104
|
+
end
|
105
|
+
}
|
106
|
+
</ul>
|
107
|
+
</body></html>
|
108
|
+
__
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Return calendar information based on RESTful (lovein' the jargon...)
|
113
|
+
# paths. Input is a Vpim::Repo.
|
114
|
+
#
|
115
|
+
# .../coding/month/atom
|
116
|
+
# .../coding/events/month/ics <- next month?
|
117
|
+
# .../coding/events/month/2008-04/ics <- a specified month?
|
118
|
+
# .../coding/week/atom
|
119
|
+
# .../year/atom
|
120
|
+
class Calendars
|
121
|
+
def initialize(repo)
|
122
|
+
@repo = repo
|
123
|
+
end
|
124
|
+
|
125
|
+
class Calendar
|
126
|
+
def initialize(cal)
|
127
|
+
@cal = cal
|
128
|
+
@list = ResourceList.new(
|
129
|
+
"Calendar #{@cal.name.inspect}:",
|
130
|
+
[
|
131
|
+
["calendar", "download"],
|
132
|
+
["calendar", "subscription", "webcal"],
|
133
|
+
["atom", "syndication"],
|
134
|
+
]
|
135
|
+
)
|
136
|
+
end
|
137
|
+
|
138
|
+
def get(path)
|
139
|
+
form = path.shift
|
140
|
+
|
141
|
+
# TODO should redirect to an object, so that extra paths can be
|
142
|
+
# handled more gracefully.
|
143
|
+
case form
|
144
|
+
when nil
|
145
|
+
return @list.get(path)
|
146
|
+
when "calendar"
|
147
|
+
return @cal.encode, Form::ICS
|
148
|
+
when "atom"
|
149
|
+
return Atomize.calendar(@cal, path.uri, nil, @cal.name).to_xml, Form::ATOM
|
150
|
+
else
|
151
|
+
raise NotFound.new(form, path)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# Get object at this path. Return value is a tuple of data and mime content type.
|
157
|
+
def get(path)
|
158
|
+
case name = path.to_path.shift
|
159
|
+
when nil
|
160
|
+
list = ResourceList.new("Calendars:", @repo.map{|c| c.name})
|
161
|
+
return list.get(path)
|
162
|
+
else
|
163
|
+
if cal = @repo.find{|c| c.name == name}
|
164
|
+
return Calendar.new(cal).get(path)
|
165
|
+
else
|
166
|
+
raise NotFound.new(name, path)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|