Commit d7ab4a9dfa8b388dbfba1004a6c69ff4f9690a02

Authored by Rodrigo Souto
Committed by Antonio Terceiro
1 parent 88921ec9

Georeferencing

  * The user can set his location in the map by dragging
    a marker or by setting his address in the form.
  * After setting his location, the map is added to
    the location block.
  * The user can configure the zoom level and the map
    type that will be shown in the location block.

ActionItem1256
app/controllers/my_profile/maps_controller.rb 0 → 100644
... ... @@ -0,0 +1,21 @@
  1 +class MapsController < MyProfileController
  2 +
  3 + protect 'edit_profile', :profile
  4 +
  5 + def edit_location
  6 + @profile_data = profile
  7 + if request.post?
  8 + begin
  9 + Profile.transaction do
  10 + if profile.update_attributes!(params[:profile_data])
  11 + flash[:notice] = _('Address was updated successfully!')
  12 + redirect_to :action => 'edit_location'
  13 + end
  14 + end
  15 + rescue
  16 + flash[:error] = _('Address could not be saved.')
  17 + end
  18 + end
  19 + end
  20 +
  21 +end
... ...
app/controllers/my_profile/profile_design_controller.rb
... ... @@ -5,7 +5,7 @@ class ProfileDesignController &lt; BoxOrganizerController
5 5 protect 'edit_profile_design', :profile
6 6  
7 7 def available_blocks
8   - blocks = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock ]
  8 + blocks = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock ]
9 9  
10 10 # blocks exclusive for organizations
11 11 if profile.has_members?
... ... @@ -22,7 +22,6 @@ class ProfileDesignController &lt; BoxOrganizerController
22 22  
23 23 # blocks exclusive for enterprises
24 24 if profile.enterprise?
25   - blocks << LocalizationBlock
26 25 blocks << DisabledEnterpriseMessageBlock
27 26 end
28 27  
... ...
app/models/community.rb
... ... @@ -4,6 +4,7 @@ class Community &lt; Organization
4 4  
5 5 settings_items :description
6 6 settings_items :language
  7 + settings_items :zip_code, :city, :state, :country
7 8  
8 9 xss_terminate :only => [ :name, :address, :contact_phone, :description ]
9 10  
... ... @@ -23,6 +24,10 @@ class Community &lt; Organization
23 24 end
24 25  
25 26 FIELDS = %w[
  27 + city
  28 + state
  29 + country
  30 + zip_code
26 31 description
27 32 language
28 33 ]
... ...
app/models/environment.rb
... ... @@ -275,6 +275,16 @@ class Environment &lt; ActiveRecord::Base
275 275 self.settings['message_for_disabled_enterprise'] = value
276 276 end
277 277  
  278 + # the environment's default location
  279 + def location
  280 + self.settings['location']
  281 + end
  282 +
  283 + # sets the environment's location.
  284 + def location=(value)
  285 + self.settings['location'] = value
  286 + end
  287 +
