radiant-event_calendar-extension 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. data/.gitmodules +0 -0
  2. data/README.md +198 -0
  3. data/Rakefile +139 -0
  4. data/VERSION +1 -0
  5. data/app/controllers/admin/calendars_controller.rb +18 -0
  6. data/app/controllers/admin/event_venues_controller.rb +3 -0
  7. data/app/controllers/admin/events_controller.rb +9 -0
  8. data/app/controllers/admin/icals_controller.rb +26 -0
  9. data/app/controllers/events_controller.rb +201 -0
  10. data/app/models/calendar.rb +57 -0
  11. data/app/models/event.rb +409 -0
  12. data/app/models/event_calendar_page.rb +138 -0
  13. data/app/models/event_occurrence.rb +89 -0
  14. data/app/models/event_recurrence_rule.rb +85 -0
  15. data/app/models/event_venue.rb +17 -0
  16. data/app/models/ical.rb +132 -0
  17. data/app/views/admin/calendars/_actions.html.haml +6 -0
  18. data/app/views/admin/calendars/_form.html.haml +56 -0
  19. data/app/views/admin/calendars/edit.html.haml +11 -0
  20. data/app/views/admin/calendars/help.html.erb +22 -0
  21. data/app/views/admin/calendars/index.html.haml +60 -0
  22. data/app/views/admin/calendars/new.html.haml +10 -0
  23. data/app/views/admin/calendars/show.html.haml +36 -0
  24. data/app/views/admin/event_venues/_event_venue.html.haml +18 -0
  25. data/app/views/admin/event_venues/_form.html.haml +44 -0
  26. data/app/views/admin/event_venues/edit.html.haml +32 -0
  27. data/app/views/admin/event_venues/index.html.haml +33 -0
  28. data/app/views/admin/event_venues/new.html.haml +32 -0
  29. data/app/views/admin/event_venues/remove.html.haml +17 -0
  30. data/app/views/admin/events/_event.html.haml +47 -0
  31. data/app/views/admin/events/_form.html.haml +116 -0
  32. data/app/views/admin/events/_list_head.html.haml +18 -0
  33. data/app/views/admin/events/edit.html.haml +6 -0
  34. data/app/views/admin/events/index.html.haml +23 -0
  35. data/app/views/admin/events/new.html.haml +6 -0
  36. data/app/views/admin/events/remove.html.haml +17 -0
  37. data/app/views/admin/icals/refresh.html.haml +11 -0
  38. data/app/views/admin/icals/refresh_all.html.haml +0 -0
  39. data/app/views/events/_defacet.html.haml +2 -0
  40. data/app/views/events/_event.html.haml +29 -0
  41. data/app/views/events/_event_postscript.html.haml +0 -0
  42. data/app/views/events/_faceting.html.haml +27 -0
  43. data/app/views/events/_minicalendar.html.haml +49 -0
  44. data/app/views/events/_other_page_parts.html.haml +0 -0
  45. data/app/views/events/_views.html.haml +6 -0
  46. data/app/views/events/index.html.haml +56 -0
  47. data/app/views/events/index.ics.erb +7 -0
  48. data/app/views/events/index.rss.builder +20 -0
  49. data/config/routes.rb +13 -0
  50. data/db/migrate/001_create_calendar_and_events.rb +21 -0
  51. data/db/migrate/002_calendar_add_ical_url.rb +9 -0
  52. data/db/migrate/003_add_calendar_category.rb +9 -0
  53. data/db/migrate/004_add_slug.rb +9 -0
  54. data/db/migrate/005_add_subscription_refresh_history.rb +11 -0
  55. data/db/migrate/006_create_icals.rb +13 -0
  56. data/db/migrate/007_move_subscriptions_to_ical.rb +15 -0
  57. data/db/migrate/008_clean_out_calendar.rb +12 -0
  58. data/db/migrate/009_basic_authentication.rb +12 -0
  59. data/db/migrate/010_refresh_interval.rb +9 -0
  60. data/db/migrate/011_more_properties.rb +8 -0
  61. data/db/migrate/20090818133511_simpler_ical_columns.rb +17 -0
  62. data/db/migrate/20090819130919_ownership.rb +17 -0
  63. data/db/migrate/20090820073805_site_scope.rb +13 -0
  64. data/db/migrate/20091118100725_event_status.rb +9 -0
  65. data/db/migrate/20100216080944_more_event_data.rb +31 -0
  66. data/db/migrate/20100218131410_recurrence_parts.rb +19 -0
  67. data/db/migrate/20100219102227_venues_and_categories.rb +23 -0
  68. data/db/migrate/20100221180539_recurrence_rules.rb +34 -0
  69. data/db/migrate/20100222182112_occurrences.rb +13 -0
  70. data/event_calendar_extension.rb +32 -0
  71. data/lib/calendar_period.rb +151 -0
  72. data/lib/event_calendar_admin_ui.rb +78 -0
  73. data/lib/event_calendar_tags.rb +1021 -0
  74. data/lib/event_search.rb +22 -0
  75. data/lib/event_statuses.rb +24 -0
  76. data/lib/tasks/event_calendar_extension_tasks.rake +29 -0
  77. data/pkg/radiant-event_calendar-extension-1.0.0.gem +0 -0
  78. data/public/icals/blank +0 -0
  79. data/public/images/admin/calendar.png +0 -0
  80. data/public/images/event_calendar/calendarlinkbg.png +0 -0
  81. data/public/images/event_calendar/event_shadow.png +0 -0
  82. data/public/images/event_calendar/ical16.png +0 -0
  83. data/public/images/event_calendar/maplinkbg.png +0 -0
  84. data/public/images/event_calendar/one_event.png +0 -0
  85. data/public/images/event_calendar/several_events.png +0 -0
  86. data/public/javascripts/admin/event_calendar.js +240 -0
  87. data/public/stylesheets/sass/admin/event_calendar.sass +266 -0
  88. data/public/stylesheets/sass/admin/modules/_grid.sass +56 -0
  89. data/public/stylesheets/sass/constants.sass +80 -0
  90. data/public/stylesheets/sass/event_calendar.sass +203 -0
  91. data/radiant-event_calendar-extension.gemspec +166 -0
  92. data/spec/datasets/calendar_events_dataset.rb +43 -0
  93. data/spec/datasets/calendar_pages_dataset.rb +8 -0
  94. data/spec/datasets/calendar_sites_dataset.rb +6 -0
  95. data/spec/datasets/calendars_dataset.rb +34 -0
  96. data/spec/datasets/recurrence_dataset.rb +7 -0
  97. data/spec/files/dummy.ics +59 -0
  98. data/spec/files/ny.ics +36 -0
  99. data/spec/lib/event_calendar_page_spec.rb +24 -0
  100. data/spec/models/calendar_spec.rb +11 -0
  101. data/spec/models/event_spec.rb +98 -0
  102. data/spec/models/ical_spec.rb +63 -0
  103. data/spec/models/recurrence_rule_spec.rb +82 -0
  104. data/spec/spec.opts +6 -0
  105. data/spec/spec_helper.rb +36 -0
  106. 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,3 @@
1
+ class Admin::EventVenuesController < Admin::ResourceController
2
+
3
+ end
@@ -0,0 +1,9 @@
1
+ class Admin::EventsController < Admin::ResourceController
2
+ paginate_models :per_page => 20
3
+
4
+ def load_models
5
+ finder = params[:all] ? Event.all : Event.future_and_current
6
+ self.models = finder.paginate(pagination_parameters)
7
+ end
8
+
9
+ 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("&amp;")
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