radiant-event_calendar-extension 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitmodules +0 -0
- data/README.md +198 -0
- data/Rakefile +139 -0
- data/VERSION +1 -0
- data/app/controllers/admin/calendars_controller.rb +18 -0
- data/app/controllers/admin/event_venues_controller.rb +3 -0
- data/app/controllers/admin/events_controller.rb +9 -0
- data/app/controllers/admin/icals_controller.rb +26 -0
- data/app/controllers/events_controller.rb +201 -0
- data/app/models/calendar.rb +57 -0
- data/app/models/event.rb +409 -0
- data/app/models/event_calendar_page.rb +138 -0
- data/app/models/event_occurrence.rb +89 -0
- data/app/models/event_recurrence_rule.rb +85 -0
- data/app/models/event_venue.rb +17 -0
- data/app/models/ical.rb +132 -0
- data/app/views/admin/calendars/_actions.html.haml +6 -0
- data/app/views/admin/calendars/_form.html.haml +56 -0
- data/app/views/admin/calendars/edit.html.haml +11 -0
- data/app/views/admin/calendars/help.html.erb +22 -0
- data/app/views/admin/calendars/index.html.haml +60 -0
- data/app/views/admin/calendars/new.html.haml +10 -0
- data/app/views/admin/calendars/show.html.haml +36 -0
- data/app/views/admin/event_venues/_event_venue.html.haml +18 -0
- data/app/views/admin/event_venues/_form.html.haml +44 -0
- data/app/views/admin/event_venues/edit.html.haml +32 -0
- data/app/views/admin/event_venues/index.html.haml +33 -0
- data/app/views/admin/event_venues/new.html.haml +32 -0
- data/app/views/admin/event_venues/remove.html.haml +17 -0
- data/app/views/admin/events/_event.html.haml +47 -0
- data/app/views/admin/events/_form.html.haml +116 -0
- data/app/views/admin/events/_list_head.html.haml +18 -0
- data/app/views/admin/events/edit.html.haml +6 -0
- data/app/views/admin/events/index.html.haml +23 -0
- data/app/views/admin/events/new.html.haml +6 -0
- data/app/views/admin/events/remove.html.haml +17 -0
- data/app/views/admin/icals/refresh.html.haml +11 -0
- data/app/views/admin/icals/refresh_all.html.haml +0 -0
- data/app/views/events/_defacet.html.haml +2 -0
- data/app/views/events/_event.html.haml +29 -0
- data/app/views/events/_event_postscript.html.haml +0 -0
- data/app/views/events/_faceting.html.haml +27 -0
- data/app/views/events/_minicalendar.html.haml +49 -0
- data/app/views/events/_other_page_parts.html.haml +0 -0
- data/app/views/events/_views.html.haml +6 -0
- data/app/views/events/index.html.haml +56 -0
- data/app/views/events/index.ics.erb +7 -0
- data/app/views/events/index.rss.builder +20 -0
- data/config/routes.rb +13 -0
- data/db/migrate/001_create_calendar_and_events.rb +21 -0
- data/db/migrate/002_calendar_add_ical_url.rb +9 -0
- data/db/migrate/003_add_calendar_category.rb +9 -0
- data/db/migrate/004_add_slug.rb +9 -0
- data/db/migrate/005_add_subscription_refresh_history.rb +11 -0
- data/db/migrate/006_create_icals.rb +13 -0
- data/db/migrate/007_move_subscriptions_to_ical.rb +15 -0
- data/db/migrate/008_clean_out_calendar.rb +12 -0
- data/db/migrate/009_basic_authentication.rb +12 -0
- data/db/migrate/010_refresh_interval.rb +9 -0
- data/db/migrate/011_more_properties.rb +8 -0
- data/db/migrate/20090818133511_simpler_ical_columns.rb +17 -0
- data/db/migrate/20090819130919_ownership.rb +17 -0
- data/db/migrate/20090820073805_site_scope.rb +13 -0
- data/db/migrate/20091118100725_event_status.rb +9 -0
- data/db/migrate/20100216080944_more_event_data.rb +31 -0
- data/db/migrate/20100218131410_recurrence_parts.rb +19 -0
- data/db/migrate/20100219102227_venues_and_categories.rb +23 -0
- data/db/migrate/20100221180539_recurrence_rules.rb +34 -0
- data/db/migrate/20100222182112_occurrences.rb +13 -0
- data/event_calendar_extension.rb +32 -0
- data/lib/calendar_period.rb +151 -0
- data/lib/event_calendar_admin_ui.rb +78 -0
- data/lib/event_calendar_tags.rb +1021 -0
- data/lib/event_search.rb +22 -0
- data/lib/event_statuses.rb +24 -0
- data/lib/tasks/event_calendar_extension_tasks.rake +29 -0
- data/pkg/radiant-event_calendar-extension-1.0.0.gem +0 -0
- data/public/icals/blank +0 -0
- data/public/images/admin/calendar.png +0 -0
- data/public/images/event_calendar/calendarlinkbg.png +0 -0
- data/public/images/event_calendar/event_shadow.png +0 -0
- data/public/images/event_calendar/ical16.png +0 -0
- data/public/images/event_calendar/maplinkbg.png +0 -0
- data/public/images/event_calendar/one_event.png +0 -0
- data/public/images/event_calendar/several_events.png +0 -0
- data/public/javascripts/admin/event_calendar.js +240 -0
- data/public/stylesheets/sass/admin/event_calendar.sass +266 -0
- data/public/stylesheets/sass/admin/modules/_grid.sass +56 -0
- data/public/stylesheets/sass/constants.sass +80 -0
- data/public/stylesheets/sass/event_calendar.sass +203 -0
- data/radiant-event_calendar-extension.gemspec +166 -0
- data/spec/datasets/calendar_events_dataset.rb +43 -0
- data/spec/datasets/calendar_pages_dataset.rb +8 -0
- data/spec/datasets/calendar_sites_dataset.rb +6 -0
- data/spec/datasets/calendars_dataset.rb +34 -0
- data/spec/datasets/recurrence_dataset.rb +7 -0
- data/spec/files/dummy.ics +59 -0
- data/spec/files/ny.ics +36 -0
- data/spec/lib/event_calendar_page_spec.rb +24 -0
- data/spec/models/calendar_spec.rb +11 -0
- data/spec/models/event_spec.rb +98 -0
- data/spec/models/ical_spec.rb +63 -0
- data/spec/models/recurrence_rule_spec.rb +82 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +36 -0
- metadata +238 -0
data/.gitmodules
ADDED
File without changes
|
data/README.md
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
# Event Calendar Extension for Radiant
|
2
|
+
|
3
|
+
This extension lets your radiant site present calendar events in various useful ways. The events can be administered directly or retrieved by subscription to ical and caldav services including Google Calendar, and can be served as RSS, Ical or JSON feeds as well as through a broad set of radius tags on your normal pages. This extension supports a wide range of uses from a basic display of forthcoming events through to a full calendar aggregation and mapping service.
|
4
|
+
|
5
|
+
The calendaring functionality comes from [ri_cal](http://github.com/rubyredrick/ri_cal) and supports proper recurrence and duration. We also recognise all-day events and pass through notes and urls: the ical subscription and redistribution should be fully RFC2445 compliant. It is not yet a full CalDAV client, and we don't have proper support for principals, groups or availability.
|
6
|
+
|
7
|
+
See the [event_map](http://github.com/spanner/radiant-event_map-extension) extension for googlemapping of events and [taggable_events](http://github.com/spanner/radiant-taggable_events-extension) for more fine-grained tagging and retrieval options. A `reader_events` extension is also in the works for reader-submitted calendar events, but [reader](http://github.com/spanner/radiant-reader-extension) will need to be updated for 0.9 first.
|
8
|
+
|
9
|
+
## Requirements
|
10
|
+
|
11
|
+
Radiant 0.9, share_layouts and the `ri_cal` gem to handle iCal data. It's declared in the extension so this should do it:
|
12
|
+
|
13
|
+
sudo rake gems:install
|
14
|
+
|
15
|
+
This is compatible with `multi_site` and with the [sites](http://github.com/spanner/radiant-event_map-extension "spanner's radiant-event_map-extension at master - GitHub") extension. With the latter everything will be site-scoped.
|
16
|
+
|
17
|
+
There is a 0.81 tag in the repository for the last version good with radiant 0.8.1 and `scoped_admin`.
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
|
21
|
+
Should be straightforward:
|
22
|
+
|
23
|
+
script/extension install event_calendar
|
24
|
+
|
25
|
+
## Configuration
|
26
|
+
|
27
|
+
There are a few optional config settings:
|
28
|
+
|
29
|
+
* `event_calendar.icals_path` is the directory (under /public/) holding the calendar subscription files. Default is `icals`.
|
30
|
+
* `event_calendar.default_refresh_interval` is the period, in seconds, after which the calendar subscriptions are refreshed. Default is one hour. Set to zero to refresh only in the admin interface.
|
31
|
+
* `event_calendar.layout` is the name of the layout that EventsController will use (see below)
|
32
|
+
* `event_calendar.filename_prefix` is an optional prefix for ics filenames
|
33
|
+
* `event_calendar.cached?` determines whether the EventsController pages are cached by Rack::Cache. EventCalendarPages are always cached like other pages.
|
34
|
+
* `event_calendar.cache_duration` determines for how long.
|
35
|
+
|
36
|
+
Each calendar subscription will have its own address and authentication settings.
|
37
|
+
|
38
|
+
## Usage
|
39
|
+
|
40
|
+
### Subscribing to a calendar
|
41
|
+
|
42
|
+
1. Create a calendar source. You can do that by publishing a feed from your desktop calendar application, by making a google calendar public or by setting up a CalDAV calendar and persuading all the right people to subscribe to it.
|
43
|
+
2. Find the ical subscription address of your calendar.
|
44
|
+
3. Choose 'new calendar' in the radiant admin menu and enter the address and any authentication information you need to get at it. See below for notes about connecting to CalDAV. In the case of an ical file or google calendar you should only need an address. Give the calendar a slug, just as you would for a page, and optionally a category. Let's say you call it 'test'.
|
45
|
+
4. Your calendar should appear in the subscription list. Click through to browse its events and make sure everything is as it should be.
|
46
|
+
|
47
|
+
### Adding events manually
|
48
|
+
|
49
|
+
Should be obvious. There are a few points to remember:
|
50
|
+
|
51
|
+
* Event venues are expected to be reused, so they present as a list with the option to add a new one.
|
52
|
+
* The postcode field is a convenience for geocoding purposes. You can leave it blank unless you're mapping and your locations are a bit odd.
|
53
|
+
* Recurrence is for the repetition of identical separate events. A single event that spans several days only needs to have the right start and end times.
|
54
|
+
* End times are optional: an event with just a start time is just listed where it begins.
|
55
|
+
|
56
|
+
### Displaying events with the EventsController
|
57
|
+
|
58
|
+
The events controller uses `share_layouts` to define various page parts that your layout can bring in. To use it, create a layout with any or all of these parts:
|
59
|
+
|
60
|
+
* `title` is the page title and can also be shown with `r:title`
|
61
|
+
* `events` is a formatted list of events with date stamps and descriptions
|
62
|
+
* `continuing_events` is a compact list of events that have already begun but which continue into the period being shown
|
63
|
+
* `calendar` is a usual calendar block with links to months and days. Days without events are not linked.
|
64
|
+
* `pagination` is the usual will_paginate block.
|
65
|
+
* `faceting` here only gives the option to remove any date filters that have been applied. If you add the `taggable_events` extension it gets more useful.
|
66
|
+
|
67
|
+
You will find minimal styling of some of these parts in `/stylesheets/sass/calendar.sass`,
|
68
|
+
|
69
|
+
Set the config entry `event_calendar.layout` to the name of your layout and point a browser at /calendar to see what you've got.
|
70
|
+
|
71
|
+
Here's a basic sample layout that should just work:
|
72
|
+
|
73
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
74
|
+
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
75
|
+
|
76
|
+
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
|
77
|
+
<head>
|
78
|
+
<title><r:title /></title>
|
79
|
+
<link rel="stylesheet" href="/stylesheets/event_calendar.css" />
|
80
|
+
</head>
|
81
|
+
<body>
|
82
|
+
<h1 id="pagetitle"><r:title /></h1>
|
83
|
+
<r:content part="faceting" />
|
84
|
+
<r:content part="calendar" />
|
85
|
+
<r:content part="events" />
|
86
|
+
<r:content part="continuing_events" />
|
87
|
+
<r:content part="pagination" />
|
88
|
+
</body>
|
89
|
+
</html>
|
90
|
+
|
91
|
+
One quirk that might go away: at the moment if there are few new events then the continuing events are moved into the events page part. Most layouts work better that way.
|
92
|
+
|
93
|
+
### Displaying events with an EventCalendar page
|
94
|
+
|
95
|
+
Set up a new page at /events/ with the type 'EventCalendar'. To show a pageable calendar view of the current month, all you need is this:
|
96
|
+
|
97
|
+
<r:events:as_calendar month="now" month_links="true" />
|
98
|
+
|
99
|
+
Or to show a list of all events in the next six months:
|
100
|
+
|
101
|
+
<p><r:calendars:summary /></p>
|
102
|
+
|
103
|
+
<div class="event_list">
|
104
|
+
<r:events:each calendar_months="6">
|
105
|
+
<r:event:header name="date">
|
106
|
+
<h2 id="<r:event:year />_<r:event:month />"><r:event:month /> <r:event:year /></h2>
|
107
|
+
</r:event:header>
|
108
|
+
<p id="event_<r:event:id />">
|
109
|
+
<acronym class="date">
|
110
|
+
<r:event:day_ordinal />
|
111
|
+
</acronym>
|
112
|
+
<r:event:link class="title" />
|
113
|
+
<r:event:if_location>
|
114
|
+
<span class="location"><r:event:location /></span>
|
115
|
+
</r:event:if_location>
|
116
|
+
<r:event:if_description>
|
117
|
+
<br />
|
118
|
+
<span class="description"><r:event:description /></span>
|
119
|
+
</r:event:if_description>
|
120
|
+
</p>
|
121
|
+
</r:events:each>
|
122
|
+
</div>
|
123
|
+
|
124
|
+
Note that the `event:header` tag only shows when it changes, which in this case gives you a non-repeating date slip. For more about the available radius tags, see the extension wiki or the 'available tags' documentation.
|
125
|
+
|
126
|
+
If you have another column in your layout, try adding this:
|
127
|
+
|
128
|
+
<r:events:as_calendar calendar_months="6" date_links="true" compact="true" />
|
129
|
+
|
130
|
+
For clickable thumbnails of coming months.
|
131
|
+
|
132
|
+
## Notes
|
133
|
+
|
134
|
+
This is developing quite quickly at the moment but it's in production use on one big and several small sites. If there are bugs they will be fixed immediately.
|
135
|
+
|
136
|
+
### Compatibility
|
137
|
+
|
138
|
+
I've tested this with Darwin Calendar Server (on Ubuntu), with Google Calendar and with feeds published from iCal on a mac. It should work just as well with iCal server on OS X Server, and in theory any other CalDav-compliant back end. See http://caldav.calconnect.org/ for more possibilities.
|
139
|
+
|
140
|
+
### Connecting to Google Calendar
|
141
|
+
|
142
|
+
Create a calendar in your Google Calendar account. Call it 'public', or whatever you like, and tick the box marked 'make this calendar public'.
|
143
|
+
|
144
|
+
Click on 'calendar settings' from the drop-down menu next to the name of the public calendar, and look towards the bottom for the 'Calendar Address' section. Click on 'ical' and the address that pops up is your subscription address. You shouldn't need anything else.
|
145
|
+
|
146
|
+
### Connecting to CalDAV
|
147
|
+
|
148
|
+
We aren't really doing CalDAV properly here, but taking advantage of its compatibility with the simpler ical standard. A simple GET to addresses under /calendar will return a file in ical format, which is what we get and parse. As a passive display client, that's all we need, but it does mean that so far we can't display groups properly, or interact with principals, or take proper advantage of the more collaborative functions of CalDAV.
|
149
|
+
|
150
|
+
The address for your calendar will either look like this:
|
151
|
+
|
152
|
+
https://[calendar.server.com]:8443/calendars/users/[someone]/calendar/
|
153
|
+
|
154
|
+
or like this:
|
155
|
+
|
156
|
+
https://[calendar.server.com]:8443/calendars/users/[someone]/1D09F977-2F27-4E87-954D-FFED95A70BC0/
|
157
|
+
|
158
|
+
but note that the port and protocol will depend on your server setup. The best way to find these addresses is to visit the calendar server in a normal web browser, log in as the authorised person, and cut and paste the address of the calendar you want. You may also be able to get the right address out of iCal or sunbird but some trial and error will be involved.
|
159
|
+
|
160
|
+
The best way to think of these addresses under /calendar/ is as the read-only version. The address that you will subscribe to in your desktop calendar application is the read-write version and will look slightly different:
|
161
|
+
|
162
|
+
https://[calendar.server.com]:8443/principals/users/[someone]/
|
163
|
+
|
164
|
+
is all you need to put into iCal, for example: it will handle subcalendars and GUIDs without bothering you for details.
|
165
|
+
|
166
|
+
### Setting up a CalDAV server
|
167
|
+
|
168
|
+
It's not nice. There are three main options:
|
169
|
+
|
170
|
+
* OS X Server has an excellent built-in ICal server
|
171
|
+
* [Darwin Calendar Server](https://trac.calendarserver.org/wiki) is the open-source version of that. It's written mostly in Python and distinctly quirky, but once set up it works very well.
|
172
|
+
* Use a Google calendar and endure a bit of sync bother to get it on the desktop.
|
173
|
+
* er
|
174
|
+
* That's it unless you speak Java, in which case there are [several](http://caldav.calconnect.org/implementations/servers.html) other [good options](http://www.bedework.org/bedework/).
|
175
|
+
|
176
|
+
See [http://caldav.calconnect.org/](http://caldav.calconnect.org/) for news and background information.
|
177
|
+
|
178
|
+
### Quirks
|
179
|
+
|
180
|
+
Calendars are only refreshed if they're accessed. The `event_calendar.default_refresh_interval` setting is really a cache duration: on the next request after that interval, we go back to the original source. If that gets too slow for the end user who triggers the refresh then I'll need to add a calendar-refresh rake task that can be crontabbed, but so far it seems to work well enough.
|
181
|
+
|
182
|
+
If you're administering your calendars in iCal, the first calendar you set up will be accessible at the simple /users/uid/calendar address but after that you'll have to get the GUIDs. You can get-info on a calendar to get a subscription address but if it's long it may be truncated.
|
183
|
+
|
184
|
+
OS X 10.6 promises to handle all this a lot better. On Windows you're pretty much confined to Sunbird at the moment. There is a project to [hook Outlook up](http://openconnector.org/) to CalDAV but it seems to have stalled.
|
185
|
+
|
186
|
+
## Bugs and features
|
187
|
+
|
188
|
+
[Github issues](http://github.com/radiant/radiant-event-calendar-extension/issues) please, or for little things an email or github message is fine.
|
189
|
+
|
190
|
+
## Author & Copyright
|
191
|
+
|
192
|
+
Originally created by Loren Johnson - see www.hellovenado.com - and then taken over by the radiant team. Currently maintained by Will at spanner.org.
|
193
|
+
|
194
|
+
Released under the same terms as Radiant and/or Rails.
|
195
|
+
|
196
|
+
|
197
|
+
|
198
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |gem|
|
4
|
+
gem.name = "radiant-event_calendar-extension"
|
5
|
+
gem.summary = %Q{Event Calendar Extension for Radiant CMS}
|
6
|
+
gem.description = %Q{An event calendar extension that administers events locally or draws them from any ical or CalDAV publishers (Google Calendar, .Mac, Darwin Calendar Server, etc.)}
|
7
|
+
gem.email = "will@spanner.org"
|
8
|
+
gem.homepage = "http://github.com/radiant/radiant-event_calendar-extension"
|
9
|
+
gem.authors = ["spanner"]
|
10
|
+
gem.add_dependency "radiant", ">= 0.9.0"
|
11
|
+
gem.add_dependency "ri_cal"
|
12
|
+
gem.add_dependency "chronic"
|
13
|
+
gem.add_dependency "uuidtools"
|
14
|
+
end
|
15
|
+
rescue LoadError
|
16
|
+
puts "Jeweler (or a dependency) not available. This is only required if you plan to package event_calendar as a gem."
|
17
|
+
end
|
18
|
+
|
19
|
+
# In rails 1.2, plugins aren't available in the path until they're loaded.
|
20
|
+
# Check to see if the rspec plugin is installed first and require
|
21
|
+
# it if it is. If not, use the gem version.
|
22
|
+
|
23
|
+
# Determine where the RSpec plugin is by loading the boot
|
24
|
+
unless defined? RADIANT_ROOT
|
25
|
+
ENV["RAILS_ENV"] = "test"
|
26
|
+
case
|
27
|
+
when ENV["RADIANT_ENV_FILE"]
|
28
|
+
require File.dirname(ENV["RADIANT_ENV_FILE"]) + "/boot"
|
29
|
+
when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
|
30
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot"
|
31
|
+
else
|
32
|
+
require "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
require 'rake'
|
37
|
+
require 'rake/rdoctask'
|
38
|
+
require 'rake/testtask'
|
39
|
+
|
40
|
+
rspec_base = File.expand_path(RADIANT_ROOT + '/vendor/plugins/rspec/lib')
|
41
|
+
$LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
|
42
|
+
require 'spec/rake/spectask'
|
43
|
+
require 'cucumber'
|
44
|
+
require 'cucumber/rake/task'
|
45
|
+
|
46
|
+
# Cleanup the RADIANT_ROOT constant so specs will load the environment
|
47
|
+
Object.send(:remove_const, :RADIANT_ROOT)
|
48
|
+
|
49
|
+
extension_root = File.expand_path(File.dirname(__FILE__))
|
50
|
+
|
51
|
+
task :default => :spec
|
52
|
+
task :stats => "spec:statsetup"
|
53
|
+
|
54
|
+
desc "Run all specs in spec directory"
|
55
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
56
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
57
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
58
|
+
end
|
59
|
+
|
60
|
+
task :features => 'spec:integration'
|
61
|
+
|
62
|
+
namespace :spec do
|
63
|
+
desc "Run all specs in spec directory with RCov"
|
64
|
+
Spec::Rake::SpecTask.new(:rcov) do |t|
|
65
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
66
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
67
|
+
t.rcov = true
|
68
|
+
t.rcov_opts = ['--exclude', 'spec', '--rails']
|
69
|
+
end
|
70
|
+
|
71
|
+
desc "Print Specdoc for all specs"
|
72
|
+
Spec::Rake::SpecTask.new(:doc) do |t|
|
73
|
+
t.spec_opts = ["--format", "specdoc", "--dry-run"]
|
74
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
75
|
+
end
|
76
|
+
|
77
|
+
[:models, :controllers, :views, :helpers].each do |sub|
|
78
|
+
desc "Run the specs under spec/#{sub}"
|
79
|
+
Spec::Rake::SpecTask.new(sub) do |t|
|
80
|
+
t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
|
81
|
+
t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
desc "Run the Cucumber features"
|
86
|
+
Cucumber::Rake::Task.new(:integration) do |t|
|
87
|
+
t.fork = true
|
88
|
+
t.cucumber_opts = ['--format', (ENV['CUCUMBER_FORMAT'] || 'pretty')]
|
89
|
+
# t.feature_pattern = "#{extension_root}/features/**/*.feature"
|
90
|
+
t.profile = "default"
|
91
|
+
end
|
92
|
+
|
93
|
+
# Setup specs for stats
|
94
|
+
task :statsetup do
|
95
|
+
require 'code_statistics'
|
96
|
+
::STATS_DIRECTORIES << %w(Model\ specs spec/models)
|
97
|
+
::STATS_DIRECTORIES << %w(View\ specs spec/views)
|
98
|
+
::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers)
|
99
|
+
::STATS_DIRECTORIES << %w(Helper\ specs spec/views)
|
100
|
+
::CodeStatistics::TEST_TYPES << "Model specs"
|
101
|
+
::CodeStatistics::TEST_TYPES << "View specs"
|
102
|
+
::CodeStatistics::TEST_TYPES << "Controller specs"
|
103
|
+
::CodeStatistics::TEST_TYPES << "Helper specs"
|
104
|
+
::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
|
105
|
+
end
|
106
|
+
|
107
|
+
namespace :db do
|
108
|
+
namespace :fixtures do
|
109
|
+
desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y"
|
110
|
+
task :load => :environment do
|
111
|
+
require 'active_record/fixtures'
|
112
|
+
ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
|
113
|
+
(ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
|
114
|
+
Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*'))
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
desc 'Generate documentation for the event_calendar extension.'
|
122
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
123
|
+
rdoc.rdoc_dir = 'rdoc'
|
124
|
+
rdoc.title = 'EventCalendarExtension'
|
125
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
126
|
+
rdoc.rdoc_files.include('README')
|
127
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
128
|
+
end
|
129
|
+
|
130
|
+
# For extensions that are in transition
|
131
|
+
desc 'Test the event_calendar extension.'
|
132
|
+
Rake::TestTask.new(:test) do |t|
|
133
|
+
t.libs << 'lib'
|
134
|
+
t.pattern = 'test/**/*_test.rb'
|
135
|
+
t.verbose = true
|
136
|
+
end
|
137
|
+
|
138
|
+
# Load any custom rakefiles for extension
|
139
|
+
Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Admin::CalendarsController < Admin::ResourceController
|
2
|
+
paginate_models
|
3
|
+
before_filter :check_refreshments, :only => [:index, :show]
|
4
|
+
|
5
|
+
def show
|
6
|
+
@calendar = Calendar.find(params[:id])
|
7
|
+
@year = params[:year] ? params[:year].to_i : Date.today.year
|
8
|
+
@month = params[:month] ? params[:month].to_i : Date.today.month
|
9
|
+
response_for :singular
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def check_refreshments
|
15
|
+
Ical.check_refreshments
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class Admin::IcalsController < Admin::ResourceController
|
2
|
+
|
3
|
+
def refresh_all
|
4
|
+
# This is the correct line for the agent to run.
|
5
|
+
# Calendar::refresh_all
|
6
|
+
|
7
|
+
# We'll keep using this so we can keep an eye on the downlaod status until an agent and proper error checking is implemented.
|
8
|
+
@icals = Ical.find(:all)
|
9
|
+
@icals.each do |ical|
|
10
|
+
ical.refresh
|
11
|
+
end
|
12
|
+
flash[:notice] = "iCal subscription refresh complete."
|
13
|
+
redirect_to admin_calendars_path
|
14
|
+
end
|
15
|
+
|
16
|
+
def refresh
|
17
|
+
ical = Ical.find(params[:id])
|
18
|
+
if ical.refresh
|
19
|
+
flash[:notice] = ical.calendar.name + " calendar refreshed from iCal subscription ."
|
20
|
+
else
|
21
|
+
flash[:notice] = "Error parsing " + ical.calendar.name + " calendar from iCal subscription."
|
22
|
+
end
|
23
|
+
redirect_to :back
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
class EventsController < SiteController
|
2
|
+
require "uri"
|
3
|
+
|
4
|
+
helper_method :events, :all_events, :continuing_events, :period, :calendars, :list_description
|
5
|
+
helper_method :url_for_date, :url_for_month, :url_without_period, :calendar_parameters, :month_name, :short_month_name, :day_names
|
6
|
+
before_filter :numerical_parameters
|
7
|
+
|
8
|
+
radiant_layout { |controller| controller.layout_for :event_calendar }
|
9
|
+
no_login_required
|
10
|
+
|
11
|
+
# delivers designated lists of events in minimal formats
|
12
|
+
|
13
|
+
def index
|
14
|
+
@seen_events = {}
|
15
|
+
respond_to do |format|
|
16
|
+
format.html {
|
17
|
+
if Radiant::Config['event_calendar:cached?']
|
18
|
+
timeout = Radiant::Config['event_calendar:cache_duration'] || self.class.cache_timeout
|
19
|
+
expires_in timeout.to_i, :public => true, :private => false
|
20
|
+
else
|
21
|
+
expires_now
|
22
|
+
end
|
23
|
+
}
|
24
|
+
format.js {
|
25
|
+
render :json => events.to_json
|
26
|
+
}
|
27
|
+
format.rss {
|
28
|
+
render :layout => false
|
29
|
+
}
|
30
|
+
format.ics {
|
31
|
+
headers["Content-disposition"] = %{attachment; filename="#{filename}.ics"}
|
32
|
+
render :layout => false
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def period
|
38
|
+
return @period if @period
|
39
|
+
this = Date.today
|
40
|
+
if params[:mday]
|
41
|
+
start = Date.civil(params[:year] || this.year, params[:month] || this.month, params[:mday])
|
42
|
+
@period = CalendarPeriod.between(start, start.to_datetime.end_of_day)
|
43
|
+
elsif params[:month]
|
44
|
+
start = Date.civil(params[:year] || this.year, params[:month])
|
45
|
+
@period = CalendarPeriod.between(start, start.to_datetime.end_of_month)
|
46
|
+
elsif params[:year]
|
47
|
+
start = Date.civil(params[:year])
|
48
|
+
@period = CalendarPeriod.between(start, start.to_datetime.end_of_year)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def calendars
|
53
|
+
return @calendars if @calendars
|
54
|
+
if params[:calendar_id]
|
55
|
+
@calendars = [Calendar.find(params[:calendar_id])]
|
56
|
+
elsif params[:slug]
|
57
|
+
@calendars = Calendar.with_slugs(params[:slug])
|
58
|
+
elsif params[:category]
|
59
|
+
@calendars = Calendar.in_category(params[:category])
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def events
|
64
|
+
@events ||= event_finder.paginate(pagination_parameters)
|
65
|
+
end
|
66
|
+
|
67
|
+
def all_events
|
68
|
+
@all_events ||= event_finder.all
|
69
|
+
end
|
70
|
+
|
71
|
+
def event_finder
|
72
|
+
ef = Event.scoped
|
73
|
+
if period
|
74
|
+
if period.bounded?
|
75
|
+
ef = ef.between(period.start, period.finish)
|
76
|
+
elsif period.start
|
77
|
+
ef = ef.after(period.start)
|
78
|
+
else
|
79
|
+
ef = ef.before(period.finish)
|
80
|
+
end
|
81
|
+
else
|
82
|
+
ef = ef.future
|
83
|
+
end
|
84
|
+
ef = ef.approved if Radiant::Config['event_calendar.require_approval']
|
85
|
+
ef = ef.in_calendars(calendars) if calendars
|
86
|
+
ef
|
87
|
+
end
|
88
|
+
|
89
|
+
def continuing_events
|
90
|
+
return @continuing_events if @continuing_events
|
91
|
+
if period && period.start
|
92
|
+
@continuing_events = Event.unfinished(period.start).by_end_date
|
93
|
+
else
|
94
|
+
@continuing_events = Event.unfinished(Time.now).by_end_date
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def list_description
|
99
|
+
return @description if @description
|
100
|
+
parts = []
|
101
|
+
parts << (period ? period.description : "coming up")
|
102
|
+
parts << "in #{calendars.to_sentence}" if calendars
|
103
|
+
@description = parts.join(' ')
|
104
|
+
end
|
105
|
+
|
106
|
+
def url_for_date(date)
|
107
|
+
url_for(url_parts({
|
108
|
+
:mday => date.mday,
|
109
|
+
:month => month_name(date.month).downcase,
|
110
|
+
:year => date.year
|
111
|
+
}))
|
112
|
+
end
|
113
|
+
|
114
|
+
def url_for_month(date)
|
115
|
+
url_for(url_parts({
|
116
|
+
:mday => nil,
|
117
|
+
:month => month_name(date.month).downcase,
|
118
|
+
:year => date.year
|
119
|
+
}))
|
120
|
+
end
|
121
|
+
|
122
|
+
def url_without_period
|
123
|
+
url_for(url_parts({
|
124
|
+
:mday => nil,
|
125
|
+
:month => nil,
|
126
|
+
:year => nil
|
127
|
+
}))
|
128
|
+
end
|
129
|
+
|
130
|
+
def query_string
|
131
|
+
url_parts.map{|p| "#{p}=params[p]" }.join("&")
|
132
|
+
end
|
133
|
+
|
134
|
+
def filename
|
135
|
+
url_parts.map{|p| params[p] }.join("_")
|
136
|
+
end
|
137
|
+
|
138
|
+
# this is broken down to provide chain points for other extensions that add more ways to filter
|
139
|
+
# eg, to start with, taggable_events
|
140
|
+
|
141
|
+
def calendar_parameters
|
142
|
+
url_parts
|
143
|
+
end
|
144
|
+
|
145
|
+
def url_parts(amendments={})
|
146
|
+
parts = params.slice(*calendar_parameter_names) # Hash#slice is defined in will_paginate/lib/core_ext
|
147
|
+
parts.merge(amendments)
|
148
|
+
end
|
149
|
+
|
150
|
+
def calendar_parameter_names
|
151
|
+
[:year, :month, :mday, :category, :slug, :calendar_id]
|
152
|
+
end
|
153
|
+
|
154
|
+
def month_name(month)
|
155
|
+
month_names[month]
|
156
|
+
end
|
157
|
+
|
158
|
+
def short_month_name(month)
|
159
|
+
short_month_names[month]
|
160
|
+
end
|
161
|
+
|
162
|
+
def day_names
|
163
|
+
return @day_names if @day_names
|
164
|
+
@day_names ||= Date::DAYNAMES.dup
|
165
|
+
@day_names.push(@day_names.shift) # Class::Date and ActiveSupport::CoreExtensions::Time::Calculations have different ideas of when is the start of the week. We've gone for the rails standard.
|
166
|
+
@day_names
|
167
|
+
end
|
168
|
+
|
169
|
+
protected
|
170
|
+
|
171
|
+
def short_month_names
|
172
|
+
@short_month_names ||= Date::ABBR_MONTHNAMES.dup
|
173
|
+
end
|
174
|
+
|
175
|
+
def month_names
|
176
|
+
@month_names ||= Date::MONTHNAMES.dup
|
177
|
+
end
|
178
|
+
|
179
|
+
# months can be passed around either as names or numbers
|
180
|
+
# any date part can be 'now' or 'next' for ease of linking
|
181
|
+
# and everything is converted to_i to save clutter later
|
182
|
+
|
183
|
+
def numerical_parameters
|
184
|
+
if params[:month] && month_names.include?(params[:month].titlecase)
|
185
|
+
params[:month] = month_names.index(params[:month].titlecase)
|
186
|
+
end
|
187
|
+
[:year, :month, :mday].select{|p| params[p] }.each do |p|
|
188
|
+
params[p] = Date.today.send(p) if params[p] == 'now'
|
189
|
+
params[p] = (Date.today + 1.send(p == :mday ? :day : p)).send(p) if params[p] == 'next'
|
190
|
+
params[p] = params[p].to_i
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def pagination_parameters
|
195
|
+
{
|
196
|
+
:page => (params[:p] || 1).to_i,
|
197
|
+
:per_page => (params[:pp] || Radiant::Config['event_calendar.per_page'] || 10).to_i
|
198
|
+
}
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|