278 288 # returns the approval method used for this environment. Possible values are:
279 289 #
280 290 # Defaults to <tt>:admim</tt>.
... ...
app/models/localization_block.rb
... ... @@ -1,24 +0,0 @@
1   -class LocalizationBlock < Block
2   -
3   - def self.description
4   - _('Localization map block')
5   - end
6   -
7   - def help
8   - _('Shows where the profile is on the material world.')
9   - end
10   -
11   - def content
12   - profile = self.owner
13   - title = self.title
14   - lambda do
15   - profile.lat ?
16   - block_title(title) +
17   - content_tag('div',
18   - '<img src="http://maps.google.com/staticmap?center='+profile.lat.to_s()+','+profile.lng.to_s()+'&zoom=8&size=205x250&maptype=roadmap&markers='+profile.lat.to_s()+','+profile.lng.to_s()+',green&key='+GoogleMaps::key+'&sensor=false"/>',
19   - :class => 'the-localization-map' ) :
20   - content_tag('i', _('This profile has no geographical position registered.'))
21   - end
22   - end
23   -
24   -end
app/models/location_block.rb 0 → 100644
... ... @@ -0,0 +1,30 @@
  1 +class LocationBlock < Block
  2 +
  3 + settings_items :zoom, :type => :integer , :default => 4
  4 + settings_items :map_type, :type => :string , :default => 'roadmap'
  5 +
  6 + def self.description
  7 + _('Location map block')
  8 + end
  9 +
  10 + def help
  11 + _('Shows where the profile is on the material world.')
  12 + end
  13 +
  14 + def content
  15 + profile = self.owner
  16 + title = self.title
  17 + if profile.lat
  18 + block_title(title) +
  19 + content_tag('div',
  20 + '<img src="http://maps.google.com/staticmap?center=' + profile.lat.to_s() +
  21 + ',' + profile.lng.to_s() + '&zoom=' + zoom.to_s() +
  22 + '&size=205x250&maptype=' + map_type + '&markers=' + profile.lat.to_s() + ',' +
  23 + profile.lng.to_s() + ',green&key=' + GoogleMaps::key + '&sensor=false"/>',
  24 + :class => 'the-localization-map' )
  25 + else
  26 + content_tag('i', _('This profile has no geographical position registered.'))
  27 + end
  28 + end
  29 +
  30 +end
... ...
app/models/profile.rb
... ... @@ -151,12 +151,23 @@ class Profile &lt; ActiveRecord::Base
151 151  
152 152 belongs_to :region
153 153  
154   - def location
  154 + def location(separator = ' - ')
155 155 myregion = self.region
156 156 if myregion
157   - myregion.hierarchy.reverse.first(2).map(&:name).join(' - ')
  157 + myregion.hierarchy.reverse.first(2).map(&:name).join(separator)
158 158 else
159   - [ :city, :state, :country_name ].map {|item| self.respond_to?(item) ? self.send(item) : nil }.compact.join(' - ')
  159 + %w[address city state country_name zip_code ].map {|item| (self.respond_to?(item) && !self.send(item).blank?) ? self.send(item) : nil }.compact.join(separator)
  160 + end
  161 + end
  162 +
  163 + def geolocation
  164 + unless location.blank?
  165 + location
  166 + else
  167 + if environment.location.blank?
  168 + environment.location = "BRA"
  169 + end
  170 + environment.location
160 171 end
161 172 end
162 173  
... ...
app/views/box_organizer/_location_block.rhtml 0 → 100644
... ... @@ -0,0 +1,10 @@
  1 +<div>
  2 + <%= labelled_form_field _('Zoom Level:'), select(:block, :zoom, [[_('World'), 0],
  3 + 1,2,3,4,5,6,7,8,10,11,12,13,14,15,16,17,18,
  4 + [_('Street'), 19]]) %>
  5 + <%= labelled_form_field _('Map Type:'), select(:block, :map_type, [[_('Road Map') , 'roadmap' ],
  6 + [_('Mobile') , 'mobile' ],
  7 + [_('Satellite'), 'satellite'],
  8 + [_('Terrain') , 'terrain' ],
  9 + [_('Hybrid') , 'hybrid' ]]) %>
  10 +</div>
