diff --git a/app/assets/images/icons/map_edit.png b/app/assets/images/icons/map_edit.png new file mode 100755 index 0000000000..93d4d7e5ff Binary files /dev/null and b/app/assets/images/icons/map_edit.png differ diff --git a/app/assets/images/icons/page.png b/app/assets/images/icons/page.png new file mode 100755 index 0000000000..03ddd799fa Binary files /dev/null and b/app/assets/images/icons/page.png differ diff --git a/app/assets/images/icons/page_add.png b/app/assets/images/icons/page_add.png new file mode 100755 index 0000000000..d5bfa0719b Binary files /dev/null and b/app/assets/images/icons/page_add.png differ diff --git a/app/assets/stylesheets/common.css.scss b/app/assets/stylesheets/common.css.scss index 294d648c2a..af5f67ba01 100644 --- a/app/assets/stylesheets/common.css.scss +++ b/app/assets/stylesheets/common.css.scss @@ -335,6 +335,49 @@ h2 { top: 15px; } +/* Rules for User profile page */ +#usermap { + width: 50%; + float: right; +} +.sidebar_section { + margin-top: 10px; + border-top: 1px solid #BBB; + padding-top: 10px; +} +#user_info { + width: 50%; + float: left; +} + +li.activity, .vcard { + list-style-type: none; + margin-bottom: 4px; + padding: 3px; +} + +li.activity { + padding: 0 0 5px 0; + overflow: hidden; + position: relative; +} +.activity_body { + border-bottom: 1px solid #CCC; + overflow: hidden; + padding: 0 0 5px 0; +} +.activity_image, .profile_image { + float: left; + padding-right: 5px +} +.activity_info { + padding: 5px 0 0 0; +} +ul#activities_list, ul#friends { + padding: 0; + margin: 0; +} + /* Rules for OpenLayers maps */ #map { @@ -760,13 +803,14 @@ table.browse_details th { -moz-border-radius: 15px; } -#login_login { +#login_wrapper div#login_login { background-color: #f5f5ff; border: 1px solid #f3f3ff; border-radius: 15px; -moz-border-radius: 15px; } + #login_login h1 { margin-top: 5px; } @@ -849,9 +893,7 @@ p#contributorGuidance { #accountForm .user_map { position: relative; - width: 500px; - height: 400px; - border: 1px solid #ccc; + height: 300px; } #accountImage td { @@ -877,18 +919,13 @@ p#contributorGuidance { /* Rules for the user view */ .user_view .user_map { - position: relative; - width: 400px; - height: 400px; - border: 1px solid #ccc; + height: 300px; } .user_view .user_map p#no_home_location { - position: absolute; top: 0px; bottom: 0px; width: 90%; - height: 30%; margin: auto 5% } @@ -1124,3 +1161,11 @@ abbr.geo { } } } + +.addendum { + font-weight:normal; + vertical-align:middle; + padding:0 10px; + font-size:11px; + color:#888; +} diff --git a/app/assets/stylesheets/ltr.css.scss b/app/assets/stylesheets/ltr.css.scss index bbfebdbc1f..53d7f476ed 100644 --- a/app/assets/stylesheets/ltr.css.scss +++ b/app/assets/stylesheets/ltr.css.scss @@ -191,7 +191,6 @@ form#termsForm input#agree { } .user_view .user_map { - float: right; } /* Rules for rails validation error boxes */ diff --git a/app/controllers/user_controller.rb b/app/controllers/user_controller.rb index bccd5d5cb4..e1b0765614 100644 --- a/app/controllers/user_controller.rb +++ b/app/controllers/user_controller.rb @@ -14,7 +14,7 @@ class UserController < ApplicationController before_filter :require_allow_read_gpx, :only => [:api_gpx_files] before_filter :require_cookies, :only => [:login, :confirm] before_filter :require_administrator, :only => [:set_status, :delete, :list] - before_filter :lookup_this_user, :only => [:set_status, :delete] + before_filter :lookup_this_user, :only => [:set_status, :delete, :users] cache_sweeper :user_sweeper, :only => [:account, :set_status, :delete] @@ -394,6 +394,15 @@ def view else render_unknown_user params[:display_name] end + respond_to do |format| + format.html {} + format.json { render :json => @this_user.to_json, :content_type => "application/json", :status => 200 } + end + end + + # Get a list of nearby users that can become friends + def users + @this_user = User.find_by_display_name(params[:display_name]) end def make_friend diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 37dcf90e09..ed1e0c39dc 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -101,6 +101,16 @@ def user_image(user, options = {}) image_tag user.image.url(:large), options end + def user_image_url(user, options = {}) + options[:class] ||= "user_image" + + if user.image + image_path user.image.url(:large) + else + image_path "anon_large.png" + end + end + def user_thumbnail(user, options = {}) options[:class] ||= "user_thumbnail" diff --git a/app/helpers/user_helper.rb b/app/helpers/user_helper.rb index acaecff48f..eb09d144d9 100644 --- a/app/helpers/user_helper.rb +++ b/app/helpers/user_helper.rb @@ -1,4 +1,11 @@ module UserHelper + + # Returns true if this_user (or @this_user) is the currently logged_in user + def current_user(this_user = nil) + this_user ||= @this_user + @user && this_user.id == @user.id + end + def openid_logo image_tag "openid_small.png", :alt => t('user.login.openid_logo_alt'), :class => "openid_logo" end @@ -11,4 +18,14 @@ def openid_button(name, url) :title => t("user.login.openid_providers.#{name}.title") ) end + + def contribution_terms_status(user) + if not @this_user.terms_agreed.nil? + return t 'user.view.ct accepted', :ago =>time_ago_in_words(@this_user.terms_agreed) + elsif not @this_user.terms_seen? + return t 'user.view.ct undecided' + else + return t 'user.view.ct declined' + end + end end diff --git a/app/models/changeset.rb b/app/models/changeset.rb index b76d0c5a7e..30e36d4b19 100644 --- a/app/models/changeset.rb +++ b/app/models/changeset.rb @@ -52,6 +52,37 @@ def set_closed_time_now end end + # Determines the best 'summary' of the changeset, either from comments or automatically + def summary + unless (self.tags['comment'].nil?) + return self.tags['comment'].nil? + else + "#{self.num_changes} modifications made in this changeset." + end + end + + def activity_actor + return self.user + end + + # Returns a list of nodes that represent city/town/village that is nearby the center of changeset's bounding box. + def related_places(radius = 5) + bb = self.bbox.to_unscaled + + gc = OSM::GreatCircle.new(bb.centre_lat, bb.centre_lon) + sql_for_distance = gc.sql_for_distance_scaled('latitude', 'longitude') + + sql=<<-EOF + SELECT n.*, #{sql_for_distance} AS distance + FROM current_nodes n + INNER JOIN current_node_tags tag_place ON (tag_place.node_id = n.id AND tag_place.k = 'place' AND tag_place.v IN ('city', 'village', 'town')) + WHERE n.visible=TRUE AND + #{sql_for_distance} < #{radius} + ORDER BY distance + EOF + return Node.find_by_sql(sql) + end + def self.from_xml(xml, create=false) begin p = XML::Parser.string(xml, :options => XML::Parser::Options::NOERROR) diff --git a/app/models/diary_entry.rb b/app/models/diary_entry.rb index 64a412d28c..60fbf9e025 100644 --- a/app/models/diary_entry.rb +++ b/app/models/diary_entry.rb @@ -1,4 +1,8 @@ class DiaryEntry < ActiveRecord::Base + # including for #truncate. Makes a similar API for diaries & changesets. + include ActionView::Helpers::TextHelper + + belongs_to :user belongs_to :language, :foreign_key => 'language_code' @@ -37,4 +41,9 @@ def body def set_defaults self.body_format = "markdown" unless self.attribute_present?(:body_format) end + + # Determines the best 'summary' of the diary + def summary + truncate(self.body, :length => 50) + end end diff --git a/app/models/user.rb b/app/models/user.rb index 636f834243..127290762d 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,6 +1,7 @@ class User < ActiveRecord::Base require 'xml/libxml' + has_many :traces, :conditions => { :visible => true } has_many :diary_entries, :order => 'created_at DESC' has_many :diary_comments, :order => 'created_at DESC' @@ -133,6 +134,11 @@ def nearby(radius = NEARBY_RADIUS, num = NEARBY_USERS) return nearby end + # Returns array of users who could be your friends but aren't yet + def nearby_nonfriends(radius = NEARBY_RADIUS, num = NEARBY_USERS) + return self.nearby(radius,num) - self.friend_users + end + def distance(nearby_user) return OSM::GreatCircle.new(self.home_lat, self.home_lon).distance(nearby_user.home_lat, nearby_user.home_lon) end @@ -222,6 +228,40 @@ def access_token(application_key) return ClientApplication.find_by_key(application_key).access_token_for_user(self) end + +public + + # Helpers for outputting only public data via JSON + cattr_accessor :public_fields + @@public_fields = [:id, :display_name] + + alias_method :ar_to_json, :to_json + + def to_json(options = {}) + options[:only] = @@public_fields + options[:methods] = [:terms_agreed, :terms_seen, :statistics] + ar_to_json(options) + end + + # Returns a hash of statistics. + # Currently this is limited, will extend for usage history. + def statistics + { + :changesets => self.changesets.count, + :friends => self.friends.count + } + end + + def recent_changesets(limit = 5) + self.changesets.includes(:changeset_tags).limit(limit) + end + + def recent_activities(limit = 10) + nearby_changesets = [] + self.nearby.each {|user| nearby_changesets += user.recent_changesets.to_a} + (self.recent_changesets + self.diary_entries + nearby_changesets).sort {|a,b| b.created_at <=> a.created_at}[0..limit - 1] + end + private def set_defaults diff --git a/app/views/changeset/_changesets.html.erb b/app/views/changeset/_changesets.html.erb index 2bd40dafc3..0ce65a3c76 100644 --- a/app/views/changeset/_changesets.html.erb +++ b/app/views/changeset/_changesets.html.erb @@ -1,4 +1,4 @@ <% showusername = true if showusername.nil? %>