radiant-event_map-extension 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # Event Map Extension for Radiant
2
+
3
+ This extension adds mapping to the [event_calendar](http://github.com/radiant/radiant-event-calendar-extension). It will geocode your events automatically based on the address of the venue, or you can supply a postal code, UK postcode or grid reference for greater precision.
4
+
5
+ Events can be displayed on a google map and you can create links to bing or google maps that display the location of each event.
6
+
7
+ ## Installation
8
+
9
+ Should be straightforward:
10
+
11
+ script/extension install event_map
12
+
13
+ or as a gem:
14
+
15
+ gem install radiant-event_map-extension
16
+
17
+ ## Requirements
18
+
19
+ * [event_calendar](http://github.com/radiant/radiant-event-calendar-extension) extension
20
+ * [layouts](http://github.com/squaretalent/radiant-layouts-extension) or [share_layouts](http://github.com/radiant/radiant-share-layouts-extension) extension
21
+ * [geokit](http://geokit.rubyforge.org/) gem
22
+
23
+ ## Configuration
24
+
25
+ There is one required config setting:
26
+
27
+ * `event_map.layout` is the name of the layout used by the controller
28
+
29
+ ## Basic Usage
30
+
31
+ ### Linking to maps
32
+
33
+ We override the `url` method of EventVenue to return a map link if no other url is defined. The format of those links is determined by the link template stored in the config setting `event_map.link_format`. The markers :lat, :lng and :title will be replaced with the the correct value for each event venue.
34
+
35
+ There are also two shortcuts:
36
+
37
+ * 'google' is the default and equivalent to `http://maps.google.com/maps?q=:lat+:lng+(:title)` and will drop a pin on a google map.
38
+ * 'bing' is equivalent to `http://www.bing.com/maps/?v=2&cp=:lat~:lng&rtp=~pos.:lat_:lng_:title&lvl=15&sty=s&eo=0`, which will display a 1:25000 ordnance survey map (if you're in the UK) with a destination flag at your chosen point.
39
+
40
+ ### Displaying a map page
41
+
42
+ Create a layout that includes a `map_canvas` div and these page parts:
43
+
44
+ * `map_js` is required. It brings in the javascripts (and can be used in the header or at the end of the page as you prefer)
45
+ * `title` is the page title and can also be shown with `r:title`
46
+ * `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.
47
+
48
+ Here's a starting point:
49
+
50
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="en">
51
+ <head>
52
+ <title><r:title /></title>
53
+ <r:content part="map_js" />
54
+ </head>
55
+ <body>
56
+ <h1 id="pagetitle"><r:title /></h1>
57
+ <r:content part="faceting" />
58
+ <div id="map_canvas" style="width: 600px; height 400px;"></div>
59
+ </body>
60
+ </html>
61
+
62
+ Make sure that `Radiant::Config['event_map.layout']` matches exactly the _name_ of this layout.
63
+
64
+ ### javascript compatibility
65
+
66
+ The map javascript is generated by the EventVenuesController using `app/views/event_venues/index.js.erb`. It provides a `build_map_and_markers` method and the minimal jQuery hook required to populate #map_canvas when the DOM loads.
67
+
68
+ If you're not using jQuery you should find it straightforward to call `build_map_and_markers(div element)` from another script, and in that case you don't have to use our naming scheme either.
69
+
70
+ ### JSON interface
71
+
72
+ If you don't want to use the included scripts, you can skip that whole mechanism and work with the event data instead. EventVenuesController provides a simple JSON interface. Usually it's at /map and looks for addresses like this:
73
+
74
+ /map everything
75
+ /map/2010 events in 2010
76
+ /map/2010/12/ events in December 2010
77
+ /map/2010/12/12 events on 12 December 2010
78
+
79
+ If you're using `taggable_events` then we also inherit the tag-faceting interface here.
80
+
81
+ The data returned looks like this:
82
+
83
+ {
84
+
85
+ }
86
+
87
+ ## Bugs and features
88
+
89
+ [Github issues](http://github.com/spanner/radiant-event_map-extension/issues) please, or for little things an email or github message is fine.
90
+
91
+ ## Author & Copyright
92
+
93
+ Copyright 2008-2010 Will at spanner.org.
94
+
95
+ Released under the same terms as Radiant and/or Rails.
data/Rakefile ADDED
@@ -0,0 +1,138 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gem|
4
+ gem.name = "radiant-event_map-extension"
5
+ gem.summary = %Q{Google mapping for events in the radiant CMS}
6
+ gem.description = %Q{Further extends the event_calendar extension to allow easy google mapping with automatic geolocation based on event venues}
7
+ gem.email = "will@spanner.org"
8
+ gem.homepage = "http://github.com/radiant/radiant-event_map-extension"
9
+ gem.authors = ["spanner"]
10
+ gem.add_dependency "geokit"
11
+ gem.add_dependency "radiant", ">= 0.9.0"
12
+ gem.add_dependency "radiant-event_calendar-extension"
13
+ end
14
+ rescue LoadError
15
+ puts "Jeweler (or a dependency) not available. This is only required if you plan to package event_map as a gem."
16
+ end
17
+
18
+ # In rails 1.2, plugins aren't available in the path until they're loaded.
19
+ # Check to see if the rspec plugin is installed first and require
20
+ # it if it is. If not, use the gem version.
21
+
22
+ # Determine where the RSpec plugin is by loading the boot
23
+ unless defined? RADIANT_ROOT
24
+ ENV["RAILS_ENV"] = "test"
25
+ case
26
+ when ENV["RADIANT_ENV_FILE"]
27
+ require File.dirname(ENV["RADIANT_ENV_FILE"]) + "/boot"
28
+ when File.dirname(__FILE__) =~ %r{vendor/radiant/vendor/extensions}
29
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../../../")}/config/boot"
30
+ else
31
+ require "#{File.expand_path(File.dirname(__FILE__) + "/../../../")}/config/boot"
32
+ end
33
+ end
34
+
35
+ require 'rake'
36
+ require 'rake/rdoctask'
37
+ require 'rake/testtask'
38
+
39
+ rspec_base = File.expand_path(RADIANT_ROOT + '/vendor/plugins/rspec/lib')
40
+ $LOAD_PATH.unshift(rspec_base) if File.exist?(rspec_base)
41
+ require 'spec/rake/spectask'
42
+ require 'cucumber'
43
+ require 'cucumber/rake/task'
44
+
45
+ # Cleanup the RADIANT_ROOT constant so specs will load the environment
46
+ Object.send(:remove_const, :RADIANT_ROOT)
47
+
48
+ extension_root = File.expand_path(File.dirname(__FILE__))
49
+
50
+ task :default => :spec
51
+ task :stats => "spec:statsetup"
52
+
53
+ desc "Run all specs in spec directory"
54
+ Spec::Rake::SpecTask.new(:spec) do |t|
55
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
56
+ t.spec_files = FileList['spec/**/*_spec.rb']
57
+ end
58
+
59
+ task :features => 'spec:integration'
60
+
61
+ namespace :spec do
62
+ desc "Run all specs in spec directory with RCov"
63
+ Spec::Rake::SpecTask.new(:rcov) do |t|
64
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
65
+ t.spec_files = FileList['spec/**/*_spec.rb']
66
+ t.rcov = true
67
+ t.rcov_opts = ['--exclude', 'spec', '--rails']
68
+ end
69
+
70
+ desc "Print Specdoc for all specs"
71
+ Spec::Rake::SpecTask.new(:doc) do |t|
72
+ t.spec_opts = ["--format", "specdoc", "--dry-run"]
73
+ t.spec_files = FileList['spec/**/*_spec.rb']
74
+ end
75
+
76
+ [:models, :controllers, :views, :helpers].each do |sub|
77
+ desc "Run the specs under spec/#{sub}"
78
+ Spec::Rake::SpecTask.new(sub) do |t|
79
+ t.spec_opts = ['--options', "\"#{extension_root}/spec/spec.opts\""]
80
+ t.spec_files = FileList["spec/#{sub}/**/*_spec.rb"]
81
+ end
82
+ end
83
+
84
+ desc "Run the Cucumber features"
85
+ Cucumber::Rake::Task.new(:integration) do |t|
86
+ t.fork = true
87
+ t.cucumber_opts = ['--format', (ENV['CUCUMBER_FORMAT'] || 'pretty')]
88
+ # t.feature_pattern = "#{extension_root}/features/**/*.feature"
89
+ t.profile = "default"
90
+ end
91
+
92
+ # Setup specs for stats
93
+ task :statsetup do
94
+ require 'code_statistics'
95
+ ::STATS_DIRECTORIES << %w(Model\ specs spec/models)
96
+ ::STATS_DIRECTORIES << %w(View\ specs spec/views)
97
+ ::STATS_DIRECTORIES << %w(Controller\ specs spec/controllers)
98
+ ::STATS_DIRECTORIES << %w(Helper\ specs spec/views)
99
+ ::CodeStatistics::TEST_TYPES << "Model specs"
100
+ ::CodeStatistics::TEST_TYPES << "View specs"
101
+ ::CodeStatistics::TEST_TYPES << "Controller specs"
102
+ ::CodeStatistics::TEST_TYPES << "Helper specs"
103
+ ::STATS_DIRECTORIES.delete_if {|a| a[0] =~ /test/}
104
+ end
105
+
106
+ namespace :db do
107
+ namespace :fixtures do
108
+ desc "Load fixtures (from spec/fixtures) into the current environment's database. Load specific fixtures using FIXTURES=x,y"
109
+ task :load => :environment do
110
+ require 'active_record/fixtures'
111
+ ActiveRecord::Base.establish_connection(RAILS_ENV.to_sym)
112
+ (ENV['FIXTURES'] ? ENV['FIXTURES'].split(/,/) : Dir.glob(File.join(RAILS_ROOT, 'spec', 'fixtures', '*.{yml,csv}'))).each do |fixture_file|
113
+ Fixtures.create_fixtures('spec/fixtures', File.basename(fixture_file, '.*'))
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ desc 'Generate documentation for the event_map extension.'
121
+ Rake::RDocTask.new(:rdoc) do |rdoc|
122
+ rdoc.rdoc_dir = 'rdoc'
123
+ rdoc.title = 'EventMapExtension'
124
+ rdoc.options << '--line-numbers' << '--inline-source'
125
+ rdoc.rdoc_files.include('README')
126
+ rdoc.rdoc_files.include('lib/**/*.rb')
127
+ end
128
+
129
+ # For extensions that are in transition
130
+ desc 'Test the event_map extension.'
131
+ Rake::TestTask.new(:test) do |t|
132
+ t.libs << 'lib'
133
+ t.pattern = 'test/**/*_test.rb'
134
+ t.verbose = true
135
+ end
136
+
137
+ # Load any custom rakefiles for extension
138
+ Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.1.0
@@ -0,0 +1,50 @@
1
+ # We inherit from EventsController to share subsetting functionality
2
+ # All we do here is organise that information by venue.
3
+
4
+ class EventVenuesController < EventsController
5
+ helper_method :venues, :events_at_venue
6
+ radiant_layout { |controller| controller.layout_for :event_map }
7
+
8
+ def index
9
+ respond_to do |format|
10
+ format.html { }
11
+ format.js {
12
+ render :layout => false
13
+ }
14
+ format.json {
15
+ render :json => venue_events.to_json
16
+ }
17
+ end
18
+ end
19
+
20
+ # event_finder is defined in EventsController
21
+
22
+ def events
23
+ @events ||= all_events
24
+ end
25
+
26
+ def venues
27
+ @venues ||= events.map(&:event_venue).compact.uniq
28
+ end
29
+
30
+ # events are stashed in venue buckets to avoid returning to the database
31
+
32
+ def events_at_venue(venue)
33
+ venue_events[venue.id]
34
+ end
35
+
36
+ protected
37
+
38
+ def venue_events
39
+ return @venue_events if @venue_events
40
+ @venue_events = {}
41
+ events.each do |e|
42
+ if e.event_venue
43
+ @venue_events[e.event_venue.id] ||= []
44
+ @venue_events[e.event_venue.id].push(e)
45
+ end
46
+ end
47
+ @venue_events
48
+ end
49
+
50
+ end
@@ -0,0 +1,20 @@
1
+ - without_title ||= false
2
+
3
+ - if event
4
+ .map_event
5
+ - unless without_title
6
+ %h3.event
7
+ = link_to event.title, event.url
8
+
9
+ %p.date
10
+ %strong
11
+ = event.summarize_period
12
+ - if event.event_venue
13
+ at
14
+ = link_to event.event_venue.title, event.event_venue.url
15
+ - else
16
+ at
17
+ = event.location
18
+
19
+ %p.description
20
+ = event.short_description
@@ -0,0 +1,23 @@
1
+ - if venue
2
+ - if events = events_at_venue(venue)
3
+ .map_venue
4
+ - if events.length == 1
5
+ = render :partial => 'event', :object => events.first
6
+ - else
7
+ %h3
8
+ Events at
9
+ = link_to venue.title, venue.url
10
+ %ul
11
+ - remainder = events.slice!(4, 10000)
12
+ - events.each do |event|
13
+ %li
14
+ %strong
15
+ = link_to event.title, event.url
16
+ on
17
+ = event.date
18
+ - if remainder && remainder.any?
19
+ %li
20
+ and
21
+ = remainder.length
22
+ more events...
23
+
@@ -0,0 +1,17 @@
1
+ - content_for :title do
2
+ Events
3
+ = list_description
4
+
5
+ - content_for :faceting do
6
+ = render :partial => 'events/faceting'
7
+
8
+ - content_for :map_js do
9
+ %script{:src => "http://maps.google.com/maps/api/js?sensor=false"}
10
+ %script{:src => "#{url_for(calendar_parameters.merge(:format => :js))}"}
11
+
12
+ / deprecated older syntax probably unknown to anyone but me
13
+ - content_for :head do
14
+ %script{:src => "http://maps.google.com/maps/api/js?sensor=false"}
15
+ %script{:src => "#{url_for(calendar_parameters.merge(:format => :js))}"}
16
+
17
+ = render :partial => 'events/other_page_parts'
@@ -0,0 +1,55 @@
1
+ if(typeof jQuery == 'function') {
2
+ jQuery.fn.populate_map = function() {
3
+ this.each(function() { build_map_and_markers(this); });
4
+ };
5
+
6
+ jQuery(function() {
7
+ jQuery("#map_canvas").populate_map();
8
+ });
9
+ }
10
+
11
+ function build_map_and_markers(element) {
12
+ if (element != null) {
13
+ var map = new google.maps.Map(element, {mapTypeId: google.maps.MapTypeId.ROADMAP});
14
+ var bounds = new google.maps.LatLngBounds();
15
+ var one_icon = new google.maps.MarkerImage('/images/event_calendar/one_event.png',
16
+ new google.maps.Size(26,45),
17
+ new google.maps.Point(0,0),
18
+ new google.maps.Point(7, 45));
19
+ var several_icon = new google.maps.MarkerImage('/images/event_calendar/several_events.png',
20
+ new google.maps.Size(26,45),
21
+ new google.maps.Point(0,0),
22
+ new google.maps.Point(9, 45));
23
+ var one_shadow = new google.maps.MarkerImage('/images/event_calendar/event_shadow.png',
24
+ new google.maps.Size(60, 44),
25
+ new google.maps.Point(0,0),
26
+ new google.maps.Point(8, 45));
27
+ var several_shadow = new google.maps.MarkerImage('/images/event_calendar/event_shadow.png',
28
+ new google.maps.Size(60, 44),
29
+ new google.maps.Point(0,0),
30
+ new google.maps.Point(5, 45));
31
+ <% venues.each do |venue| %>
32
+ add_marker(map, {
33
+ title : "<%= escape_javascript(venue.title) %>",
34
+ image : <%= events_at_venue(venue).length > 1 ? "several_icon" : "one_icon" %>,
35
+ shadow : <%= events_at_venue(venue).length > 1 ? "several_shadow" : "one_shadow" %>,
36
+ position : new google.maps.LatLng("<%= venue.lat %>","<%= venue.lng %>"),
37
+ description : "<%= escape_javascript(render :partial => 'venue', :object => venue) %>"
38
+ });
39
+ bounds.extend(new google.maps.LatLng("<%= venue.lat %>","<%= venue.lng %>"));
40
+ <% end %>
41
+ map.fitBounds(bounds);
42
+ }
43
+ }
44
+
45
+ function add_marker (map, mark) {
46
+ var marker = new google.maps.Marker({ map : map, position : mark['position'], title : mark['title'], icon : mark['image'], shadow : mark['shadow'] });
47
+ var infowindow = new google.maps.InfoWindow({ content : mark['description'] , maxWidth : 400 });
48
+ google.maps.event.addListener(marker, 'click', function() { infowindow.open(map, marker); });
49
+ }
50
+
51
+
52
+
53
+
54
+
55
+
@@ -0,0 +1,11 @@
1
+ %span.calendar_views
2
+ %br
3
+ View as
4
+ = link_to("map", eventmap_url(calendar_parameters))
5
+ or
6
+ = link_to("calendar", calendar_url(calendar_parameters)) + "."
7
+ Stay up to date with
8
+ = link_to("RSS", calendar_url(calendar_parameters.merge(:format => :rss)))
9
+ or
10
+ = link_to("calendar feed", calendar_url(calendar_parameters.merge(:format => :ics, :protocol => 'webcal://', :only_path => false)))
11
+
data/config/routes.rb ADDED
@@ -0,0 +1,8 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+ map.with_options :controller => 'event_venues', :action => 'index' do |m|
3
+ m.eventmap "/map.:format"
4
+ m.eventmap_year "/map/:year.:format"
5
+ m.eventmap_month "/map/:year/:month.:format"
6
+ m.eventmap_day "/map/:year/:month/:mday.:format"
7
+ end
8
+ end
data/cucumber.yml ADDED
@@ -0,0 +1 @@
1
+ default: --format progress features --tags ~@proposed,~@in_progress
@@ -0,0 +1,20 @@
1
+ class LatLong < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :events, :lat, :string
4
+ add_column :events, :lng, :string
5
+ add_column :event_venues, :lat, :string
6
+ add_column :event_venues, :lng, :string
7
+ add_index :events, [:lat, :lng]
8
+ add_index :event_venues, [:lat, :lng]
9
+
10
+ end
11
+
12
+ def self.down
13
+ remove_column :events, :lat
14
+ remove_column :events, :lng
15
+ remove_column :event_venues, :lat
16
+ remove_column :event_venues, :lng
17
+ remove_index :events, [:lat, :lng]
18
+ remove_index :event_venues, [:lat, :lng]
19
+ end
20
+ end