... ...
app/views/maps/_google_map.rhtml 0 → 100644
... ... @@ -0,0 +1,136 @@
  1 +<%= content_tag('script', '', :src => GoogleMaps.api_url, :type => 'text/javascript') %>
  2 +
  3 +<script type="text/javascript" >
  4 + var geocoder;
  5 + var map;
  6 + var marker;
  7 + var center;
  8 + var move = true;
  9 + var previousCenter;
  10 +
  11 + function getAddress(overlay, latlng) {
  12 + $('location-fields').addClassName("loading");
  13 + if (latlng != null) {
  14 + geocoder.getLocations(latlng, showAddress);
  15 + }
  16 + }
  17 +
  18 + function getAddressData() {
  19 + var text = '';
  20 + var fields = [
  21 + 'profile_data_country',
  22 + 'profile_data_state',
  23 + 'profile_data_city',
  24 + 'profile_data_address',
  25 + 'profile_data_zip_code'
  26 + ];
  27 + for (var i = 0; i < fields.length; i++) {
  28 + var field = fields[i];
  29 + if ($(field)) {
  30 + text += $(field).value + " ";
  31 + }
  32 + }
  33 + return text;
  34 + }
  35 +
  36 + function showAddress(response) {
  37 + var message;
  38 + place = geoCodeAddress(response);
  39 + if ( place ) {
  40 + if ( move ) {
  41 + center = new GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);
  42 + marker.setLatLng(center);
  43 + } else {
  44 + move = true;
  45 + }
  46 + message = showMessage(place);
  47 + updateFields(place);
  48 + } else {
  49 + message = showNotFoundMessage();
  50 + }
  51 + map.addOverlay(marker);
  52 + map.setCenter(marker.getLatLng());
  53 + marker.openInfoWindowHtml(message, {maxWidth:300});
  54 + }
  55 +
  56 + function geoCodeAddress(response) {
  57 + if (!response || (response && response.Status.code != '200')) {
  58 + return false;
  59 + } else {
  60 + place = response.Placemark[0];
  61 + return place;
  62 + }
  63 + }
  64 +
  65 + function showMessage(place) {
  66 + var message = '<b><%= _('Address:') %></b> ' + place.address + '<br>' +
  67 + '<b><%= _('Coordinates:') %></b> ' + place.Point.coordinates[0] + "," + place.Point.coordinates[1] + '<br>' +
  68 + '<b><%= _('Country code:') %></b> ' + place.AddressDetails.Country.CountryNameCode + '<br>';
  69 + return message;
  70 + }
  71 +
  72 + function showNotFoundMessage() {
  73 + var message = '<%= _('Address not found') %>' + '<br>' +
  74 + '<b><%= _('Coordinates:') %></b> ' + marker.getLatLng().lng() + "," + marker.getLatLng().lat();
  75 + return message;
  76 + }
  77 +
  78 + function updateFields(response) {
  79 + var position = marker.getLatLng();
  80 + $('profile_data_lat').value = position.lat();
  81 + $('profile_data_lng').value = position.lng();
  82 + $('location-fields').removeClassName("loading");
  83 + }
  84 +
  85 + function loadMap() {
  86 + if (GBrowserIsCompatible()) {
  87 + map = new GMap2(document.getElementById("location-map"));
  88 + geocoder = new GClientGeocoder();
  89 + var lat = <%= profile.lat || 'false' %>;
  90 + var lng = <%= profile.lng || 'false' %>;
  91 + if ( lat && lng ) {
  92 + center = new GLatLng( lat, lng );
  93 + continueLoadMap();
  94 + } else {
  95 + geocoder.getLocations('<%= profile.geolocation %>', loadAddress);
  96 + }
  97 + }
  98 + }
  99 +
  100 + function loadAddress(response) {
  101 + place = geoCodeAddress(response);
  102 + if ( move ) {
  103 + center = new GLatLng(place.Point.coordinates[1],place.Point.coordinates[0]);
  104 + }
  105 + continueLoadMap();
  106 + }
  107 +
  108 + function continueLoadMap() {
  109 + marker = new GMarker(center, {draggable: true});
  110 + map.setCenter(center, 4);
  111 +
  112 + map.addControl(new GLargeMapControl());
  113 + map.addControl(new GScaleControl());
  114 + map.addControl(new GMapTypeControl());
  115 +
  116 + GEvent.addListener(marker, "dragstart", function() {
  117 + previousCenter = marker.getLatLng();
  118 + map.closeInfoWindow();
  119 + });
  120 +
  121 + GEvent.addListener(marker, "dragend", function() {
  122 + move = false;
  123 + getAddress(overlay, marker.getLatLng());
  124 + });
  125 +
  126 + GEvent.addListener(marker, "click", function() {
  127 + move = false;
  128 + getAddress(overlay, marker.getLatLng());
  129 + });
  130 +
  131 + map.addOverlay(marker);
  132 + }
  133 +
  134 + window.onload = loadMap;
  135 + window.unload = GUnload();
  136 +</script>
