radiant-location-extension 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/README +74 -0
- data/Rakefile +15 -0
- data/app/.DS_Store +0 -0
- data/app/controllers/.DS_Store +0 -0
- data/app/controllers/admin/locations_controller.rb +3 -0
- data/app/helpers/.DS_Store +0 -0
- data/app/helpers/admin/location_helper.rb +2 -0
- data/app/models/location.rb +24 -0
- data/app/models/location_finder_page.rb +105 -0
- data/app/models/locations_tags.rb +109 -0
- data/app/views/.DS_Store +0 -0
- data/app/views/admin/.DS_Store +0 -0
- data/app/views/admin/locations/_location.html.haml +11 -0
- data/app/views/admin/locations/edit.html.haml +59 -0
- data/app/views/admin/locations/index.html.haml +26 -0
- data/app/views/admin/locations/new.html.haml +75 -0
- data/app/views/admin/locations/remove.html.haml +20 -0
- data/config/routes.rb +5 -0
- data/db/migrate/001_create_locations.rb +15 -0
- data/db/migrate/002_set_properties.rb +31 -0
- data/lib/geo_kit/acts_as_mappable.rb +436 -0
- data/lib/geo_kit/geocoders.rb +351 -0
- data/lib/geo_kit/ip_geocode_lookup.rb +46 -0
- data/lib/geo_kit/mappable.rb +430 -0
- data/lib/location_geo_kit.rb +39 -0
- data/lib/radiant-location-extension.rb +5 -0
- data/lib/radiant-location-extension/version.rb +3 -0
- data/lib/tasks/location_extension_tasks.rake +28 -0
- data/location_extension.rb +51 -0
- data/public/images/admin/location.png +0 -0
- data/public/stylesheets/admin/location.css +19 -0
- data/radiant-location-extension.gemspec +21 -0
- metadata +102 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
= Location
|
2
|
+
|
3
|
+
== Introduction
|
4
|
+
|
5
|
+
Location is a plugin that will allow you to create relatively robust list of locations (stores, churches, etc.). This uses the Directory extension by Loren Johnson as a starting point but makes a number of different decisions. First it uses Geokit (http://geokit.rubyforge.org) to handle the heavy lifting. Second, the JavaScript is completely rewritten to use version 2 of the Google Maps API and to make some additional API changes (this is still TODO)
|
6
|
+
|
7
|
+
== Settings
|
8
|
+
|
9
|
+
You may enable or disable visible features for editing locations by setting these options in your environment using Radiant::Config or using the Settings extension:
|
10
|
+
|
11
|
+
Key Default Value Possible Values
|
12
|
+
=========================================================================
|
13
|
+
locations.use_groups? false true/false
|
14
|
+
locations.use_website_urls? false true/false
|
15
|
+
locations.use_page_paths? false true/false
|
16
|
+
locations.allow_manual_geocoding? false true/false
|
17
|
+
|
18
|
+
geokit.default_formula sphere sphere/flat
|
19
|
+
geokit.default_units miles miles/kms
|
20
|
+
|
21
|
+
geokit.geocoders.proxy_addr nil [IP Address or hostname of Proxy Server]
|
22
|
+
geokit.geocoders.proxy_port nil [Port of Proxy server]
|
23
|
+
geokit.geocoders.proxy_user nil [username for Proxy]
|
24
|
+
geokit.geocoders.proxy_pass nil [password for Proxy]
|
25
|
+
geokit.geocoders.timeout nil [number in seconds]
|
26
|
+
geokit.geocoders.yahoo nil [Yahoo Geocoder API Key]
|
27
|
+
geokit.geocoders.google [api code] [Google Maps API Key]
|
28
|
+
geokit.geocoders.geocoder_us false [Geocoder.us API Key]
|
29
|
+
geokit.geocoders.geocoder_ca false [Geocoder.ca API Key]
|
30
|
+
geokit.geocoders.provider_order 'google us' [space-separated list of geocoding services: google yahoo us ca ip]
|
31
|
+
|
32
|
+
== Geokit Modifications
|
33
|
+
|
34
|
+
I've made a few modifications to GeoKit to get it to work inside the extension and to maintain certain Radiant paradigms.
|
35
|
+
|
36
|
+
* Geokit is included as a library to the extension rather than as a plugin
|
37
|
+
* All references to the configuration values have been changed to use Radiant::Config
|
38
|
+
|
39
|
+
== Finding Results in space
|
40
|
+
|
41
|
+
To process search requests for locations you must create a Location Page. use <r:location> to find and return locations.
|
42
|
+
|
43
|
+
The parameters you can use to "find" locations are:
|
44
|
+
|
45
|
+
=== start point
|
46
|
+
|
47
|
+
The start point is some point in space relative to which we will find locations. This allows a user to say "Find location near Main Street and First Street, My Town, BC, Canada".
|
48
|
+
|
49
|
+
When a start value is specified (using either *address* or *lat*&*lng*), then it will be always be used to calculate a distance to each returned location. If a distance is requested, we will return all the locations with that distance of the start point.
|
50
|
+
|
51
|
+
==== *address*
|
52
|
+
|
53
|
+
If supplied, this will be an address that will be geocoded using the configured geocode providers. Once a latitude & longitude are calculated, they form the start point for the request.
|
54
|
+
|
55
|
+
==== *lat* & *lng*
|
56
|
+
|
57
|
+
If both parameters are supplied, they are used as the start point. They always take precedence over *address*.
|
58
|
+
|
59
|
+
=== *distance*
|
60
|
+
|
61
|
+
When specified, all the locations within this distance of the start point are returned. If no distance is specified, _all_ locations are returned
|
62
|
+
|
63
|
+
=== *units*
|
64
|
+
|
65
|
+
The units are used for calculating distance from a location to the start point. The default value is "km"
|
66
|
+
|
67
|
+
=== *count*
|
68
|
+
|
69
|
+
The number of results to return. Defaults to returning ALL results
|
70
|
+
|
71
|
+
=== *offset*
|
72
|
+
|
73
|
+
The number of the first result to return. This allows you to build pagination into your code
|
74
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'rdoc/task'
|
4
|
+
|
5
|
+
desc 'Generate documentation for the location extension.'
|
6
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
7
|
+
rdoc.rdoc_dir = 'rdoc'
|
8
|
+
rdoc.title = 'LocationExtension'
|
9
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
10
|
+
rdoc.rdoc_files.include('README')
|
11
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
12
|
+
end
|
13
|
+
|
14
|
+
# Load any custom rakefiles for extension
|
15
|
+
Dir[File.dirname(__FILE__) + '/tasks/*.rake'].sort.each { |f| require f }
|
data/app/.DS_Store
ADDED
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Location < ActiveRecord::Base
|
2
|
+
# Associations
|
3
|
+
belongs_to :created_by, :class_name => 'User'
|
4
|
+
belongs_to :updated_by, :class_name => 'User'
|
5
|
+
|
6
|
+
acts_as_mappable :default_units => :kms
|
7
|
+
before_validation :geocode_address
|
8
|
+
|
9
|
+
default_scope :order => 'name'
|
10
|
+
|
11
|
+
after_save :clear_page_cache
|
12
|
+
private
|
13
|
+
def geocode_address
|
14
|
+
unless self.manual_geocode
|
15
|
+
geo=GeoKit::Geocoders::MultiGeocoder.geocode self.full_address
|
16
|
+
errors.add(:full_address, "Could not Geocode address") if !geo.success
|
17
|
+
self.lat, self.lng = geo.lat,geo.lng if geo.success
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def clear_page_cache
|
22
|
+
Radiant::Cache.clear
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
class LocationFinderPage < Page
|
2
|
+
include Radiant::Taggable
|
3
|
+
include GeoKit::Geocoders
|
4
|
+
description 'A page to show locations.'
|
5
|
+
|
6
|
+
desc %{
|
7
|
+
The root location element.
|
8
|
+
|
9
|
+
Retrieves locations based on the supplied parameters. Permitted parameters are:
|
10
|
+
|
11
|
+
* *origin* -- The address from which to start searching
|
12
|
+
* *lat* & *lng* -- The coordinates of the origin from which to start searching. These override the *origign* attribute
|
13
|
+
* *distance* -- Find all the locations within the supplied distance
|
14
|
+
* *units* -- The units to use for distance calculations (km or miles)
|
15
|
+
* *limit* -- Limits the number of locations to show
|
16
|
+
* *offset* -- The number of the first result (for pagination)
|
17
|
+
|
18
|
+
Attributes:
|
19
|
+
* *group* -- When supplied, it will show only those locations in the given group
|
20
|
+
}
|
21
|
+
tag "location" do |tag|
|
22
|
+
unless tag.attr["group"].blank?
|
23
|
+
@options[:conditions] = {:group => tag.attr['group']}
|
24
|
+
end
|
25
|
+
|
26
|
+
tag.locals.location = Location.find(:first, @options)
|
27
|
+
tag.expand if tag.locals.location
|
28
|
+
end
|
29
|
+
|
30
|
+
tag "location:if_distance" do |tag|
|
31
|
+
if tag.locals.location.respond_to?("distance")
|
32
|
+
tag.expand
|
33
|
+
end
|
34
|
+
end
|
35
|
+
tag "location:unless_distance" do |tag|
|
36
|
+
if !tag.locals.location.respond_to?("distance")
|
37
|
+
tag.expand
|
38
|
+
end
|
39
|
+
end
|
40
|
+
tag "location:distance" do |tag|
|
41
|
+
|
42
|
+
pre = tag.attr.include?('precision') ? tag.attr['precision'].to_i : 1
|
43
|
+
|
44
|
+
pre
|
45
|
+
|
46
|
+
# "%0.#{pre}f" % (tag.locals.location.distance || 0)
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
def process(request,response)
|
51
|
+
# Parameters
|
52
|
+
# origin = Address string (if lat and lng are defined they are used preferentially)
|
53
|
+
# lat = latitude of origin
|
54
|
+
# lng = longitude of origin
|
55
|
+
# distance = distance to search within
|
56
|
+
# units = the units to apply to the distance (km or miles)
|
57
|
+
# count = the number of results to return
|
58
|
+
# offset = The result number to start with
|
59
|
+
|
60
|
+
@origin = request.parameters["origin"]
|
61
|
+
@lat = request.parameters["lat"]
|
62
|
+
@lng = request.parameters["lng"]
|
63
|
+
@distance = request.parameters["distance"]
|
64
|
+
@units = request.parameters["units"]
|
65
|
+
@count = request.parameters["count"]
|
66
|
+
@offset = request.parameters["offset"]
|
67
|
+
|
68
|
+
if @lat.blank? || @lng.blank?
|
69
|
+
unless @origin.blank?
|
70
|
+
geocode = MultiGeocoder.geocode(@origin)
|
71
|
+
logger.debug("geocode: #{geocode}")
|
72
|
+
@origin_geo = geocode if geocode.success
|
73
|
+
else
|
74
|
+
@origin_geo = nil;
|
75
|
+
end
|
76
|
+
else
|
77
|
+
@origin_geo = [@lat.to_f, @lng.to_f]
|
78
|
+
end
|
79
|
+
|
80
|
+
@options = {}
|
81
|
+
unless @origin_geo.nil?
|
82
|
+
@options[:origin] = @origin_geo
|
83
|
+
@options[:order] = "distance"
|
84
|
+
end
|
85
|
+
unless @distance.blank?
|
86
|
+
@options[:within] = @distance
|
87
|
+
end
|
88
|
+
unless @units.blank?
|
89
|
+
@options[:units] = @units
|
90
|
+
end
|
91
|
+
unless @count.blank?
|
92
|
+
@options[:limit] = @count
|
93
|
+
end
|
94
|
+
unless @offset.blank?
|
95
|
+
@options[:offset] = @offset
|
96
|
+
end
|
97
|
+
|
98
|
+
super request, response
|
99
|
+
end
|
100
|
+
|
101
|
+
def cache?
|
102
|
+
false
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
module LocationsTags
|
2
|
+
include Radiant::Taggable
|
3
|
+
include GeoKit::Geocoders
|
4
|
+
|
5
|
+
|
6
|
+
desc %{
|
7
|
+
The root location element.
|
8
|
+
|
9
|
+
Retrieves locations based on the supplied parameters. Permitted parameters are:
|
10
|
+
|
11
|
+
* *limit* -- Limits the number of locations to show
|
12
|
+
* *offset* -- The number of the first result (for pagination)
|
13
|
+
|
14
|
+
Attributes:
|
15
|
+
* *group* -- When supplied, it will show only those locations in the given group
|
16
|
+
}
|
17
|
+
tag "locations" do |tag|
|
18
|
+
options = {}
|
19
|
+
unless tag.attr['limit'].blank?
|
20
|
+
options[:limit] = tag.attr['limit'].to_i.to_s
|
21
|
+
options[:offset] = tag.attr['offset']
|
22
|
+
end
|
23
|
+
|
24
|
+
unless tag.attr["group"].blank?
|
25
|
+
options[:conditions] = {:group => tag.attr['group']}
|
26
|
+
end
|
27
|
+
|
28
|
+
options = options.merge(@options)
|
29
|
+
|
30
|
+
tag.locals.locations = Location.find(:all, options)
|
31
|
+
tag.expand
|
32
|
+
end
|
33
|
+
|
34
|
+
desc %{
|
35
|
+
loops through each of the locations
|
36
|
+
}
|
37
|
+
tag "locations:each" do |tag|
|
38
|
+
st = tag.attr.include?('offset') ? tag.attr['offset'].to_i : 0
|
39
|
+
en = tag.attr.include?('length') ? tag.attr['length'].to_i - st : -1
|
40
|
+
each_location tag, st, en
|
41
|
+
end
|
42
|
+
tag "locations:first" do |tag|
|
43
|
+
tag.locals.locations.first
|
44
|
+
end
|
45
|
+
tag "locations:last" do |tag|
|
46
|
+
tag.locals.locations.last
|
47
|
+
end
|
48
|
+
tag "locations:count" do |tag|
|
49
|
+
tag.locals.locations.length
|
50
|
+
end
|
51
|
+
tag "locations:each:has_next?" do |tag|
|
52
|
+
(tag.locals.locations.length - tag.locals.location_index) > 1
|
53
|
+
end
|
54
|
+
tag "locations:each:index" do |tag|
|
55
|
+
tag.locals.location_index
|
56
|
+
end
|
57
|
+
tag "locations:each:location" do |tag|
|
58
|
+
tag.expand if tag.locals.location
|
59
|
+
end
|
60
|
+
tag "location" do |tag|
|
61
|
+
unless tag.attr["id"].blank?
|
62
|
+
tag.locals.location = Location.find(tag.attr["id"].to_i)
|
63
|
+
end
|
64
|
+
tag.expand if tag.locals.location
|
65
|
+
end
|
66
|
+
|
67
|
+
[:full_address, :group, :website_url, :name, :lat, :lng].each do |method|
|
68
|
+
desc %{ returns the #{method} attribute of the current Location}
|
69
|
+
tag("location:#{method.to_s}") do |tag|
|
70
|
+
tag.locals.location.send(method)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
desc %{Allows you to use page tags (such as <r:slug>, <r:title>, etc.) for the page associated with the location.}
|
75
|
+
tag "location:page" do |tag|
|
76
|
+
if tag.locals.location.page_path?
|
77
|
+
tag.locals.page = Page.find_by_url tag.locals.location.page_path
|
78
|
+
tag.expand
|
79
|
+
end
|
80
|
+
end
|
81
|
+
tag "location:unless_page" do |tag|
|
82
|
+
unless Page.find_by_url tag.locals.location.page_path
|
83
|
+
tag.expand
|
84
|
+
end
|
85
|
+
end
|
86
|
+
tag "location:if_page" do |tag|
|
87
|
+
if Page.find_by_url tag.locals.location.page_path
|
88
|
+
tag.expand
|
89
|
+
end
|
90
|
+
end
|
91
|
+
# tag "location:map" do |tag|
|
92
|
+
# #content = '<script type="text/javascript" src="http://www.google.com/jsapi?key=#{Radiant::Config['geokit.geocoders.google']}"></script>'
|
93
|
+
#
|
94
|
+
# end
|
95
|
+
|
96
|
+
private
|
97
|
+
def each_location(tag, first=0, last=-1)
|
98
|
+
rng = Range.new first, last
|
99
|
+
offset = ((first < 0) ? tag.locals.location.length : 0) + first
|
100
|
+
result = []
|
101
|
+
|
102
|
+
tag.locals.locations[rng].each_with_index do |item, idx|
|
103
|
+
tag.locals.location_index = offset + idx
|
104
|
+
tag.locals.location = item
|
105
|
+
result << tag.expand
|
106
|
+
end
|
107
|
+
result
|
108
|
+
end
|
109
|
+
end
|
data/app/views/.DS_Store
ADDED
Binary file
|
Binary file
|
@@ -0,0 +1,11 @@
|
|
1
|
+
%tr.node.level-1
|
2
|
+
%td.location-title
|
3
|
+
= image("location", :class => "icon", :alt => 'page-icon', :title => '')
|
4
|
+
= link_to location.name , edit_admin_location_url(location)
|
5
|
+
%td.info
|
6
|
+
= h location.full_address
|
7
|
+
|
8
|
+
- if config['locations.use_groups?']
|
9
|
+
%td
|
10
|
+
= location.group
|
11
|
+
%td.remove= link_to 'Remove', remove_admin_location_path(location)
|
@@ -0,0 +1,59 @@
|
|
1
|
+
- include_stylesheet 'admin/location'
|
2
|
+
|
3
|
+
- render_region :main do |main|
|
4
|
+
- main.edit_header do
|
5
|
+
%h1 Edit Location
|
6
|
+
- main.edit_form do
|
7
|
+
- form_tag admin_location_path(@location), :method => :put do
|
8
|
+
= hidden_field 'location', 'lock_version'
|
9
|
+
= render_region :form_top
|
10
|
+
- render_region :form do |form|
|
11
|
+
- form.edit_title do
|
12
|
+
%p.title
|
13
|
+
%label{ :for => "location_name" } Name
|
14
|
+
= text_field "location", "name", :class => 'textbox', :maxlength => 100
|
15
|
+
- form.edit_full_address do
|
16
|
+
%p.address
|
17
|
+
%label{:for=>"location_full_address"} Full Address
|
18
|
+
= text_area "location", "full_address", :class => 'textbox', :size => '30x3'
|
19
|
+
%span.help Required. The address is used for determining the latitude and longitude for searching and showing on the map. the preferred format is: "123 1 Ave, MyCity, AB, Canada, T0M 0R0"
|
20
|
+
- form.edit_group do
|
21
|
+
- if config['locations.use_groups?']
|
22
|
+
%p.groups
|
23
|
+
%label.optional{:for=>"location_group"} Group
|
24
|
+
= text_field "location", "group", :class => 'textbox', :maxlength => 100
|
25
|
+
%span.help Optional. Assigning a group allows you to show and search locations within a specific group.
|
26
|
+
- form.edit_website_url do
|
27
|
+
- if config['locations.use_website_urls?']
|
28
|
+
%p.urls
|
29
|
+
%label.optional{:for=>"location_website_url"} Website URL
|
30
|
+
= text_field "location", "website_url", :class => 'textbox', :maxlength => 100
|
31
|
+
%span.help Optional.
|
32
|
+
- form.edit_page_path do
|
33
|
+
- if config['locations.use_page_paths?']
|
34
|
+
%p.page_path
|
35
|
+
%label.optional{:for=>"location_page_path"} Page path
|
36
|
+
= text_field "location", "page_path", :class => 'textbox', :maxlength => 100
|
37
|
+
%span.help Optional. Associate a page on your site with this location. Example: /locations/office
|
38
|
+
- form.edit_geocode do
|
39
|
+
- if config['locations.allow_manual_geocoding?']
|
40
|
+
%p.geocode
|
41
|
+
%label{:for=>"location_latitude"} Latitude
|
42
|
+
= text_field "location", "lat", :class => 'textbox', :maxlength => 100
|
43
|
+
%label{:for=>"location_longitude"} Longitude
|
44
|
+
= text_field "location", "lng", :class => 'textbox', :maxlength => 100
|
45
|
+
%label
|
46
|
+
= check_box "location", "manual_geocode"
|
47
|
+
Manually Geocode this Location
|
48
|
+
%span.help Auto-generated. Required if 'Manually Geocode this Location' is selected. The latitude and longitude are typically geocoded automatically. However some locations (particularly rural locations) cannot be easily geocoded. If you choose "manual geocoding" you will be able to set the latitude and longitude manually.
|
49
|
+
|
50
|
+
- render_region :form_bottom do |form_bottom|
|
51
|
+
- form_bottom.edit_timestamp do
|
52
|
+
= updated_stamp @location
|
53
|
+
- form_bottom.edit_buttons do
|
54
|
+
%p.buttons
|
55
|
+
= save_model_button(@location)
|
56
|
+
= save_model_and_continue_editing_button(@location)
|
57
|
+
or
|
58
|
+
= link_to "Cancel", admin_locations_url
|
59
|
+
|