googlecalendar 0.0.6 → 1.0.0
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/{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
|