... ...
app/views/maps/edit_location.rhtml 0 → 100644
... ... @@ -0,0 +1,35 @@
  1 +<h1><%= _('Location') %></h1>
  2 +
  3 +<% form_for :profile_data, :url => {:action => 'edit_location'} do |f| %>
  4 +
  5 + <div id='location-fields'>
  6 + <%= optional_field(profile, 'country', select_country(_('Country'), 'profile_data', 'country', {:class => 'type-select'})) %>
  7 + <%= optional_field(profile, 'state', labelled_form_field(_('State'), f.text_field(:state))) %>
  8 + <%= optional_field(profile, 'city', labelled_form_field(_('City'), f.text_field(:city))) %>
  9 + <%= optional_field(profile, 'zip_code', labelled_form_field(_('ZIP code'), text_field(:profile_data, :zip_code))) %>
  10 + <%= optional_field(profile, 'address', labelled_form_field(_('Address (street and number)'), text_field(:profile_data, :address))) %>
  11 + <% button_bar do %>
  12 + <%= button_to_function :search, _('Locate in the map'), "getAddress(null, getAddressData())", :title => _("Locate the address informed above in the map below (note that you'll probably need to adjust the marker to get a precise position)") %>
  13 + <%= submit_button 'save', _('Save') %>
  14 + <%= button(:back, _('Back to control panel'), :controller => 'profile_editor') %>
  15 + <% end %>
  16 + </div>
  17 +
  18 +
  19 + <div style='overflow: hidden'>
  20 + <p><div id="location-map"></div></p>
  21 + </div>
  22 +
  23 + <%= f.hidden_field(:lat) %>
  24 + <%= f.hidden_field(:lng) %>
  25 +
  26 +
  27 +
  28 + <% button_bar do %>
  29 + <%= submit_button 'save', _('Save') %>
  30 + <%= button(:back, _('Back to control panel'), :controller => 'profile_editor') %>
  31 + <% end %>
  32 +
  33 +<% end %>
  34 +
  35 +<%= render :partial => 'google_map'%>
... ...
app/views/profile_editor/index.rhtml
... ... @@ -14,6 +14,8 @@
14 14 <%= control_panel_button(__('Community Info and settings'), 'edit-profile-group', :controller => 'profile_editor', :action => 'edit') if profile.community? %>
15 15 <%= control_panel_button(__('Enterprise Info and settings'), 'edit-profile-enterprise', :controller => 'profile_editor', :action => 'edit') if profile.enterprise? %>
16 16  
  17 + <%= control_panel_button(_('Location'), 'edit-location', :controller => 'maps', :action => 'edit_location') %>
  18 +
17 19 <%= control_panel_button(_('Mail settings'), 'mail', :controller => 'mailconf') if profile.person? && MailConf.enabled? %>
18 20  
19 21 <%= control_panel_button(_('Tasks'), 'todo', :controller => 'tasks', :action => 'index') %>
... ...
db/migrate/077_rename_location_block.rb 0 → 100644
... ... @@ -0,0 +1,9 @@
  1 +class RenameLocationBlock < ActiveRecord::Migration
  2 + def self.up
  3 + execute "update blocks set type='LocationBlock' where type='LocalizationBlock'"
  4 + end
  5 +
  6 + def self.down
  7 + execute "update blocks set type='LocalizationBlock' where type='LocationBlock'"
  8 + end
  9 +end
... ...
db/schema.rb
... ... @@ -9,7 +9,7 @@
9 9 #
10 10 # It's strongly recommended to check this file into your version control system.
11 11  
12   -ActiveRecord::Schema.define(:version => 76) do
  12 +ActiveRecord::Schema.define(:version => 77) do
