useful and the code will need some heavy refactoring.
class ChangesetController < ApplicationController
require 'xml/libxml'
- before_filter :authorize, :only => [:create, :update, :delete]
- before_filter :check_write_availability, :only => [:create, :update, :delete]
- before_filter :check_read_availability, :except => [:create, :update, :delete]
+ before_filter :authorize, :only => [:create, :update, :delete, :upload]
+ before_filter :check_write_availability, :only => [:create, :update, :delete, :upload]
+ before_filter :check_read_availability, :except => [:create, :update, :delete, :upload]
+ after_filter :compress_output
# Create a changeset from XML.
def create
render :nothing => true, :status => :method_not_allowed
end
end
+
+ def create_prim(ids, prim, nd)
+ prim.version = 0
+ prim.user_id = @user.id
+ prim.visible = true
+ prim.save_with_history!
+
+ ids[nd['id'].to_i] = prim.id
+ end
+
+ def fix_way(w, node_ids)
+ w.nds.each { |nd|
+ new_id = node_ids[nd.node_id]
+ nd.node_id = new_id unless new_id.nil?
+ }
+ end
+
+ def fix_rel(r, ids)
+ r.members.each { |memb|
+ new_id = ids[memb.member_type][memb.member_id]
+ nd.member_id = new_id unless new_id.nil?
+ }
+ end
+
+ def upload
+ if not request.put?
+ render :nothing => true, :status => :method_not_allowed
+ return
+ end
+
+ # FIXME: this should really be done without loading the whole XML file
+ # into memory.
+ p = XML::Parser.new
+ p.string = request.raw_post
+ doc = p.parse
+
+ node_ids, way_ids, rel_ids = {}, {}, {}
+ ids = {"node"=>node_ids, "way"=>way_ids, "relation"=>rel_ids}
+
+ Changeset.transaction do
+ doc.find('//osm/create/node').each do |nd|
+ create_prim node_ids, Node.from_xml_node(nd, true), nd
+ end
+ doc.find('//osm/create/way').each do |nd|
+ way = Way.from_xml_node(nd, true)
+ raise OSM::APIPreconditionFailedError.new if !way.preconditions_ok?
+ create_prim way_ids, fix_way(way, node_ids), nd
+ end
+ doc.find('//osm/create/relation').each do |nd|
+ relation = Relation.from_xml_node(nd, true)
+ raise OSM::APIPreconditionFailedError.new if !way.preconditions_ok?
+ create_prim relation_ids, fix_rel(relation, ids), nd
+ end
+
+ doc.find('//osm/modify/node').each do |nd|
+ unless NodeController.update_internal nil, Node.from_xml_node(nd)
+ raise OSM::APIPreconditionFailedError.new
+ end
+ end
+ doc.find('//osm/modify/way').each do |nd|
+ unless WayController.update_internal nil, fix_way(Way.from_xml_node(nd), node_ids)
+ raise OSM::APIPreconditionFailedError.new
+ end
+ end
+ doc.find('//osm/modify/relation').each do |nd|
+ unless RelationController.update_internal nil, fix_rel(Relation.from_xml_node(nd), ids)
+ raise OSM::APIPreconditionFailedError.new
+ end
+ end
+
+ doc.find('//osm/delete/node').each do |nd|
+ unless NodeController.delete_internal nil, Node.from_xml_node(n)
+ raise OSM::APIPreconditionFailedError.new
+ end
+ end
+ doc.find('//osm/delete/way').each do |nd|
+ Way.from_xml_node(nd).delete_with_relations_and_history(@user)
+ end
+ doc.find('//osm/delete/relation').each do |nd|
+ unless RelationController.delete_internal nil, fix_rel(Relation.from_xml_node(nd), ids)
+ raise OSM::APIPreconditionFailedError.new
+ end
+ end
+ end
+
+ render :text => "Ok, Fine. Upload worked without errors.\n", :status => 200
+ end
end
new_node = Node.from_xml(request.raw_post)
if new_node and new_node.id == node.id
- node.user_id = @user.id
- node.latitude = new_node.latitude
- node.longitude = new_node.longitude
- node.tags = new_node.tags
- node.visible = true
- node.save_with_history!
-
+ update_internal node, new_node
render :nothing => true
else
render :nothing => true, :status => :bad_request
end
end
+ def update_internal(node, new_node)
+ node = Node.find(new_node.id) if node.nil?
+
+ node.user_id = @user.id
+ node.latitude = new_node.latitude
+ node.longitude = new_node.longitude
+ node.tags = new_node.tags
+ node.visible = true
+ node.save_with_history!
+
+ return true
+ end
+
# Delete a node. Doesn't actually delete it, but retains its history in a wiki-like way.
# FIXME remove all the fricking SQL
def delete
begin
node = Node.find(params[:id])
- if node.visible
- if WayNode.find(:first, :joins => "INNER JOIN current_ways ON current_ways.id = current_way_nodes.id", :conditions => [ "current_ways.visible = 1 AND current_way_nodes.node_id = ?", node.id ])
- render :text => "", :status => :precondition_failed
- elsif RelationMember.find(:first, :joins => "INNER JOIN current_relations ON current_relations.id=current_relation_members.id", :conditions => [ "visible = 1 AND member_type='node' and member_id=?", params[:id]])
- render :text => "", :status => :precondition_failed
- else
- node.user_id = @user.id
- node.visible = 0
- node.save_with_history!
-
- render :nothing => true
- end
+ res = delete_internal(node)
+ unless res
+ render :text => "", :status => :precondition_failed
else
- render :text => "", :status => :gone
+ render :text => "", :status => res
end
rescue ActiveRecord::RecordNotFound
render :nothing => true, :status => :not_found
end
end
+ def delete_internal(node)
+ if node.visible
+ if WayNode.find(:first, :joins => "INNER JOIN current_ways ON current_ways.id = current_way_nodes.id", :conditions => [ "current_ways.visible = 1 AND current_way_nodes.node_id = ?", node.id ])
+ return false
+ elsif RelationMember.find(:first, :joins => "INNER JOIN current_relations ON current_relations.id=current_relation_members.id", :conditions => [ "visible = 1 AND member_type='node' and member_id=?", params[:id]])
+ return false
+ else
+ node.user_id = @user.id
+ node.visible = 0
+ node.save_with_history!
+
+ return :ok
+ end
+ else
+ return :gone
+ end
+ end
+
# WTF does this do?
def nodes
ids = params['nodes'].split(',').collect { |n| n.to_i }
begin
relation = Relation.find(params[:id])
- if relation.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=?", params[:id]])
- render :text => "", :status => :precondition_failed
- else
- relation.user_id = @user.id
- relation.tags = []
- relation.members = []
- relation.visible = false
- relation.save_with_history!
-
- render :nothing => true
- end
+ res = delete_internal(node)
+ unless res
+ render :text => "", :status => :precondition_failed
else
- render :text => "", :status => :gone
+ render :text => "", :status => res
end
rescue ActiveRecord::RecordNotFound
render :nothing => true, :status => :not_found
end
end
+ def delete_internal(relation)
+ if relation.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=?", params[:id]])
+ return false
+ else
+ relation.user_id = @user.id
+ relation.tags = []
+ relation.members = []
+ relation.visible = false
+ relation.save_with_history!
+
+ return :ok
+ end
+ else
+ return :gone
+ end
+ end
+
# -----------------------------------------------------------------
# full
#
new_way = Way.from_xml(request.raw_post)
if new_way and new_way.id == way.id
- if !new_way.preconditions_ok?
+ unless update_internal(way, new_way)
render :text => "", :status => :precondition_failed
else
- way.user_id = @user.id
- way.tags = new_way.tags
- way.nds = new_way.nds
- way.visible = true
- way.save_with_history!
-
render :nothing => true
end
else
end
end
+ def update_internal way, new_way
+ way = Way.find(new_way.id) if way.nil?
+
+ if !new_way.preconditions_ok?
+ return false
+ else
+ way.user_id = @user.id
+ way.tags = new_way.tags
+ way.nds = new_way.nds
+ way.visible = true
+ way.save_with_history!
+
+ return true
+ end
+ end
+
# This is the API call to delete a way
def delete
begin
p = XML::Parser.new
p.string = xml
doc = p.parse
-
- node = Node.new
doc.find('//osm/node').each do |pt|
- node.lat = pt['lat'].to_f
- node.lon = pt['lon'].to_f
-
- return nil unless node.in_world?
+ return Node.from_xml_node(pt, create)
+ end
+ rescue
+ return nil
+ end
+ end
- unless create
- if pt['id'] != '0'
- node.id = pt['id'].to_i
- end
- end
+ def self.from_xml_node(pt, create=false)
+ node = Node.new
- node.visible = pt['visible'] and pt['visible'] == 'true'
+ node.lat = pt['lat'].to_f
+ node.lon = pt['lon'].to_f
- if create
- node.timestamp = Time.now
- else
- if pt['timestamp']
- node.timestamp = Time.parse(pt['timestamp'])
- end
- end
+ return nil unless node.in_world?
- tags = []
+ unless create
+ if pt['id'] != '0'
+ node.id = pt['id'].to_i
+ end
+ end
- pt.find('tag').each do |tag|
- node.add_tag_key_val(tag['k'],tag['v'])
- end
+ node.visible = pt['visible'] and pt['visible'] == 'true'
+ if create
+ node.timestamp = Time.now
+ else
+ if pt['timestamp']
+ node.timestamp = Time.parse(pt['timestamp'])
end
- rescue
- node = nil
+ end
+
+ tags = []
+
+ pt.find('tag').each do |tag|
+ node.add_tag_key_val(tag['k'],tag['v'])
end
return node
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
+ relation = Relation.from_xml_node pt, create
+ end
+ rescue
+ relation = nil
+ end
- if create
- relation.timestamp = Time.now
- relation.visible = true
- else
- if pt['timestamp']
- relation.timestamp = Time.parse(pt['timestamp'])
- end
- end
+ return relation
+ end
- pt.find('tag').each do |tag|
- relation.add_tag_keyval(tag['k'], tag['v'])
- end
+ def self.from_xml_node(pt, create=false)
+ relation = Relation.new
- pt.find('member').each do |member|
- relation.add_member(member['type'], member['ref'], member['role'])
- end
+ if !create and pt['id'] != '0'
+ relation.id = pt['id'].to_i
+ end
+
+ 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
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
+ way = Way.from_xml_node pt, create
+ end
+ rescue
+ way = nil
+ end
- if create
- way.timestamp = Time.now
- way.visible = true
- else
- if pt['timestamp']
- way.timestamp = Time.parse(pt['timestamp'])
- end
- end
+ return way
+ end
- pt.find('tag').each do |tag|
- way.add_tag_keyval(tag['k'], tag['v'])
- end
+ def self.from_xml_node(pt, create=false)
+ way = Way.new
- pt.find('nd').each do |nd|
- way.add_nd_num(nd['ref'])
- end
+ if !create and pt['id'] != '0'
+ way.id = pt['id'].to_i
+ 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
# 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}/node/create", :controller => 'node', :action => 'create'
map.connect "api/#{API_VERSION}/node/:id/ways", :controller => 'way', :action => 'ways_for_node', :id => /\d+/