fairtilizer-vpim 0.695

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.
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
+