13 13  
14 14 create_table "article_versions", :force => true do |t|
15 15 t.integer "article_id"
... ... @@ -88,8 +88,8 @@ ActiveRecord::Schema.define(:version =&gt; 76) do
88 88 t.boolean "virtual", :default => false
89 89 end
90 90  
91   - add_index "articles_categories", ["article_id"], :name => "index_articles_categories_on_article_id"
92 91 add_index "articles_categories", ["category_id"], :name => "index_articles_categories_on_category_id"
  92 + add_index "articles_categories", ["article_id"], :name => "index_articles_categories_on_article_id"
93 93  
94 94 create_table "blocks", :force => true do |t|
95 95 t.string "title"
... ... @@ -103,10 +103,10 @@ ActiveRecord::Schema.define(:version =&gt; 76) do
103 103 t.datetime "fetched_at"
104 104 end
105 105  
106   - add_index "blocks", ["box_id"], :name => "index_blocks_on_box_id"
107   - add_index "blocks", ["enabled"], :name => "index_blocks_on_enabled"
108   - add_index "blocks", ["fetched_at"], :name => "index_blocks_on_fetched_at"
109 106 add_index "blocks", ["type"], :name => "index_blocks_on_type"
  107 + add_index "blocks", ["fetched_at"], :name => "index_blocks_on_fetched_at"
  108 + add_index "blocks", ["enabled"], :name => "index_blocks_on_enabled"
  109 + add_index "blocks", ["box_id"], :name => "index_blocks_on_box_id"
110 110  
111 111 create_table "boxes", :force => true do |t|
112 112 t.string "owner_type"
... ... @@ -136,8 +136,8 @@ ActiveRecord::Schema.define(:version =&gt; 76) do
136 136 t.boolean "virtual", :default => false
137 137 end
138 138  
139   - add_index "categories_profiles", ["category_id"], :name => "index_categories_profiles_on_category_id"
140 139 add_index "categories_profiles", ["profile_id"], :name => "index_categories_profiles_on_profile_id"
  140 + add_index "categories_profiles", ["category_id"], :name => "index_categories_profiles_on_category_id"
141 141  
142 142 create_table "comments", :force => true do |t|
143 143 t.string "title"
... ... @@ -189,8 +189,8 @@ ActiveRecord::Schema.define(:version =&gt; 76) do
189 189 t.integer "update_errors", :default => 0
190 190 end
191 191  
192   - add_index "external_feeds", ["enabled"], :name => "index_external_feeds_on_enabled"
193 192 add_index "external_feeds", ["fetched_at"], :name => "index_external_feeds_on_fetched_at"
  193 + add_index "external_feeds", ["enabled"], :name => "index_external_feeds_on_enabled"
194 194  
195 195 create_table "favorite_enteprises_people", :id => false, :force => true do |t|
196 196 t.integer "person_id"
... ... @@ -224,8 +224,8 @@ ActiveRecord::Schema.define(:version =&gt; 76) do
224 224 t.datetime "updated_at"
225 225 end
226 226  
227   - add_index "product_categorizations", ["category_id"], :name => "index_product_categorizations_on_category_id"
228 227 add_index "product_categorizations", ["product_id"], :name => "index_product_categorizations_on_product_id"
  228 + add_index "product_categorizations", ["category_id"], :name => "index_product_categorizations_on_category_id"
229 229  
230 230 create_table "products", :force => true do |t|
231 231 t.integer "enterprise_id"
... ... @@ -305,8 +305,8 @@ ActiveRecord::Schema.define(:version =&gt; 76) do
305 305 t.datetime "created_at"
306 306 end
307 307  
308   - add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id"
309 308 add_index "taggings", ["taggable_id", "taggable_type"], :name => "index_taggings_on_taggable_id_and_taggable_type"
  309 + add_index "taggings", ["tag_id"], :name => "index_taggings_on_tag_id"
