From: Shaun McDonald Date: Thu, 3 Jul 2008 13:06:24 +0000 (+0000) Subject: Merge changes from trunk 7673:8632. X-Git-Tag: live~8563^2~325 X-Git-Url: https://git.openstreetmap.org./rails.git/commitdiff_plain/5f8ab9e9244550b20b8d3bd97b3567df7020d06d?hp=-c Merge changes from trunk 7673:8632. --- 5f8ab9e9244550b20b8d3bd97b3567df7020d06d diff --combined app/controllers/api_controller.rb index d7937cb01,9cf8977d3..05dfb0133 --- a/app/controllers/api_controller.rb +++ b/app/controllers/api_controller.rb @@@ -120,12 -120,10 +120,10 @@@ class ApiController < ApplicationContro return end if node_ids.length == 0 - render :text => "", :content_type => "text/xml" + render :text => "", :content_type => "text/xml" return end - relations = Array.new - doc = OSM::API.new.get_xml_doc # get ways @@@ -170,19 -168,15 +168,15 @@@ end end - relations = Relation.find_for_nodes_and_ways(visible_nodes.keys, way_ids) + relations = visible_nodes.values.collect { |node| node.containing_relations.visible }.flatten + + way_ids.collect { |id| Way.find(id).containing_relations.visible }.flatten # we do not normally return the "other" partners referenced by an relation, # e.g. if we return a way A that is referenced by relation X, and there's # another way B also referenced, that is not returned. But we do make # an exception for cases where an relation references another *relation*; # in that case we return that as well (but we don't go recursive here) - relation_ids = relations.collect { |relation| relation.id } - if relation_ids.length > 0 - relations += Relation.find_by_sql("select e.* from current_relations e,current_relation_members em where " + - "e.visible=1 and " + - "em.id = e.id and em.member_type='relation' and em.member_id in (#{relation_ids.join(',')})") - end + relations += relations.collect { |relation| relation.containing_relations.visible }.flatten # this "uniq" may be slightly inefficient; it may be better to first collect and output # all node-related relations, then find the *not yet covered* way-related ones etc. @@@ -252,8 -246,8 +246,8 @@@ api = XML::Node.new 'api' version = XML::Node.new 'version' - version['minimum'] = '0.5'; - version['maximum'] = '0.5'; + version['minimum'] = "#{API_VERSION}"; + version['maximum'] = "#{API_VERSION}"; api << version area = XML::Node.new 'area' area['maximum'] = MAX_REQUEST_AREA.to_s; diff --combined app/controllers/application.rb index 8d082c2ca,918e4b617..68359585e --- a/app/controllers/application.rb +++ b/app/controllers/application.rb @@@ -2,6 -2,10 +2,10 @@@ # Likewise, all the methods added will be available for all controllers. class ApplicationController < ActionController::Base + if OSM_STATUS == :database_offline + session :off + end + def authorize_web if session[:user] @user = User.find(session[:user]) @@@ -39,8 -43,14 +43,14 @@@ end end + def check_database_availability(need_api = false) + if OSM_STATUS == :database_offline or (need_api and OSM_STATUS == :api_offline) + redirect_to :controller => 'site', :action => 'offline' + end + end + def check_read_availability - if API_STATUS == :offline + if OSM_STATUS == :database_offline or OSM_STATUS == :api_offline response.headers['Error'] = "Database offline for maintenance" render :nothing => true, :status => :service_unavailable return false @@@ -48,7 -58,7 +58,7 @@@ end def check_write_availability - if API_STATUS == :offline or API_STATUS == :readonly + if OSM_STATUS == :database_offline or OSM_STATUS == :api_offline or OSM_STATUS == :api_readonly response.headers['Error'] = "Database offline for maintenance" render :nothing => true, :status => :service_unavailable return false @@@ -61,7 -71,7 +71,7 @@@ # phrase from that, we can also put the error message into the status # message. For now, rails won't let us) def report_error(message) - render :nothing => true, :status => :bad_request + render :text => message, :status => :bad_request # Todo: some sort of escaping of problem characters in the message response.headers['Error'] = message end @@@ -72,8 -82,6 +82,8 @@@ privat def get_auth_data if request.env.has_key? 'X-HTTP_AUTHORIZATION' # where mod_rewrite might have put it authdata = request.env['X-HTTP_AUTHORIZATION'].to_s.split + elsif request.env.has_key? 'REDIRECT_X_HTTP_AUTHORIZATION' # mod_fcgi + authdata = request.env['REDIRECT_X_HTTP_AUTHORIZATION'].to_s.split elsif request.env.has_key? 'HTTP_AUTHORIZATION' # regular location authdata = request.env['HTTP_AUTHORIZATION'].to_s.split end diff --combined app/models/old_relation.rb index 03d5aebff,bac03c4d2..f5885f39f --- a/app/models/old_relation.rb +++ b/app/models/old_relation.rb @@@ -9,7 -9,6 +9,7 @@@ class OldRelation < ActiveRecord::Bas old_relation.user_id = relation.user_id old_relation.timestamp = relation.timestamp old_relation.id = relation.id + old_relation.version = relation.version old_relation.members = relation.members old_relation.tags = relation.tags return old_relation @@@ -92,7 -91,6 +92,7 @@@ el1['visible'] = self.visible.to_s el1['timestamp'] = self.timestamp.xmlschema el1['user'] = self.user.display_name if self.user.data_public? + el1['version'] = self.version.to_s self.old_members.each do |member| e = XML::Node.new 'member' @@@ -109,5 -107,20 +109,20 @@@ el1 << e end return el1 - end + end + + # Temporary method to match interface to nodes + def tags_as_hash + return self.tags + end + + # Temporary method to match interface to relations + def relation_members + return self.old_members + end + + # Pretend we're not in any relations + def containing_relation_members + return [] + end end diff --combined app/models/old_way.rb index 136e64793,1abb23bbb..edf66aac3 --- a/app/models/old_way.rb +++ b/app/models/old_way.rb @@@ -9,7 -9,6 +9,7 @@@ class OldWay < ActiveRecord::Bas old_way.user_id = way.user_id old_way.timestamp = way.timestamp old_way.id = way.id + old_way.version = way.version old_way.nds = way.nds old_way.tags = way.tags return old_way @@@ -95,7 -94,6 +95,7 @@@ el1['visible'] = self.visible.to_s el1['timestamp'] = self.timestamp.xmlschema el1['user'] = self.user.display_name if self.user.data_public? + el1['version'] = self.version.to_s self.old_nodes.each do |nd| # FIXME need to make sure they come back in the right order e = XML::Node.new 'nd' @@@ -110,5 -108,20 +110,20 @@@ el1 << e end return el1 - end + end + + # Temporary method to match interface to nodes + def tags_as_hash + return self.tags + end + + # Temporary method to match interface to ways + def way_nodes + return self.old_nodes + end + + # Pretend we're not in any relations + def containing_relation_members + return [] + end end diff --combined app/models/relation.rb index 984732c71,9ee118f6e..eb3b06a13 --- a/app/models/relation.rb +++ b/app/models/relation.rb @@@ -1,14 -1,17 +1,17 @@@ class Relation < ActiveRecord::Base require 'xml/libxml' + set_table_name 'current_relations' + belongs_to :user + has_many :old_relations, :foreign_key => 'id', :order => 'version' + has_many :relation_members, :foreign_key => 'id' has_many :relation_tags, :foreign_key => 'id' - has_many :old_relations, :foreign_key => 'id', :order => 'version' - - set_table_name 'current_relations' + has_many :containing_relation_members, :class_name => "RelationMember", :as => :member + has_many :containing_relations, :class_name => "Relation", :through => :containing_relation_members, :source => :relation, :extend => ObjectFinder def self.from_xml(xml, create=false) begin @@@ -16,38 -19,32 +19,38 @@@ p.string = xml doc = p.parse - relation = Relation.new - doc.find('//osm/relation').each do |pt| - if !create and pt['id'] != '0' - relation.id = pt['id'].to_i - end + return Relation.from_xml_node(pt, create) + end + rescue + return nil + end + end - if create - relation.timestamp = Time.now - relation.visible = true - else - if pt['timestamp'] - relation.timestamp = Time.parse(pt['timestamp']) - end - end + def self.from_xml_node(pt, create=false) + relation = Relation.new - pt.find('tag').each do |tag| - relation.add_tag_keyval(tag['k'], tag['v']) - end + if !create and pt['id'] != '0' + relation.id = pt['id'].to_i + end - pt.find('member').each do |member| - relation.add_member(member['type'], member['ref'], member['role']) - end + relation.version = pt['version'] + + if create + relation.timestamp = Time.now + relation.visible = true + else + if pt['timestamp'] + relation.timestamp = Time.parse(pt['timestamp']) end - rescue - relation = nil + end + + pt.find('tag').each do |tag| + relation.add_tag_keyval(tag['k'], tag['v']) + end + + pt.find('member').each do |member| + relation.add_member(member['type'], member['ref'], member['role']) end return relation @@@ -64,7 -61,6 +67,7 @@@ el1['id'] = self.id.to_s el1['visible'] = self.visible.to_s el1['timestamp'] = self.timestamp.xmlschema + el1['version'] = self.version.to_s user_display_name_cache = {} if user_display_name_cache.nil? @@@ -109,29 -105,6 +112,6 @@@ return el1 end - - # collect relationships. currently done in one big block at the end; - # may need to move this upwards if people want automatic completion of - # relationships, i.e. deliver referenced objects like we do with ways... - # FIXME: rip out the fucking SQL - def self.find_for_nodes_and_ways(node_ids, way_ids) - relations = [] - - if node_ids.length > 0 - relations += Relation.find_by_sql("select e.* from current_relations e,current_relation_members em where " + - "e.visible=1 and " + - "em.id = e.id and em.member_type='node' and em.member_id in (#{node_ids.join(',')})") - end - if way_ids.length > 0 - relations += Relation.find_by_sql("select e.* from current_relations e,current_relation_members em where " + - "e.visible=1 and " + - "em.id = e.id and em.member_type='way' and em.member_id in (#{way_ids.join(',')})") - end - - relations # if you don't do this then it returns nil and not [] - end - - # FIXME is this really needed? def members unless @members @@@ -174,12 -147,13 +154,12 @@@ def save_with_history! Relation.transaction do t = Time.now + self.version += 1 self.timestamp = t self.save! tags = self.tags - RelationTag.delete_all(['id = ?', self.id]) - tags.each do |k,v| tag = RelationTag.new tag.k = k @@@ -189,7 -163,9 +169,7 @@@ end members = self.members - RelationMember.delete_all(['id = ?', self.id]) - members.each do |n| mem = RelationMember.new mem.id = self.id @@@ -205,53 -181,50 +185,80 @@@ end end + def delete_with_history(user) + if self.visible + if RelationMember.find(:first, :joins => "INNER JOIN current_relations ON current_relations.id=current_relation_members.id", :conditions => [ "visible = 1 AND member_type='relation' and member_id=?", self.id ]) + raise OSM::APIPreconditionFailedError.new + else + self.user_id = user.id + self.tags = [] + self.members = [] + self.visible = false + save_with_history! + end + else + raise OSM::APIAlreadyDeletedError.new + end + end + + def update_from(new_relation, user) + if !new_relation.preconditions_ok? + raise OSM::APIPreconditionFailedError.new + elsif new_relation.version != version + raise OSM::APIVersionMismatchError.new(new_relation.version, version) + else + self.user_id = user.id + self.tags = new_relation.tags + self.members = new_relation.members + self.visible = true + save_with_history! + end + end + def preconditions_ok? + # These are hastables that store an id in the index of all + # the nodes/way/relations that have already been added. + # Once we know the id of the node/way/relation exists + # we check to see if it is already existing in the hashtable + # if it does, then we return false. Otherwise + # we add it to the relevant hash table, with the value true.. + # Thus if you have nodes with the ids of 50 and 1 already in the + # relation, then the hash table nodes would contain: + # => {50=>true, 1=>true} + nodes = Hash.new + ways = Hash.new + relations = Hash.new self.members.each do |m| if (m[0] == "node") n = Node.find(:first, :conditions => ["id = ?", m[1]]) unless n and n.visible return false end + if nodes[m[1]] + return false + else + nodes[m[1]] = true + end elsif (m[0] == "way") w = Way.find(:first, :conditions => ["id = ?", m[1]]) unless w and w.visible and w.preconditions_ok? return false end + if ways[m[1]] + return false + else + ways[m[1]] = true + end elsif (m[0] == "relation") e = Relation.find(:first, :conditions => ["id = ?", m[1]]) unless e and e.visible and e.preconditions_ok? return false end + if relations[m[1]] + return false + else + relations[m[1]] = true + end else return false end @@@ -261,4 -234,8 +268,8 @@@ return false end + # Temporary method to match interface to nodes + def tags_as_hash + return self.tags + end end diff --combined app/models/way.rb index ea027fb47,64b11cf67..34afc6585 --- a/app/models/way.rb +++ b/app/models/way.rb @@@ -1,15 -1,19 +1,19 @@@ class Way < ActiveRecord::Base require 'xml/libxml' + set_table_name 'current_ways' + belongs_to :user - has_many :nodes, :through => :way_nodes, :order => 'sequence_id' + has_many :old_ways, :foreign_key => 'id', :order => 'version' + has_many :way_nodes, :foreign_key => 'id', :order => 'sequence_id' - has_many :way_tags, :foreign_key => 'id' + has_many :nodes, :through => :way_nodes, :order => 'sequence_id' - has_many :old_ways, :foreign_key => 'id', :order => 'version' + has_many :way_tags, :foreign_key => 'id' - set_table_name 'current_ways' + has_many :containing_relation_members, :class_name => "RelationMember", :as => :member + has_many :containing_relations, :class_name => "Relation", :through => :containing_relation_members, :source => :relation, :extend => ObjectFinder def self.from_xml(xml, create=false) begin @@@ -17,38 -21,32 +21,38 @@@ p.string = xml doc = p.parse - way = Way.new - doc.find('//osm/way').each do |pt| - if !create and pt['id'] != '0' - way.id = pt['id'].to_i - end + return Way.from_xml_node(pt, create) + end + rescue + return nil + end + end - if create - way.timestamp = Time.now - way.visible = true - else - if pt['timestamp'] - way.timestamp = Time.parse(pt['timestamp']) - end - end + def self.from_xml_node(pt, create=false) + way = Way.new - pt.find('tag').each do |tag| - way.add_tag_keyval(tag['k'], tag['v']) - end + if !create and pt['id'] != '0' + way.id = pt['id'].to_i + end + + way.version = pt['version'] - pt.find('nd').each do |nd| - way.add_nd_num(nd['ref']) - end + if create + way.timestamp = Time.now + way.visible = true + else + if pt['timestamp'] + way.timestamp = Time.parse(pt['timestamp']) end - rescue - way = nil + end + + pt.find('tag').each do |tag| + way.add_tag_keyval(tag['k'], tag['v']) + end + + pt.find('nd').each do |nd| + way.add_nd_num(nd['ref']) end return way @@@ -76,7 -74,6 +80,7 @@@ el1['id'] = self.id.to_s el1['visible'] = self.visible.to_s el1['timestamp'] = self.timestamp.xmlschema + el1['version'] = self.version.to_s user_display_name_cache = {} if user_display_name_cache.nil? @@@ -165,12 -162,15 +169,12 @@@ t = Time.now Way.transaction do + self.version += 1 self.timestamp = t self.save! - end - WayTag.transaction do tags = self.tags - WayTag.delete_all(['id = ?', self.id]) - tags.each do |k,v| tag = WayTag.new tag.k = k @@@ -178,9 -178,13 +182,9 @@@ tag.id = self.id tag.save! end - end - WayNode.transaction do nds = self.nds - WayNode.delete_all(['id = ?', self.id]) - sequence = 1 nds.each do |n| nd = WayNode.new @@@ -189,25 -193,11 +193,25 @@@ nd.save! sequence += 1 end + + old_way = OldWay.from_way(self) + old_way.timestamp = t + old_way.save_with_dependencies! end + end - old_way = OldWay.from_way(self) - old_way.timestamp = t - old_way.save_with_dependencies! + def update_from(new_way, user) + if !new_way.preconditions_ok? + raise OSM::APIPreconditionFailedError.new + elsif new_way.version != version + raise OSM::APIVersionMismatchError.new(new_way.version, version) + else + self.user_id = user.id + self.tags = new_way.tags + self.nds = new_way.nds + self.visible = true + save_with_history! + end end def preconditions_ok? @@@ -221,13 -211,12 +225,13 @@@ return true end - # Delete the way and it's relations, but don't really delete it - set its visibility to false and update the history etc to maintain wiki-like functionality. - def delete_with_relations_and_history(user) + def delete_with_history(user) if self.visible # FIXME # this should actually delete the relations, # not just throw a PreconditionFailed if it's a member of a relation!! + + # FIXME: this should probably renamed to delete_with_history if RelationMember.find(:first, :joins => "INNER JOIN current_relations ON current_relations.id=current_relation_members.id", :conditions => [ "visible = 1 AND member_type='way' and member_id=?", self.id]) raise OSM::APIPreconditionFailedError @@@ -245,8 -234,6 +249,8 @@@ end # delete a way and it's nodes that aren't part of other ways, with history + + # FIXME: merge the potlatch code to delete the relations def delete_with_relations_and_nodes_and_history(user) node_ids = self.nodes.collect {|node| node.id } @@@ -267,7 -254,12 +271,12 @@@ self.user_id = user.id - self.delete_with_relations_and_history(user) + self.delete_with_history(user) end + + # Temporary method to match interface to nodes + def tags_as_hash + return self.tags + end end diff --combined config/environment.rb index 8aaab7302,495f94d80..fb7573d2a --- a/config/environment.rb +++ b/config/environment.rb @@@ -5,18 -5,22 +5,22 @@@ ENV['RAILS_ENV'] ||= 'production' # Specifies gem version of Rails to use when vendor/rails is not present - # DO NOT BUMP THIS TO 2.0.2 AS THE LIVE SERVERS CAN'T RUN THAT - RAILS_GEM_VERSION = '2.0.1' unless defined? RAILS_GEM_VERSION + RAILS_GEM_VERSION = '2.0.2' unless defined? RAILS_GEM_VERSION # Set the server URL SERVER_URL = ENV['OSM_SERVER_URL'] || 'www.openstreetmap.org' # Application constants needed for routes.rb - must go before Initializer call -API_VERSION = ENV['OSM_API_VERSION'] || '0.5' +API_VERSION = ENV['OSM_API_VERSION'] || '0.6' - # Set to :readonly to put the API in read-only mode or :offline to - # take it completely offline - API_STATUS = :online + # Set application status - possible settings are: + # + # :online - online and operating normally + # :api_readonly - site online but API in read-only mode + # :api_offline - site online but API offline + # :database_offline - database offline with site in emergency mode + # + OSM_STATUS = :online # Bootstrap the Rails environment, frameworks, and default configuration require File.join(File.dirname(__FILE__), 'boot') @@@ -29,7 -33,9 +33,9 @@@ Rails::Initializer.run do |config # Skip frameworks you're not going to use (only works if using vendor/rails). # To use Rails without a database, you must remove the Active Record framework - # config.frameworks -= [ :active_record, :active_resource, :action_mailer ] + if OSM_STATUS == :database_offline + config.frameworks -= [ :active_record ] + end # Only load the plugins named here, in the order given. By default, all plugins # in vendor/plugins are loaded in alphabetical order. diff --combined config/routes.rb index 5ca4f5a53,854e7f003..592178474 --- a/config/routes.rb +++ b/config/routes.rb @@@ -1,11 -1,6 +1,11 @@@ ActionController::Routing::Routes.draw do |map| # API + map.connect "api/#{API_VERSION}/changeset/create", :controller => 'changeset', :action => 'create' + map.connect "api/#{API_VERSION}/changeset/upload", :controller => 'changeset', :action => 'upload' + map.connect "api/#{API_VERSION}/changeset/:id", :controller => 'changeset', :action => 'read', :id => /\d+/ + map.connect "api/#{API_VERSION}/changeset/:id/close", :controller => 'changeset', :action => 'close', :id =>/\d+/ + map.connect "api/#{API_VERSION}/node/create", :controller => 'node', :action => 'create' map.connect "api/#{API_VERSION}/node/:id/ways", :controller => 'way', :action => 'ways_for_node', :id => /\d+/ map.connect "api/#{API_VERSION}/node/:id/relations", :controller => 'relation', :action => 'relations_for_node', :id => /\d+/ @@@ -59,10 -54,19 +59,20 @@@ # Potlatch API + map.connect "api/0.5/amf", :controller =>'amf', :action =>'talk' map.connect "api/#{API_VERSION}/amf", :controller =>'amf', :action =>'talk' map.connect "api/#{API_VERSION}/swf/trackpoints", :controller =>'swf', :action =>'trackpoints' + # Data browsing + map.connect '/browse', :controller => 'browse', :action => 'index' + map.connect '/browse/start', :controller => 'browse', :action => 'start' + map.connect '/browse/way/:id', :controller => 'browse', :action => 'way', :id => /\d+/ + map.connect '/browse/way/:id/history', :controller => 'browse', :action => 'way_history', :id => /\d+/ + map.connect '/browse/node/:id', :controller => 'browse', :action => 'node', :id => /\d+/ + map.connect '/browse/node/:id/history', :controller => 'browse', :action => 'node_history', :id => /\d+/ + map.connect '/browse/relation/:id', :controller => 'browse', :action => 'relation', :id => /\d+/ + map.connect '/browse/relation/:id/history', :controller => 'browse', :action => 'relation_history', :id => /\d+/ + # web site map.connect '/', :controller => 'site', :action => 'index' @@@ -70,6 -74,7 +80,7 @@@ map.connect '/export', :controller => 'site', :action => 'export' map.connect '/login', :controller => 'user', :action => 'login' map.connect '/logout', :controller => 'user', :action => 'logout' + map.connect '/offline', :controller => 'site', :action => 'offline' map.connect '/user/new', :controller => 'user', :action => 'new' map.connect '/user/save', :controller => 'user', :action => 'save' map.connect '/user/confirm', :controller => 'user', :action => 'confirm' @@@ -150,7 -155,6 +161,6 @@@ map.connect '/message/read/:message_id', :controller => 'message', :action => 'read' map.connect '/message/mark/:message_id', :controller => 'message', :action => 'mark' map.connect '/message/reply/:message_id', :controller => 'message', :action => 'reply' - map.connect '/message/delete/:message_id', :controller => 'message', :action => 'destroy' # fall through map.connect ':controller/:id/:action'