fairtilizer-vpim 0.695

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/CHANGES +510 -0
  2. data/COPYING +58 -0
  3. data/README +182 -0
  4. data/lib/vpim.rb +13 -0
  5. data/lib/vpim/address.rb +219 -0
  6. data/lib/vpim/agent/atomize.rb +104 -0
  7. data/lib/vpim/agent/base.rb +73 -0
  8. data/lib/vpim/agent/calendars.rb +173 -0
  9. data/lib/vpim/agent/handler.rb +26 -0
  10. data/lib/vpim/agent/ics.rb +161 -0
  11. data/lib/vpim/attachment.rb +102 -0
  12. data/lib/vpim/date.rb +222 -0
  13. data/lib/vpim/dirinfo.rb +277 -0
  14. data/lib/vpim/duration.rb +119 -0
  15. data/lib/vpim/enumerator.rb +32 -0
  16. data/lib/vpim/field.rb +614 -0
  17. data/lib/vpim/icalendar.rb +384 -0
  18. data/lib/vpim/maker/vcard.rb +16 -0
  19. data/lib/vpim/property/base.rb +193 -0
  20. data/lib/vpim/property/common.rb +315 -0
  21. data/lib/vpim/property/location.rb +38 -0
  22. data/lib/vpim/property/priority.rb +43 -0
  23. data/lib/vpim/property/recurrence.rb +69 -0
  24. data/lib/vpim/property/resources.rb +24 -0
  25. data/lib/vpim/repo.rb +261 -0
  26. data/lib/vpim/rfc2425.rb +367 -0
  27. data/lib/vpim/rrule.rb +591 -0
  28. data/lib/vpim/time.rb +40 -0
  29. data/lib/vpim/vcard.rb +1426 -0
  30. data/lib/vpim/version.rb +18 -0
  31. data/lib/vpim/vevent.rb +187 -0
  32. data/lib/vpim/view.rb +90 -0
  33. data/lib/vpim/vjournal.rb +58 -0
  34. data/lib/vpim/vpim.rb +65 -0
  35. data/lib/vpim/vtodo.rb +103 -0
  36. data/samples/README.mutt +93 -0
  37. data/samples/ab-query.rb +57 -0
  38. data/samples/agent.ru +10 -0
  39. data/samples/cmd-itip.rb +156 -0
  40. data/samples/ex_cpvcard.rb +55 -0
  41. data/samples/ex_get_vcard_photo.rb +22 -0
  42. data/samples/ex_mkv21vcard.rb +34 -0
  43. data/samples/ex_mkvcard.rb +64 -0
  44. data/samples/ex_mkyourown.rb +29 -0
  45. data/samples/ics-dump.rb +210 -0
  46. data/samples/ics-to-rss.rb +84 -0
  47. data/samples/mutt-aliases-to-vcf.rb +45 -0
  48. data/samples/osx-wrappers.rb +86 -0
  49. data/samples/reminder.rb +209 -0
  50. data/samples/rrule.rb +71 -0
  51. data/samples/tabbed-file-to-vcf.rb +390 -0
  52. data/samples/vcf-dump.rb +86 -0
  53. data/samples/vcf-lines.rb +61 -0
  54. data/samples/vcf-to-ics.rb +22 -0
  55. data/samples/vcf-to-mutt.rb +121 -0
  56. data/test/test_agent_atomize.rb +84 -0
  57. data/test/test_agent_calendars.rb +128 -0
  58. data/test/test_agent_ics.rb +96 -0
  59. data/test/test_all.rb +17 -0
  60. data/test/test_date.rb +120 -0
  61. data/test/test_dur.rb +41 -0
  62. data/test/test_field.rb +156 -0
  63. data/test/test_ical.rb +437 -0
  64. data/test/test_misc.rb +13 -0
  65. data/test/test_repo.rb +129 -0
  66. data/test/test_rrule.rb +1030 -0
  67. data/test/test_vcard.rb +973 -0
  68. data/test/test_view.rb +79 -0
  69. metadata +140 -0
