googlecalendar 0.0.6 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/{CHANGELOG → History.txt} +12 -0
- data/Manifest.txt +20 -0
- data/PostInstall.txt +14 -0
- data/README.rdoc +55 -0
- data/README.txt +55 -0
- data/Rakefile +4 -0
- data/bin/googlecalendar4ruby +44 -0
- data/html/scripts.js +9 -0
- data/html/styles.css +11 -0
- data/lib/builders.rb +2 -95
- data/lib/googlecalendar.rb +10 -384
- data/lib/googlecalendar/calendar.rb +23 -0
- data/lib/googlecalendar/dsl.rb +52 -0
- data/lib/googlecalendar/event.rb +32 -0
- data/lib/googlecalendar/gcalendar.rb +10 -0
- data/lib/googlecalendar/gdata.rb +200 -0
- data/lib/googlecalendar/ical.rb +126 -0
- data/lib/googlecalendar/net.rb +14 -0
- data/lib/googlecalendar/version.rb +10 -0
- data/lib/googlecalendar_builders.rb +85 -0
- data/test/test_event.rb +12 -0
- data/test/test_googlecalendar.rb +11 -0
- data/test/test_helper.rb +4 -0
- data/test/test_ical.rb +16 -0
- metadata +93 -38
- data/README +0 -14
@@ -0,0 +1,23 @@
|
|
1
|
+
module Googlecalendar
|
2
|
+
class Calendar
|
3
|
+
attr_accessor :product_id, :version, :scale, :method, :events
|
4
|
+
|
5
|
+
def add(event)
|
6
|
+
# create events if it doesn't exist
|
7
|
+
@events ||= []
|
8
|
+
@events.push event
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
data = "########## calendar ##########\n"
|
13
|
+
data << 'version: ' + @version.to_s + "\n"
|
14
|
+
data << 'scale: ' + @scale.to_s + "\n"
|
15
|
+
data << 'method: ' + @method.to_s + "\n"
|
16
|
+
data << 'number of events: ' + @events.size.to_s + "\n"
|
17
|
+
@events.each do |event|
|
18
|
+
data << event.to_s
|
19
|
+
end
|
20
|
+
return data
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end # module Googlecalendar
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
# require 'net/https'
|
3
|
+
# require 'uri'
|
4
|
+
|
5
|
+
def parse(data)
|
6
|
+
parser = Googlecalendar::ICALParser.new
|
7
|
+
parser.parse(data)
|
8
|
+
end
|
9
|
+
|
10
|
+
def scan(ical_url, base_url='www.google.com')
|
11
|
+
Net::HTTP.start(base_url, 80) do |http|
|
12
|
+
response, data = http.get(ical_url)
|
13
|
+
case response
|
14
|
+
when Net::HTTPSuccess, Net::HTTPRedirection
|
15
|
+
return data
|
16
|
+
else
|
17
|
+
response.error!
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def scan_proxy(proxy_addr, proxy_port, ical_url, base_url='www.google.com')
|
23
|
+
Net::HTTP::Proxy(proxy_addr, proxy_port).start(base_url, 80) do |http|
|
24
|
+
response, data = http.get(ical_url)
|
25
|
+
case response
|
26
|
+
when Net::HTTPSuccess, Net::HTTPRedirection
|
27
|
+
return data
|
28
|
+
else
|
29
|
+
response.error!
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Builder DSL
|
35
|
+
def text(calendar, filename)
|
36
|
+
dirname = File.dirname(filename)
|
37
|
+
Dir.mkdir(dirname) unless File.exists?(dirname)
|
38
|
+
text_builder = Googlecalendar::TextBuilder.new
|
39
|
+
text_builder.calendar = calendar
|
40
|
+
text_builder.filename = filename
|
41
|
+
text_builder.export
|
42
|
+
end
|
43
|
+
|
44
|
+
def html(calendar, filename)
|
45
|
+
dirname = File.dirname(filename)
|
46
|
+
Dir.mkdir(dirname) unless File.exists?(dirname)
|
47
|
+
html_builder = Googlecalendar::HtmlBuilder.new
|
48
|
+
html_builder.calendar = calendar
|
49
|
+
html_builder.filename = filename
|
50
|
+
html_builder.date_format = "%d-%m-%y" #"%F"#"%A %B %d %Y" #"%d-%m-%Y"
|
51
|
+
html_builder.export
|
52
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Googlecalendar
|
2
|
+
class Event
|
3
|
+
attr_accessor :start_date, :end_date, :time_stamp, :class_name, :created, :last_modified, :status, :summary, :description, :location, :rrule
|
4
|
+
|
5
|
+
def to_s
|
6
|
+
data = "---------- event ----------\n"
|
7
|
+
data << 'start_date: ' + @start_date.to_s + "\n"
|
8
|
+
data << 'end_date: ' + @end_date.to_s + "\n"
|
9
|
+
data << 'time_stamp: ' + @time_stamp.to_s + "\n"
|
10
|
+
data << 'class_name: ' + @class_name.to_s + "\n"
|
11
|
+
data << 'created: ' + @created.to_s + "\n"
|
12
|
+
data << 'last_modified: ' + @last_modified.to_s + "\n"
|
13
|
+
data << 'status: ' + @status.to_s + "\n"
|
14
|
+
data << 'rrule: ' + @rrule.to_s + "\n"
|
15
|
+
data << 'summary: ' + @summary.to_s + "\n"
|
16
|
+
data << 'desription: ' + @desription.to_s + "\n"
|
17
|
+
data << 'location: ' + @location.to_s + "\n"
|
18
|
+
return data
|
19
|
+
end
|
20
|
+
|
21
|
+
# 'FREQ=WEEKLY;BYDAY=MO;WKST=MO'
|
22
|
+
def rrule_as_hash
|
23
|
+
array = @rrule.split(';')
|
24
|
+
hash = Hash.new
|
25
|
+
array.each do |item|
|
26
|
+
pair = item.split('=')
|
27
|
+
hash[pair[0]] = pair[1]
|
28
|
+
end
|
29
|
+
return hash
|
30
|
+
end
|
31
|
+
end # class Event
|
32
|
+
end # module Googlecalendar
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
require 'uri'
|
4
|
+
require "rexml/document"
|
5
|
+
|
6
|
+
module Googlecalendar
|
7
|
+
# A ruby class to wrap calls to the Google Data API
|
8
|
+
#
|
9
|
+
# More informations
|
10
|
+
#
|
11
|
+
# Google calendar API: http://code.google.com/apis/calendar/developers_guide_protocol.html
|
12
|
+
class GData
|
13
|
+
attr_accessor :google_url
|
14
|
+
|
15
|
+
def initialize(google='www.google.com')
|
16
|
+
@calendars = []
|
17
|
+
@google_url = google
|
18
|
+
end
|
19
|
+
|
20
|
+
#Convinient method to create the conf file use in login_with_conf_file
|
21
|
+
#if you don't want to create it by hand
|
22
|
+
def self.create_conf_file(email, pwd)
|
23
|
+
p = {'email' => email, 'password' => pwd}
|
24
|
+
path = File.expand_path("~/.googlecalendar4ruby/google.yaml")
|
25
|
+
unless File.exists?(path)
|
26
|
+
puts "Creating file in #{path}"
|
27
|
+
Dir.mkdir(File.dirname(path))
|
28
|
+
end
|
29
|
+
f = File.new(path, File::CREAT|File::TRUNC|File::RDWR)
|
30
|
+
f << p.to_yaml
|
31
|
+
f.close
|
32
|
+
end
|
33
|
+
|
34
|
+
#Log into google data
|
35
|
+
#This method is basically the same as the login with user and password
|
36
|
+
#but it tries to find a ~/.googlecalendar4ruby/google.yml
|
37
|
+
#Use this if you don't want to hardcode your user/password in your .rb files
|
38
|
+
def login_with_conf_file(file='~/.googlecalendar4ruby/google.yaml')
|
39
|
+
path = File.expand_path(file)
|
40
|
+
if(File.exists?(path))
|
41
|
+
File.open(path) { |f| @yaml = YAML::load(f) }
|
42
|
+
else
|
43
|
+
GData::create_conf_file('REPLACE_WITH_YOUR_MAIL@gmail.com', 'REPLACE_WITH_YOUR_PASSWORD')
|
44
|
+
throw "Created a default file in: #{path}, you need to edit it !!"
|
45
|
+
end
|
46
|
+
email = @yaml['email']
|
47
|
+
pwd = @yaml['password']
|
48
|
+
login(email, pwd)
|
49
|
+
end # login
|
50
|
+
|
51
|
+
#Log into google data, this method needs to be call once before using other methods of the class
|
52
|
+
#* Email The user's email address.
|
53
|
+
#* Passwd The user's password.
|
54
|
+
#* source Identifies your client application. Should take the form companyName-applicationName-versionID
|
55
|
+
#*Warning* Replace the default value with something like:
|
56
|
+
#+companyName-applicationName-versionID+
|
57
|
+
def login(email, pwd, source='googlecalendar.rubyforge.org-googlecalendar-default')
|
58
|
+
# service The string cl, which is the service name for Google Calendar.
|
59
|
+
@user_id = email
|
60
|
+
response = Net::HTTPS.post_form(URI.parse("https://#{@google_url}/accounts/ClientLogin"),
|
61
|
+
{ 'Email' => email,
|
62
|
+
'Passwd' => pwd,
|
63
|
+
'source' => source,
|
64
|
+
'accountType' => 'HOSTED_OR_GOOGLE',
|
65
|
+
'service' => 'cl'})
|
66
|
+
response.error! unless response.kind_of? Net::HTTPSuccess
|
67
|
+
@token = response.body.split(/=/).last
|
68
|
+
@headers = {
|
69
|
+
'Authorization' => "GoogleLogin auth=#{@token}",
|
70
|
+
'Content-Type' => 'application/atom+xml'
|
71
|
+
}
|
72
|
+
return @token
|
73
|
+
end # login
|
74
|
+
|
75
|
+
# Reset reminders
|
76
|
+
def reset_reminders(event)
|
77
|
+
event[:reminders] = ""
|
78
|
+
end
|
79
|
+
|
80
|
+
# Add a reminder to the event hash
|
81
|
+
#* reminderMinutes
|
82
|
+
#* reminderMethod [email, alert, sms, none]
|
83
|
+
def add_reminder(event, reminderMinutes, reminderMethod)
|
84
|
+
event[:reminders] = event[:reminders].to_s +
|
85
|
+
"<gd:reminder minutes='#{reminderMinutes}' method='#{reminderMethod}' />\n"
|
86
|
+
end
|
87
|
+
|
88
|
+
# Create a quick add event
|
89
|
+
#
|
90
|
+
# <tt>text = 'Tennis with John April 11 3pm-3:30pm'</tt>
|
91
|
+
#
|
92
|
+
# http://code.google.com/apis/calendar/developers_guide_protocol.html#CreatingQuickAdd
|
93
|
+
def quick_add(text)
|
94
|
+
content = <<EOF
|
95
|
+
<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gCal='http://schemas.google.com/gCal/2005'>
|
96
|
+
<content type="html">#{text}</content>
|
97
|
+
<gCal:quickadd value="true"/>
|
98
|
+
</entry>
|
99
|
+
EOF
|
100
|
+
post_event(content)
|
101
|
+
end # quick_add
|
102
|
+
|
103
|
+
#'event' param is a hash containing
|
104
|
+
#* :title
|
105
|
+
#* :content
|
106
|
+
#* :author
|
107
|
+
#* :email
|
108
|
+
#* :where
|
109
|
+
#* :startTime '2007-06-06T15:00:00.000Z'
|
110
|
+
#* :endTime '2007-06-06T17:00:00.000Z'
|
111
|
+
#
|
112
|
+
# Use add_reminder(event, reminderMinutes, reminderMethod) method to add reminders
|
113
|
+
def new_event(event={},calendar = nil)
|
114
|
+
new_event = template(event)
|
115
|
+
post_event(new_event, calendar)
|
116
|
+
end
|
117
|
+
|
118
|
+
def post_event(xml, calendar = nil)
|
119
|
+
#Get calendar url
|
120
|
+
calendar_url = if calendar
|
121
|
+
get_calendars
|
122
|
+
find_calendar(calendar).url
|
123
|
+
else
|
124
|
+
# We will use user'default calendar in this case
|
125
|
+
'/calendar/feeds/default/private/full'
|
126
|
+
end
|
127
|
+
|
128
|
+
http = Net::HTTP.new(@google_url, 80)
|
129
|
+
response, data = http.post(calendar_url, xml, @headers)
|
130
|
+
case response
|
131
|
+
when Net::HTTPSuccess, Net::HTTPRedirection
|
132
|
+
redirect_response, redirect_data = http.post(response['location'], xml, @headers)
|
133
|
+
case response
|
134
|
+
when Net::HTTPSuccess, Net::HTTPRedirection
|
135
|
+
return redirect_response
|
136
|
+
else
|
137
|
+
response.error!
|
138
|
+
end
|
139
|
+
else
|
140
|
+
response.error!
|
141
|
+
end
|
142
|
+
end # post_event
|
143
|
+
|
144
|
+
# Retreive user's calendar urls.
|
145
|
+
def get_calendars
|
146
|
+
http = Net::HTTP.new(@google_url, 80)
|
147
|
+
response, data = http.get("http://#{@google_url}/calendar/feeds/" + @user_id, @headers)
|
148
|
+
case response
|
149
|
+
when Net::HTTPSuccess, Net::HTTPRedirection
|
150
|
+
redirect_response, redirect_data = http.get(response['location'], @headers)
|
151
|
+
case response
|
152
|
+
when Net::HTTPSuccess, Net::HTTPRedirection
|
153
|
+
doc = REXML::Document.new redirect_data
|
154
|
+
doc.elements.each('//entry')do |e|
|
155
|
+
title = e.elements['title'].text
|
156
|
+
url = e.elements['link'].attributes['href']
|
157
|
+
@calendars << GCalendar.new(title, url.sub!("http://#{@google_url}",''))
|
158
|
+
end
|
159
|
+
return redirect_response
|
160
|
+
else
|
161
|
+
response.error!
|
162
|
+
end
|
163
|
+
else
|
164
|
+
response.error!
|
165
|
+
end
|
166
|
+
end # get_calendars
|
167
|
+
|
168
|
+
def find_calendar(x)
|
169
|
+
@calendars.find {|c| c.title.match x}
|
170
|
+
end
|
171
|
+
|
172
|
+
# The atom event template to submit a new event
|
173
|
+
def template(event={})
|
174
|
+
content = <<EOF
|
175
|
+
<?xml version="1.0"?>
|
176
|
+
<entry xmlns='http://www.w3.org/2005/Atom'
|
177
|
+
xmlns:gd='http://schemas.google.com/g/2005'>
|
178
|
+
<category scheme='http://schemas.google.com/g/2005#kind'
|
179
|
+
term='http://schemas.google.com/g/2005#event'></category>
|
180
|
+
<title type='text'>#{event[:title]}</title>
|
181
|
+
<content type='text'>#{event[:content]}</content>
|
182
|
+
<author>
|
183
|
+
<name>#{event[:author]}</name>
|
184
|
+
<email>#{event[:email]}</email>
|
185
|
+
</author>
|
186
|
+
<gd:transparency
|
187
|
+
value='http://schemas.google.com/g/2005#event.opaque'>
|
188
|
+
</gd:transparency>
|
189
|
+
<gd:eventStatus
|
190
|
+
value='http://schemas.google.com/g/2005#event.confirmed'>
|
191
|
+
</gd:eventStatus>
|
192
|
+
<gd:where valueString='#{event[:where]}'></gd:where>
|
193
|
+
<gd:when startTime='#{event[:startTime]}' endTime='#{event[:endTime]}'>
|
194
|
+
#{event[:reminders]}
|
195
|
+
</gd:when>
|
196
|
+
</entry>
|
197
|
+
EOF
|
198
|
+
end # template
|
199
|
+
end # GData class
|
200
|
+
end # module Googlecalendar
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Googlecalendar
|
2
|
+
class ICALParser
|
3
|
+
attr_reader :calendar
|
4
|
+
|
5
|
+
def parse(data)
|
6
|
+
lines = data.split("\n")
|
7
|
+
|
8
|
+
reset_prefix
|
9
|
+
lines.each do |line|
|
10
|
+
handle_element(line)
|
11
|
+
end
|
12
|
+
|
13
|
+
return @calendar
|
14
|
+
end
|
15
|
+
|
16
|
+
# An ICal line consist of 2 part XXX: YYYY
|
17
|
+
# Get the first part of the line and after a small transformation call the appropriate method
|
18
|
+
# example:
|
19
|
+
# line is: VCALENDAR:BEGIN
|
20
|
+
# VERSION:VALUE
|
21
|
+
# method call: handle_vcalendar_version
|
22
|
+
# kind of work like a sax parser...
|
23
|
+
def handle_element(line)
|
24
|
+
pair = line.split(':', 2) # avoid problems when summary contains ':'
|
25
|
+
name = pair[0]
|
26
|
+
value = pair[1]
|
27
|
+
handler_method = @method_prefix + name.split(';')[0].tr('-', '_').downcase
|
28
|
+
if self.respond_to? handler_method
|
29
|
+
self.send(handler_method, value.chomp)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def reset_prefix
|
34
|
+
@method_prefix = "handle_"
|
35
|
+
end
|
36
|
+
|
37
|
+
def handle_begin(value)
|
38
|
+
if value == "VCALENDAR"
|
39
|
+
handle_vcalendar_begin(value)
|
40
|
+
elsif value == "VEVENT"
|
41
|
+
handle_vevent_begin(value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def handle_vcalendar_end(value)
|
46
|
+
reset_prefix
|
47
|
+
end
|
48
|
+
|
49
|
+
def handle_vcalendar_begin(value)
|
50
|
+
@calendar = Calendar.new
|
51
|
+
@method_prefix = @method_prefix + value.downcase + "_"
|
52
|
+
end
|
53
|
+
|
54
|
+
# def handle_vcalendar_prodid(value)
|
55
|
+
# @calendar.product_id = value
|
56
|
+
# end
|
57
|
+
|
58
|
+
def handle_vcalendar_version(value)
|
59
|
+
@calendar.version = value
|
60
|
+
end
|
61
|
+
|
62
|
+
def handle_vcalendar_calscale(value)
|
63
|
+
@calendar.scale = value
|
64
|
+
end
|
65
|
+
|
66
|
+
def handle_vcalendar_method(value)
|
67
|
+
@calendar.method = value
|
68
|
+
# FIXME don't like to do this!
|
69
|
+
reset_prefix
|
70
|
+
end
|
71
|
+
|
72
|
+
def handle_vevent_begin(value)
|
73
|
+
event = Event.new
|
74
|
+
@calendar.add event
|
75
|
+
@method_prefix = @method_prefix + value.downcase + "_"
|
76
|
+
end
|
77
|
+
|
78
|
+
def handle_vevent_end(value)
|
79
|
+
reset_prefix
|
80
|
+
end
|
81
|
+
|
82
|
+
def handle_vevent_dtstart(value)
|
83
|
+
@calendar.events.last.start_date = Date.parse(value)
|
84
|
+
end
|
85
|
+
|
86
|
+
def handle_vevent_dtend(value)
|
87
|
+
@calendar.events.last.end_date = Date.parse(value)
|
88
|
+
end
|
89
|
+
|
90
|
+
def handle_vevent_dtstamp(value)
|
91
|
+
@calendar.events.last.time_stamp = DateTime.parse(value)
|
92
|
+
end
|
93
|
+
|
94
|
+
def handle_vevent_class(value)
|
95
|
+
@calendar.events.last.class_name = value
|
96
|
+
end
|
97
|
+
|
98
|
+
def handle_vevent_created(value)
|
99
|
+
@calendar.events.last.created = DateTime.parse(value)
|
100
|
+
end
|
101
|
+
|
102
|
+
def handle_vevent_last_modified(value)
|
103
|
+
@calendar.events.last.last_modified = DateTime.parse(value)
|
104
|
+
end
|
105
|
+
|
106
|
+
def handle_vevent_status(value)
|
107
|
+
@calendar.events.last.status = value
|
108
|
+
end
|
109
|
+
|
110
|
+
def handle_vevent_summary(value)
|
111
|
+
@calendar.events.last.summary = value
|
112
|
+
end
|
113
|
+
|
114
|
+
def handle_vevent_rrule(value)
|
115
|
+
@calendar.events.last.rrule = value
|
116
|
+
end
|
117
|
+
|
118
|
+
def handle_vevent_description(value)
|
119
|
+
@calendar.events.last.description = value
|
120
|
+
end
|
121
|
+
|
122
|
+
def handle_vevent_location(value)
|
123
|
+
@calendar.events.last.location = value
|
124
|
+
end
|
125
|
+
end # class ICALParser
|
126
|
+
end # module Googlecalendar
|