concerto_calendar 0.0.1
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/LICENSE +13 -0
- data/README.md +8 -0
- data/Rakefile +40 -0
- data/app/assets/javascripts/concerto_calendar/application.js +1 -0
- data/app/assets/javascripts/concerto_calendar/concerto_calendar.js +24 -0
- data/app/assets/stylesheets/concerto_calendar/application.css +13 -0
- data/app/models/calendar.rb +305 -0
- data/app/views/contents/calendar/_form_top.html.erb +93 -0
- data/app/views/contents/calendar/_render_default.html.erb +30 -0
- data/app/views/contents/calendar/_render_grid.html.erb +3 -0
- data/app/views/contents/calendar/_render_tile.html.erb +5 -0
- data/app/views/contents/calendar/_tab_icon.html.erb +1 -0
- data/config/routes.rb +3 -0
- data/lib/concerto_calendar.rb +4 -0
- data/lib/concerto_calendar/engine.rb +9 -0
- data/lib/concerto_calendar/version.rb +3 -0
- data/test/concerto_calendar_test.rb +7 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +59 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/content_types.rb +1 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/test_helper.rb +15 -0
- metadata +190 -0
data/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright 2013 Concerto Authors
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Concerto 2 Calendar Plugin
|
|
2
|
+
This plugin provides support for displaying Google Calendar or iCal entries in Concerto 2.
|
|
3
|
+
|
|
4
|
+
1. Add to your Gemfile: ```gem 'concerto_calendar'```
|
|
5
|
+
2. ```bundle install```
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Concerto 2 Calendar is licensed under the Apache License, Version 2.0.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env rake
|
|
2
|
+
begin
|
|
3
|
+
require 'bundler/setup'
|
|
4
|
+
rescue LoadError
|
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
|
6
|
+
end
|
|
7
|
+
begin
|
|
8
|
+
require 'rdoc/task'
|
|
9
|
+
rescue LoadError
|
|
10
|
+
require 'rdoc/rdoc'
|
|
11
|
+
require 'rake/rdoctask'
|
|
12
|
+
RDoc::Task = Rake::RDocTask
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
17
|
+
rdoc.title = 'ConcertoCalendar'
|
|
18
|
+
rdoc.options << '--line-numbers'
|
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
|
|
24
|
+
load 'rails/tasks/engine.rake'
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
Bundler::GemHelper.install_tasks
|
|
29
|
+
|
|
30
|
+
require 'rake/testtask'
|
|
31
|
+
|
|
32
|
+
Rake::TestTask.new(:test) do |t|
|
|
33
|
+
t.libs << 'lib'
|
|
34
|
+
t.libs << 'test'
|
|
35
|
+
t.pattern = 'test/**/*_test.rb'
|
|
36
|
+
t.verbose = false
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
task :default => :test
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//= require_tree .
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
function attachHandlers() {
|
|
2
|
+
$('select#calendar_config_calendar_source').on('change', revealRelevantFields);
|
|
3
|
+
|
|
4
|
+
function revealRelevantFields() {
|
|
5
|
+
var vendor = $('select#calendar_config_calendar_source').val();
|
|
6
|
+
if (vendor == 'google') {
|
|
7
|
+
$('input#calendar_config_api_key').closest('div.clearfix').show();
|
|
8
|
+
$('input#calendar_config_calendar_id').closest('div.clearfix').show();
|
|
9
|
+
$('input#calendar_config_calendar_url').closest('div.clearfix').hide();
|
|
10
|
+
} else if (vendor == 'ical') {
|
|
11
|
+
$('input#calendar_config_api_key').closest('div.clearfix').hide();
|
|
12
|
+
$('input#calendar_config_calendar_id').closest('div.clearfix').hide();
|
|
13
|
+
$('input#calendar_config_calendar_url').closest('div.clearfix').show();
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
revealRelevantFields();
|
|
18
|
+
|
|
19
|
+
$("input#calendar_config_start_date.datefield").datepicker();
|
|
20
|
+
$("input#calendar_config_end_date.datefield").datepicker();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
$(document).ready(attachHandlers);
|
|
24
|
+
$(document).on('page:change', attachHandlers);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
|
3
|
+
* listed below.
|
|
4
|
+
*
|
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
|
6
|
+
* or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
|
|
7
|
+
*
|
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
|
9
|
+
* compiled file, but it's generally better to create a new file per style scope.
|
|
10
|
+
*
|
|
11
|
+
*= require_self
|
|
12
|
+
*= require_tree .
|
|
13
|
+
*/
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
class Calendar < DynamicContent
|
|
2
|
+
after_initialize :set_defaults, :on => :new
|
|
3
|
+
validate :validate_config, :on => :create
|
|
4
|
+
|
|
5
|
+
# this is the common class used for holding the content to be rendered
|
|
6
|
+
# it is populated from the various calendar sources
|
|
7
|
+
class CalendarResults
|
|
8
|
+
class CalendarResultItem
|
|
9
|
+
attr_accessor :name, :description, :location, :start_time, :end_time
|
|
10
|
+
|
|
11
|
+
def initialize(name, description, location, start_time, end_time)
|
|
12
|
+
@name=name
|
|
13
|
+
@description = description
|
|
14
|
+
@location = location
|
|
15
|
+
@start_time = start_time
|
|
16
|
+
@end_time = end_time
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
attr_accessor :error_message, :name, :items
|
|
21
|
+
|
|
22
|
+
def initialize
|
|
23
|
+
self.items = []
|
|
24
|
+
self.name = ""
|
|
25
|
+
self.error_message = ""
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def error?
|
|
29
|
+
!self.error_message.empty?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def add_item(name, description, location, start_time, end_time)
|
|
33
|
+
self.items << CalendarResultItem.new(name, description, location, start_time, end_time)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
DISPLAY_NAME = 'Calendar'
|
|
38
|
+
DISPLAY_FORMATS = {
|
|
39
|
+
"List (Multiple)" => "headlines",
|
|
40
|
+
"Detailed (Single)" => "detailed"
|
|
41
|
+
}
|
|
42
|
+
CALENDAR_SOURCES = { # exclude RSS and ATOM since cant get individual fields
|
|
43
|
+
"Google" => "google",
|
|
44
|
+
"iCal" => "ical",
|
|
45
|
+
# "Bedework JSON" => "bedeworkjson"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
def set_defaults
|
|
49
|
+
self.config['calendar_source'] ||= 'ical'
|
|
50
|
+
self.config['day_format'] ||= '%A %b %e'
|
|
51
|
+
self.config['time_format'] ||= '%l:%M %P'
|
|
52
|
+
self.config['max_results'] ||= 10
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def build_content
|
|
56
|
+
contents = []
|
|
57
|
+
result = fetch_calendar
|
|
58
|
+
|
|
59
|
+
if result.error?
|
|
60
|
+
raise result.error_message
|
|
61
|
+
else
|
|
62
|
+
day_format = self.config['day_format']
|
|
63
|
+
time_format = self.config['time_format']
|
|
64
|
+
|
|
65
|
+
case self.config['output_format']
|
|
66
|
+
when 'headlines' # 5 items per entry, titles only
|
|
67
|
+
result.items.each_slice(5).with_index do |items, index|
|
|
68
|
+
htmltext = HtmlText.new()
|
|
69
|
+
htmltext.name = "#{result.name} (#{index+1})"
|
|
70
|
+
htmltext.data = "<h1>#{result.name}</h1>#{items_to_html(items, day_format, time_format)}"
|
|
71
|
+
contents << htmltext
|
|
72
|
+
end
|
|
73
|
+
when 'detailed' # each item is a separate entry, title and description
|
|
74
|
+
result.items.each_with_index do |item, index|
|
|
75
|
+
htmltext = HtmlText.new()
|
|
76
|
+
htmltext.name = "#{result.name} (#{index+1})"
|
|
77
|
+
htmltext.data = item_to_html(item, day_format, time_format)
|
|
78
|
+
contents << htmltext
|
|
79
|
+
end
|
|
80
|
+
else
|
|
81
|
+
raise ArgumentError, 'Unexpected output format for Calendar feed.'
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
return contents
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def fetch_calendar
|
|
89
|
+
result = CalendarResults.new
|
|
90
|
+
client_key = self.config['api_key']
|
|
91
|
+
calendar_id = self.config['calendar_id']
|
|
92
|
+
calendar_source = self.config['calendar_source']
|
|
93
|
+
start_date = self.config['start_date'].strip.empty? ? Clock.time.beginning_of_day.iso8601 : self.config['start_date'].to_time.beginning_of_day.iso8601
|
|
94
|
+
end_date = self.config['end_date'].strip.empty? ? (start_date.to_time.beginning_of_day + self.config['days_ahead'].to_i.days).end_of_day.iso8601 : self.config['end_date'].to_time.beginning_of_day.iso8601
|
|
95
|
+
|
|
96
|
+
case calendar_source
|
|
97
|
+
when 'google'
|
|
98
|
+
if !client_key.empty?
|
|
99
|
+
# ---------------------------------- google calendar api v3 via client api
|
|
100
|
+
require 'google/api_client'
|
|
101
|
+
|
|
102
|
+
client = Google::APIClient.new
|
|
103
|
+
client.authorization = nil
|
|
104
|
+
client.key = client_key
|
|
105
|
+
|
|
106
|
+
cal = client.discovered_api('calendar', 'v3')
|
|
107
|
+
|
|
108
|
+
params = {}
|
|
109
|
+
params['calendarId'] = calendar_id
|
|
110
|
+
params['maxResults'] = self.config['max_results'] if !params['max_results'].blank?
|
|
111
|
+
params['singleEvents'] = true
|
|
112
|
+
params['orderBy'] = 'startTime'
|
|
113
|
+
params['fields'] = "description,items(description,end,endTimeUnspecified,location,organizer/displayName,source/title,start,status,summary,updated),summary,timeZone,updated"
|
|
114
|
+
params['timeMin'] = start_date
|
|
115
|
+
params['timeMax'] = end_date
|
|
116
|
+
|
|
117
|
+
tmp = client.execute(:api_method => cal.events.list, :parameters => params)
|
|
118
|
+
|
|
119
|
+
# convert to common data structure
|
|
120
|
+
result.error_message = tmp.error_message if tmp.error?
|
|
121
|
+
if !result.error?
|
|
122
|
+
result.name = tmp.data.summary
|
|
123
|
+
tmp.data.items.each do |item|
|
|
124
|
+
result.add_item(item.summary, item.description, item.location, item.start.dateTime, item.end.dateTime)
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
else
|
|
128
|
+
# ---------------------------------- public calendar via plain http
|
|
129
|
+
require 'net/http'
|
|
130
|
+
url = "http://www.google.com/calendar/feeds/#{calendar_id}/public/full?alt=json"
|
|
131
|
+
params = {}
|
|
132
|
+
params['max-results'] = self.config['max_results'] if !params['max_results'].blank?
|
|
133
|
+
params['singleevents'] = true
|
|
134
|
+
params['orderby'] = 'starttime'
|
|
135
|
+
params['start-min'] = start_date
|
|
136
|
+
params['start-max'] = end_date
|
|
137
|
+
url += params.collect { |k,v| "&#{k}=#{v}" }.join()
|
|
138
|
+
|
|
139
|
+
tmp = nil
|
|
140
|
+
begin
|
|
141
|
+
json_data = Net::HTTP.get_response(URI.parse(url)).body
|
|
142
|
+
tmp = JSON.load(json_data)
|
|
143
|
+
rescue => e
|
|
144
|
+
result.error_message = e.message
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# convert to common data structure
|
|
148
|
+
if !result.error?
|
|
149
|
+
result.name = tmp["feed"]["title"]["$t"]
|
|
150
|
+
tmp["feed"]["entry"].each do |item|
|
|
151
|
+
location = item["gd$where"].first["valueString"]
|
|
152
|
+
start_time = item["gd$when"].first["startTime"].to_time
|
|
153
|
+
end_time = item["gd$when"].first["endTime"].to_time
|
|
154
|
+
result.add_item(item["title"]["$t"], item["content"]["$t"], location, start_time, end_time)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
when 'ical'
|
|
159
|
+
# ---------------------------------- iCal calendar
|
|
160
|
+
# need to filter manually below because the url may not accommodate filtering
|
|
161
|
+
# so respect self.config[max_results] and start_date and end_date (which incorporates the days ahead)
|
|
162
|
+
require 'open-uri'
|
|
163
|
+
require 'icalendar'
|
|
164
|
+
|
|
165
|
+
begin
|
|
166
|
+
url = self.config['calendar_url']
|
|
167
|
+
calendars = nil
|
|
168
|
+
open(URI.parse(url)) do |cal|
|
|
169
|
+
calendars = Icalendar.parse(cal)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
max_results = self.config['max_results'].to_i
|
|
173
|
+
result.name = self.name # iCal doesn't provide a calendar name, so use the user's provided name
|
|
174
|
+
calendars.first.events.each do |item|
|
|
175
|
+
title = item.summary
|
|
176
|
+
description = item.description
|
|
177
|
+
location = item.location
|
|
178
|
+
item_start_time = item.dtstart.to_time unless item.dtstart.nil?
|
|
179
|
+
item_end_time = item.dtend.to_time unless item.dtend.nil?
|
|
180
|
+
# make sure the item's start date is within the specified range
|
|
181
|
+
if item_start_time >= start_date && item_start_time < end_date
|
|
182
|
+
result.add_item(title, description, location, item_start_time, item_end_time)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
result.items.sort! { |a, b| a.start_time <=> b.start_time }
|
|
186
|
+
result.items = result.items[0..max_results]
|
|
187
|
+
rescue => e
|
|
188
|
+
result.error_message = e.message
|
|
189
|
+
end
|
|
190
|
+
else
|
|
191
|
+
result.error_message = "unsupported calendar source #{calendar_source}"
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
return result
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def item_to_html(item, day_format, time_format)
|
|
198
|
+
start_time = item.start_time.strftime(time_format)
|
|
199
|
+
end_time = item.end_time.strftime(time_format) unless item.end_time.nil?
|
|
200
|
+
|
|
201
|
+
html = []
|
|
202
|
+
html << "<h1>#{item.name}</h1>"
|
|
203
|
+
html << "<h2>#{item.start_time.strftime(day_format)}</h2>"
|
|
204
|
+
html << (end_time.nil? ? "<div class=\"cal-time\">#{start_time}</div>" : "<div class=\"cal-time\">#{start_time} - #{end_time}</div>") unless start_time == end_time
|
|
205
|
+
html << "<div class=\"cal-location\">#{item.location}</div>"
|
|
206
|
+
html << "<p>#{item.description}</p>"
|
|
207
|
+
return html.join("")
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# display date (only when it changes) / times with title...
|
|
211
|
+
def items_to_html(items, day_format, time_format)
|
|
212
|
+
html = []
|
|
213
|
+
last_date = nil
|
|
214
|
+
items.each do |item|
|
|
215
|
+
# see if we need a date header
|
|
216
|
+
if last_date != item.start_time.to_date
|
|
217
|
+
if last_date.nil?
|
|
218
|
+
# dont need to close list
|
|
219
|
+
else
|
|
220
|
+
html << "</dl>"
|
|
221
|
+
end
|
|
222
|
+
html << "<h2>#{item.start_time.strftime(day_format)}</h2>"
|
|
223
|
+
html << "<dl>"
|
|
224
|
+
end
|
|
225
|
+
# todo: end time should include date if different
|
|
226
|
+
start_time = item.start_time.strftime(time_format)
|
|
227
|
+
end_time = item.end_time.strftime(time_format) unless item.end_time.nil?
|
|
228
|
+
|
|
229
|
+
html << (end_time.nil? ? "<dt>#{start_time}</dt>" : "<dt>#{start_time} - #{end_time}</dt>") unless start_time == end_time
|
|
230
|
+
html << "<dd>#{item.name}</dd>"
|
|
231
|
+
last_date = item.start_time.to_date
|
|
232
|
+
end
|
|
233
|
+
html << "</dl>" if !last_date.nil?
|
|
234
|
+
return html.join("")
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# calendar api parameters and preferred view (output_format)
|
|
238
|
+
def self.form_attributes
|
|
239
|
+
attributes = super()
|
|
240
|
+
attributes.concat([:config => [
|
|
241
|
+
:calendar_source, # google or ical (or bedework JSON eventually)
|
|
242
|
+
:api_key, # google
|
|
243
|
+
:calendar_id, # google
|
|
244
|
+
:calendar_url, # iCal url (specify parms in url manually)
|
|
245
|
+
:max_results,
|
|
246
|
+
:days_ahead,
|
|
247
|
+
:start_date,
|
|
248
|
+
:end_date,
|
|
249
|
+
:output_format, # all cals
|
|
250
|
+
:day_format, # all cals
|
|
251
|
+
:time_format # all cals
|
|
252
|
+
]])
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def validate_config
|
|
256
|
+
# if self.config['api_key'].blank?
|
|
257
|
+
# errors.add(:config_api_key, "can't be blank")
|
|
258
|
+
# end
|
|
259
|
+
|
|
260
|
+
prerequisites_met = true
|
|
261
|
+
if self.config['calendar_id'].blank? && self.config['calendar_source'] == "google"
|
|
262
|
+
errors.add(:config_calendar_id, "can't be blank")
|
|
263
|
+
prerequisites_met = false
|
|
264
|
+
end
|
|
265
|
+
if self.config['calendar_url'].blank? && self.config['calendar_source'] != "google"
|
|
266
|
+
errors.add(:config_calendar_url, "can't be blank")
|
|
267
|
+
prerequisites_met = false
|
|
268
|
+
end
|
|
269
|
+
if self.config['max_results'].blank?
|
|
270
|
+
errors.add(:config_max_results, "can't be blank")
|
|
271
|
+
end
|
|
272
|
+
# if self.config['days_ahead'].blank? && self.config['end_date'].blank?
|
|
273
|
+
# errors.add(:config_days_ahead, "days ahead or end_date must be specified")
|
|
274
|
+
# end
|
|
275
|
+
if !self.config['start_date'].blank? && !self.config['end_date'].blank?
|
|
276
|
+
start_date = self.config['start_date'].to_date
|
|
277
|
+
end_date = self.config['end_date'].to_date
|
|
278
|
+
if start_date > end_date
|
|
279
|
+
errors.add(:config_start_date, "must precede end date")
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
if !CALENDAR_SOURCES.values.include?(self.config['calendar_source'])
|
|
283
|
+
errors.add(:config_calendar_source, "must be #{CALENDAR_SOURCES.keys.join(' or ')}")
|
|
284
|
+
end
|
|
285
|
+
if !DISPLAY_FORMATS.values.include?(self.config['output_format'])
|
|
286
|
+
errors.add(:config_output_format, "must be #{DISPLAY_FORMATS.keys.join(' or ')}")
|
|
287
|
+
end
|
|
288
|
+
# todo: validate strftime components in day_format and time_format?
|
|
289
|
+
|
|
290
|
+
begin
|
|
291
|
+
validate_request #if !self.config['api_key'].blank?
|
|
292
|
+
rescue => e
|
|
293
|
+
errors.add(:base, "Could not fetch calendar - #{e.message}")
|
|
294
|
+
end if prerequisites_met
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
# make sure the request is valid by fetching a result back
|
|
298
|
+
def validate_request
|
|
299
|
+
result = fetch_calendar
|
|
300
|
+
|
|
301
|
+
if result.error?
|
|
302
|
+
raise result.error_message
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<%= javascript_include_tag "concerto_calendar/application" %>
|
|
2
|
+
<%= stylesheet_link_tag "concerto_calendar/application" %>
|
|
3
|
+
|
|
4
|
+
<%= form.fields_for :config do |config| %>
|
|
5
|
+
<fieldset>
|
|
6
|
+
<legend><span>Calendar</span></legend>
|
|
7
|
+
<div class="clearfix">
|
|
8
|
+
<%= config.label :calendar_source %>
|
|
9
|
+
<div class="input">
|
|
10
|
+
<%= config.select :calendar_source, Calendar::CALENDAR_SOURCES, :selected => @content.config['calendar_source'] %>
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<!-- google specific -->
|
|
15
|
+
<div class="clearfix">
|
|
16
|
+
<%= config.label :api_key %>
|
|
17
|
+
<div class="input">
|
|
18
|
+
<%= config.text_field :api_key, :placeholder => 'your secret API key from Google API Console', :class => "input-xxlarge", :value => @content.config['api_key'] %>
|
|
19
|
+
</div>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div class="clearfix">
|
|
23
|
+
<%= config.label :calendar_id %>
|
|
24
|
+
<div class="input">
|
|
25
|
+
<%= config.text_field :calendar_id, :placeholder => 'office@soldotnabiblechapel.com', :class => "input-xlarge", :value => @content.config['calendar_id'] %>
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
<!-- end of google specific -->
|
|
29
|
+
|
|
30
|
+
<!-- iCal specific -->
|
|
31
|
+
<div class="clearfix">
|
|
32
|
+
<%= config.label :calendar_url %>
|
|
33
|
+
<div class="input">
|
|
34
|
+
<%= config.text_field :calendar_url, :placeholder => 'http://events.rpi.edu/webcache/v1.0/icsDays/7/no--filter.ics', :class => "input-xxlarge", :value => @content.config['calendar_url'] %>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
<!-- end of iCal specific -->
|
|
38
|
+
</fieldset>
|
|
39
|
+
|
|
40
|
+
<fieldset>
|
|
41
|
+
<legend><span>Filter and Format</span></legend>
|
|
42
|
+
<div class="clearfix">
|
|
43
|
+
<%= config.label :max_results %>
|
|
44
|
+
<div class="input">
|
|
45
|
+
<%= config.number_field :max_results, :class => "input-small", :value => @content.config['max_results'] %>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div class="clearfix">
|
|
50
|
+
<%= config.label :days_ahead %>
|
|
51
|
+
<div class="input">
|
|
52
|
+
<%= config.number_field :days_ahead, :class => "input-small", :value => @content.config['days_ahead'] %>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<div class="clearfix">
|
|
57
|
+
<%= config.label :start_date %>
|
|
58
|
+
<div class="input">
|
|
59
|
+
<%= config.text_field :start_date, :class => "input-small datefield", :value => @content.config['start_date'] %>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div class="clearfix">
|
|
64
|
+
<%= config.label :end_date %>
|
|
65
|
+
<div class="input">
|
|
66
|
+
<%= config.text_field :end_date, :class => "input-small datefield", :value => @content.config['end_date'] %>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
<div class="clearfix">
|
|
71
|
+
<%= config.label :day_format %>
|
|
72
|
+
<div class="input">
|
|
73
|
+
<%= config.text_field :day_format, :placeholder => '%A %b %e', :class => "input-medium", :value => @content.config['day_format'] %>
|
|
74
|
+
<div><i>use <a href="http://www.ruby-doc.org/core-2.0/Time.html#method-i-strftime" target="_blank">strftime format</a> specifiers</i></div>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
<div class="clearfix">
|
|
79
|
+
<%= config.label :time_format %>
|
|
80
|
+
<div class="input">
|
|
81
|
+
<%= config.text_field :time_format, :placeholder => '%l:%M %P', :class => "input-medium", :value => @content.config['time_format'] %>
|
|
82
|
+
<div><i>use <a href="http://www.ruby-doc.org/core-2.0/Time.html#method-i-strftime" target="_blank">strftime format</a> specifiers</i></div>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<div class="clearfix">
|
|
87
|
+
<%= config.label :output_format, 'Display Format' %>
|
|
88
|
+
<div class="input">
|
|
89
|
+
<%= config.select :output_format, Calendar::DISPLAY_FORMATS, :selected => @content.config['output_format'] %>
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
</fieldset>
|
|
93
|
+
<% end %>
|