310 310  
311 311 create_table "tags", :force => true do |t|
312 312 t.string "name"
... ...
features/location.feature 0 → 100644
... ... @@ -0,0 +1,57 @@
  1 +Feature: Location
  2 + As a user
  3 + I want to edit my address and location
  4 + So that others can find me in the map
  5 +
  6 + Background:
  7 + And the following users
  8 + | login |
  9 + | zezinho |
  10 + And I am logged in as "zezinho"
  11 +
  12 + Scenario: editing my address
  13 + Given the following Person fields are enabled
  14 + | address |
  15 + | country |
  16 + | state |
  17 + | city |
  18 + | zip_code |
  19 + And I follow "Control panel"
  20 + And I follow "Location"
  21 + When I fill in "Address" with "Rua Marechal Floriano, 28"
  22 + And I select "Brazil" from "Country"
  23 + And I fill in "State" with "Bahia"
  24 + And I fill in "City" with "Salvador"
  25 + And I fill in "ZIP Code" with "40110010"
  26 + And I press "Save"
  27 + Then "zezinho" should have the following data
  28 + | address | country | state | city | zip_code |
  29 + | Rua Marechal Floriano, 28 | BR | Bahia | Salvador | 40110010 |
  30 +
  31 + Scenario Outline: editing address of collectives
  32 + Given the following <class> fields are enabled
  33 + | address |
  34 + | country |
  35 + | state |
  36 + | city |
  37 + | zip_code |
  38 + Given the following <plural>
  39 + | identifier | name | owner |
  40 + | colivre | Colivre | zezinho |
  41 + And I am on Colivre's control panel
  42 + And I follow "Location"
  43 + And I select "Brazil" from "Country"
  44 + And I fill in the following:
  45 + | Address | Rua Marechal Floriano, 28 |
  46 + | State | Bahia |
  47 + | City | Salvador |
  48 + | ZIP Code | 40110010 |
  49 + When I press "Save"
  50 + Then "colivre" should have the following data
  51 + | address | country | state | city | zip_code |
  52 + | Rua Marechal Floriano, 28 | BR | Bahia | Salvador | 40110010 |
  53 + Examples:
  54 + | class | plural |
  55 + | Community | communities |
  56 + | Enterprise | enterprises |
  57 +
... ...
features/step_definitions/custom_webrat_steps.rb
... ... @@ -6,3 +6,7 @@ When /^I should not see &quot;([^\&quot;]+)&quot; link$/ do |link|
6 6 response.should_not have_selector("a", :content => link)
7 7 end
8 8  
  9 +When /^I wait (\d+) seconds$/ do |seconds|
  10 + sleep seconds.to_i
  11 +end
  12 +
... ...
features/step_definitions/noosfero_steps.rb
... ... @@ -7,15 +7,14 @@ Given /^the following users$/ do |table|
7 7 end
8 8 end
9 9  
10   -Given /^the following communities$/ do |table|
11   - table.hashes.each do |item|
12   - Community.create!(item)
13   - end
14   -end
15   -
16   -Given /^the following enterprises$/ do |table|
17   - table.hashes.each do |item|
18   - Enterprise.create!(item)
  10 +Given /^the following (communities|enterprises)$/ do |kind,table|
  11 + klass = kind.singularize.camelize.constantize
  12 + table.hashes.each do |row|
  13 + owner = row.delete("owner")
  14 + community = klass.create!(row)
  15 + if owner
  16 + community.add_admin(Profile[owner])
  17 + end