@@ -0,0 +1,104 @@
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
+ #
56
+ # TODO should I change the scheme to be webcal?
57
+ # TODO should I extend URI to support webcal?
58
+ f.links << Atom::Link.new do |l|
59
+ l.href = caluri
60
+ l.type = "text/calendar"
61
+ l.rel = :alternate
62
+ end
63
+ end
64
+
65
+ # .icon = uri to the vAgent icon
66
+ entry_id = 0
67
+ ical.events do |ve|
68
+ # TODO - infinite?
69
+ ve.occurrences do |t|
70
+ f.entries << Atom::Entry.new do |e|
71
+ # iCalendar -> atom
72
+ # -----------------
73
+ # summary -> title
74
+ # description -> text/content
75
+ # uid -> id
76
+ # created -> published?
77
+ # organizer -> author?
78
+ # contact -> author?
79
+ # last-mod -> semantically, this is updated, but atom doesn't
80
+ # have the notion that an entry has a relationship to a time,
81
+ # other than the time the entry itself was published, and when
82
+ # the entry gets updated. We'll abuse updated for the event's time.
83
+ # categories -> where do "tags" go in atom, if anywhere?
84
+ # attachment -> into a link?
85
+ e.title = ve.summary if ve.summary
86
+ e.content = Atom::Content::Text.new(ve.description) if ve.description
87
+ e.updated = t
88
+
89
+ # Use "tag:", as defined by RFC4151, and use event UID if possible. Otherwise,
90
+ # construct something. Maybe I should mix something in to make it unique for
91
+ # each time a feed is generated for the calendar?
92
+ entry_id += 1
93
+ tag = ve.uid || "#{entry_id}@#{feeduri}"
94
+ e.id = "tag:vpim.rubyforge.org,2009:#{tag}"
95
+ end
96
+ end
97
+ end
98
+ return f
99
+ end
100
+ end # Atomize
101
+
102
+ end # Agent
103
+ end # Vpim
104
+
@@ -0,0 +1,73 @@
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 'cgi'
10
+
11
+ require 'sinatra/base'
12
+
13
+ # TODO Pasting of webcal links, conversion to webcal links?
14
+
15
+ module Vpim
16
+ module Agent
17
+
18
+ class Base < Sinatra::Base
19
+ # Ensure that this happens...
20
+ set :haml, :format=>:html4 # Appears to do nothing, but maybe it will some day...
21
+
22
+ def css(template)
23
+ render :css, template, {}
24
+ end
25
+
26
+ def render_css(template, data, options) # :nodoc:
27
+ data
28
+ end
29
+
30
+ # Complete path, as requested by the client. Take care about CGI path rewriting.
31
+ def request_path
32
+ # Using .to_s because rack/request.rb does, though I think the Rack
33
+ # spec requires these to be strings already.
34
+ begin
35
+ URI.parse(env["SCRIPT_URI"].to_s).path
36
+ rescue
37
+ env["SCRIPT_NAME"].to_s + env["PATH_INFO"].to_s
38
+ end
39
+ end
40
+
41
+ # Complete path, as requested by the client, without the env's PATH_INFO.
42
+ # This is the path to whatever is "handling" the request.
43
+ #
44
+ # Recent discussions on how PATH_INFO must be decoded leads me to think
45
+ # this might not work if the path had any URL encoded characters in it.
46
+ def script_path
47
+ request_path.sub(/#{env["PATH_INFO"]}$/, "")
48
+ end
49
+
50
+ # URL-ready form of the host and port, where the port isn't specified if
51
+ # it is the default for the URL scheme.
52
+ def host_port
53
+ r = request
54
+ host_port = r.host
55
+
56
+ if r.scheme == "https" && r.port != 443 ||
57
+ r.scheme == "http" && r.port != 80
58
+ host_port << ":#{r.port}"
59
+ end
60
+
61
+ host_port
62
+ end
63
+
64
+ # URL to the script
65
+ def script_url
66
+ request.scheme + "://" + host_port + script_path
67
+ end
68
+
69
+ end # Base
70
+
71
+ end # Agent
72
+ end # Vpim
73
+
@@ -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
+
@@ -0,0 +1,26 @@
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/base'
10
+
11
+ # Auto-choose our handler based on the environment.
12
+ # TODO Code should be in Sinatra, and should handle Thin, Mongrel, etc.
13
+ Sinatra::Base.configure do
14
+ server = Sinatra::Base.server
15
+ Sinatra::Base.set :server, Proc.new {
16
+ if ENV.include?("PHP_FCGI_CHILDREN")
17
+ break "fastcgi" # Must NOT be the correct class name!
18
+ elsif ENV.include?("REQUEST_METHOD")
19
+ break "cgi" # Must NOT be the correct class name!
20
+ else
21
+ # Fall back on whatever it was going to be.
22
+ server
23
+ end
24
+ }
25
+ end
26
+
@@ -0,0 +1,161 @@
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 'cgi'
10
+
11
+ require 'vpim/agent/base'
12
+ require 'vpim/agent/atomize'
13
+ require 'vpim/repo'
14
+ require 'vpim/view'
15
+
16
+ require 'sinatra/base'
17
+
18
+ module Vpim
19
+ module Agent
20
+
21
+ class Ics < Base
22
+ use_in_file_templates!
23
+
24
+ def atomize(caluri, feeduri)
25
+ repo = Vpim::Repo::Uri.new(caluri)
26
+ cal = repo.find{true}
27
+ cal = View.week(cal)
28
+ feed = Agent::Atomize.calendar(cal, feeduri, caluri, cal.name)
29
+ return feed.to_xml, Agent::Atomize::MIME
30
+ end
31
+
32
+ ## Route handlers:
33
+ def get_base(from)
34
+ @url_base = script_url # agent mount point
35
+ @url_ics = from # ics from here
36
+ @url_atom = nil # atom feed from here, if ics is accessible
37
+ @url_error= nil # error message, if is is not accessible
38
+
39
+ if not from.empty?
40
+ begin
41
+ atomize(from, "http://example.com")
42
+ @url_atom = @url_base + "/atom" + "?" + from
43
+ rescue
44
+ @url_error = CGI.escapeHTML($!.to_s)
45
+ end
46
+ end
47
+
48
+ haml :"vpim/agent/ics/view"
49
+ end
50
+
51
+ # When we support other forms..
52
+ #get '/ics/:form' do
53
+ # form = params[:form]
54
+ def get_atom(caluri)
55
+ if caluri.empty?
56
+ redirect script_url
57
+ end
58
+
59
+ feeduri = script_url + "/atom?" + caluri
60
+
61
+ begin
62
+ xml, xmltype = atomize(caluri, feeduri)
63
+ content_type xmltype
64
+ body xml
65
+ rescue
66
+ redirect script_url + "?" + caluri
67
+ end
68
+ end
69
+
70
+ def get_style
71
+ content_type 'text/css'
72
+ css :"vpim/agent/ics/style"
73
+ end
74
+
75
+ ## Sinatra routing:
76
+ get '/?' do
77
+ get_base(env['QUERY_STRING'])
78
+ end
79
+
80
+ post "/?" do
81
+ redirect script_url + "?" + (params[:url] || "")
82
+ end
83
+
84
+ get "/atom" do
85
+ get_atom(env['QUERY_STRING'])
86
+ end
87
+
88
+ get '/style.css' do
89
+ get_style
90
+ end
91
+
92
+ end # Ics
93
+
94
+ end # Agent
95
+ end # Vpim
96
+
97
+ __END__
98
+ @@vpim/agent/ics/style
99
+ body {
100
+ background-color: gray;
101
+ }
102
+ h1 {
103
+ border-bottom: 3px solid #8B0000;
104
+ font-size: large;
105
+ }
106
+ form {
107
+ margin-left: 10%;
108
+ }
109
+ input.text {
110
+ width: 80%;
111
+ }
112
+ a {
113
+ color: black;
114
+ }
115
+ a:hover {
116
+ color: #8B0000;
117
+ }
118
+ tt {
119
+ margin-left: 10%;
120
+ }
121
+ .footer {
122
+ border-top: 3px solid #8B0000;
123
+ }
124
+ @@vpim/agent/ics/view
125
+ !!! strict
126
+ %html
127
+ %head
128
+ %title Subscribe to calendar feeds as atom feeds
129
+ %link{:href => script_url + "/style.css", :media => "screen", :type => "text/css"}
130
+ %meta{:"http-equiv" => "Content-Type", :content => "text/html;charset=utf-8"}
131
+ %body
132
+ %h1 Subscribe to calendar feeds as atom feeds
133
+ %p
134
+ Calendar feeds are great, but when you want a reminder of what's coming up
135
+ in the next week, you might want those events as an atom feed.
136
+ %p
137
+ Paste the URL of the calendar below, submit it, and subscribe.
138
+ %form{:method => 'POST', :action => script_url}
139
+ %p
140
+ %input.text{:type => 'text', :name => 'url', :value => @url_ics}
141
+ %input{:type => 'submit', :value => 'Submit'}
142
+ - if @url_atom
143
+ %p
144
+ Subscribe to
145
+ %a{:href => @url_ics}= @url_ics
146
+ as:
147
+ %ul.feed
148
+ %li
149
+ %a{:href => @url_atom}= @url_atom
150
+ (atom feed)
151
+ - if @url_error
152
+ %p
153
+ Sorry, trying to access
154
+ %tt=@url_ics
155
+ resulted in:
156
+ %p
157
+ %tt= @url_error
158
+ .footer
159
+ :textile
160
+ Brought from the "Octet Cloud":http://octetcloud.com/ using "vPim":http://vpim.rubyforge.org/, by cloud monkey "Sam Roberts":mailto:vieuxtech@gmail.com.
161
+