19 18 end
20 19 end
21 20  
... ... @@ -111,3 +110,24 @@ end
111 110 Given /^"([^\"]*)" has no articles$/ do |profile|
112 111 (Profile[profile] || Profile.find_by_name(profile)).articles.delete_all
113 112 end
  113 +
  114 +Given /^the following (\w+) fields are enabled$/ do |klass, table|
  115 + env = Environment.default
  116 + fields = table.raw.inject({}) do |hash, line|
  117 + hash[line.first] = { "active" => 'true' }
  118 + hash
  119 + end
  120 +
  121 + env.send("custom_#{klass.downcase}_fields=", fields)
  122 + env.save!
  123 + if fields.keys != env.send("active_#{klass.downcase}_fields")
  124 + raise "Not all fields enabled! Requested: %s; Enabled: %s" % [fields.keys.inspect, env.send("active_#{klass.downcase}_fields").inspect]
  125 + end
  126 +end
  127 +
  128 +Then /^"([^\"]*)" should have the following data$/ do |id, table|
  129 + profile = Profile.find_by_identifier(id)
  130 + expected = table.hashes.first
  131 + data = expected.keys.inject({}) { |hash, key| hash[key] = profile.send(key).to_s; hash }
  132 + data.should == expected
  133 +end
... ...
public/stylesheets/blocks/localization-block.css
... ... @@ -1,13 +0,0 @@
1   -.localization-block {
2   - text-align: center;
3   -}
4   -
5   -.the-localization-map {
6   - margin: auto;
7   - width: 211px;
8   - max-width: 205px;
9   - padding: 3px;
10   - background: #FFF;
11   - border: 1px solid #888;
12   -}
13   -
public/stylesheets/blocks/location-block.css 0 → 100644
... ... @@ -0,0 +1,13 @@
  1 +.location-block {
  2 + text-align: center;
  3 +}
  4 +
  5 +.the-localization-map {
  6 + overflow: hidden;
  7 + margin: auto;
  8 + max-width: 205px;
  9 + padding: 3px;
  10 + background: #FFF;
  11 + border: 1px solid #888;
  12 +}
  13 +
... ...
public/stylesheets/common.css
... ... @@ -604,3 +604,10 @@ code input {
604 604 cursor: progress;
605 605 background: transparent url(../images/loading.gif) no-repeat scroll center 50px;
606 606 }
  607 +
  608 +/**** Location Map ****/
  609 +#location-map {
  610 + width: 100%;
  611 + height: 320px;
  612 +}
  613 +
... ...
public/stylesheets/controller_profile_editor.css
... ... @@ -88,3 +88,5 @@ a.control-panel-validation {background-image: url(../images/control-panel/applic
88 88 a.control-panel-mail {background-image: url(../images/control-panel/email.png)}
89 89 .msie6 a.control-panel-mail {background-image: url(../images/control-panel/email.gif)}
90 90  
  91 +a.control-panel-edit-location {background-image: url(../images/control-panel/set-geolocation.png)}
  92 +.msie6 a.control-panel-edit-location {background-image: url(../images/control-panel/set-geolocation.gif)}
... ...
test/functional/maps_controller_test.rb 0 → 100644
... ... @@ -0,0 +1,43 @@
  1 +require File.dirname(__FILE__) + '/../test_helper'
  2 +require 'maps_controller'
  3 +
  4 +# Re-raise errors caught by the controller.
  5 +class MapsController; def rescue_action(e) raise e end; end
  6 +
  7 +class MapsControllerTest < Test::Unit::TestCase
  8 +
  9 + def setup
  10 + @controller = MapsController.new
  11 + @request = ActionController::TestRequest.new
  12 + @response = ActionController::TestResponse.new
  13 +
  14 + @profile = create_user('test_profile').person
  15 + login_as(@profile.identifier)
  16 + end
  17 +
  18 + attr_reader :profile
  19 +
  20 + should 'save profile address' do
  21 + post :edit_location, :profile => profile.identifier, :profile_data => { 'address' => 'new address' }
  22 + assert_equal 'new address', Profile['test_profile'].address
  23 + end
  24 +
  25 + should 'back when update address fail' do
  26 + Profile.any_instance.stubs(:update_attributes!).returns(false)
  27 + post :edit_location, :profile => profile.identifier, :profile_data => { 'address' => 'new address' }
  28 + assert_nil profile.address
  29 + assert_template 'edit_location'
  30 + end
  31 +
  32 + should 'show page to edit location' do
  33 + get :edit_location, :profile => profile.identifier
  34 + assert_response :success
  35 + assert_template 'edit_location'
  36 + end
  37 +
  38 + should 'dispĺay form for address with profile address' do
  39 + get :edit_location, :profile => profile.identifier
  40 + assert_tag :tag => 'input', :attributes => { :name => 'location' }
  41 + end
  42 +
  43 +end
... ...
test/unit/profile_test.rb
... ... @@ -698,15 +698,60 @@ class ProfileTest &lt; Test::Unit::TestCase
698 698 assert_equal 'Salvador - Bahia', p.location
699 699 end
700 700  
701   - should 'use city/state/country fields for location when no region object is set' do
  701 + should 'use city/state/country/address/zip_code fields for location when no region object is set' do
702 702 p = Profile.new
703 703 p.expects(:region).returns(nil)
704   - p.expects(:city).returns("Salvador")
705   - p.expects(:state).returns("Bahia")
706   - p.expects(:country_name).returns("Brasil")
707   - assert_equal 'Salvador - Bahia - Brasil', p.location
  704 + p.expects(:address).returns("Rua A").at_least_once
  705 + p.expects(:city).returns("Salvador").at_least_once
  706 + p.expects(:state).returns("Bahia").at_least_once
  707 + p.expects(:country_name).returns("Brasil").at_least_once
  708 + p.expects(:zip_code).returns("40000000").at_least_once
  709 + assert_equal 'Rua A - Salvador - Bahia - Brasil - 40000000', p.location
708 710 end
709 711  
  712 + should 'choose separator for location' do
  713 + p = Profile.new
  714 + p.expects(:region).returns(nil)
  715 + p.expects(:address).returns("Rua A").at_least_once
  716 + p.expects(:city).returns("Salvador").at_least_once
  717 + p.expects(:state).returns("Bahia").at_least_once
  718 + p.expects(:country_name).returns("Brasil").at_least_once
  719 + p.expects(:zip_code).returns("40000000").at_least_once
  720 + assert_equal 'Rua A, Salvador, Bahia, Brasil, 40000000', p.location(', ')
  721 + end
  722 +
  723 + should 'not display separator on location if city/state/country/address/zip_code is blank' do
  724 + p = Profile.new
  725 + p.expects(:region).returns(nil)
  726 + p.expects(:address).returns("Rua A").at_least_once
  727 + p.expects(:city).returns("Salvador").at_least_once
  728 + p.expects(:state).returns("").at_least_once
  729 + p.expects(:country_name).returns("Brasil").at_least_once
  730 + p.expects(:zip_code).returns("40000000").at_least_once
  731 + assert_equal 'Rua A - Salvador - Brasil - 40000000', p.location
  732 + end
  733 +
  734 + should 'use location on geolocation if not blank' do
  735 + p = Profile.new
  736 + p.expects(:region).returns(nil).at_least_once
  737 + p.expects(:address).returns("Rua A").at_least_once
  738 + p.expects(:city).returns("Salvador").at_least_once
  739 + p.expects(:state).returns("").at_least_once
  740 + p.expects(:country_name).returns("Brasil").at_least_once
  741 + p.expects(:zip_code).returns("40000000").at_least_once
  742 + assert_equal 'Rua A - Salvador - Brasil - 40000000', p.geolocation
  743 + end
  744 +
  745 + should 'use default location on geolocation if not blank' do
  746 + p = Profile.new
  747 + p.expects(:region).returns(nil)
  748 + e = Environment.default
  749 + p.expects(:environment).returns(e)
  750 + e.expects(:location).returns('Brasil')
  751 + assert_equal 'Brasil', p.geolocation
  752 + end
  753 +
  754 +
710 755 should 'lookup country name' do
711 756 p = Profile.new
712 757 # two sample countries; trust the rest works
... ...