# ** FIXME: refactor to reduce duplication of code across read/write
def amf_read
- req=StringIO.new(request.raw_post+0.chr)# Get POST data as request
- # (cf http://www.ruby-forum.com/topic/122163)
- req.read(2) # Skip version indicator and client ID
- results={} # Results of each body
+ req=StringIO.new(request.raw_post+0.chr)# Get POST data as request
+ # (cf http://www.ruby-forum.com/topic/122163)
+ req.read(2) # Skip version indicator and client ID
+ results={} # Results of each body
- # Parse request
+ # Parse request
- headers=AMF.getint(req) # Read number of headers
+ headers=AMF.getint(req) # Read number of headers
- headers.times do # Read each header
- name=AMF.getstring(req) # |
- req.getc # | skip boolean
- value=AMF.getvalue(req) # |
- header["name"]=value # |
- end
-
- bodies=AMF.getint(req) # Read number of bodies
- bodies.times do # Read each body
- message=AMF.getstring(req) # | get message name
- index=AMF.getstring(req) # | get index in response sequence
- bytes=AMF.getlong(req) # | get total size in bytes
- args=AMF.getvalue(req) # | get response (probably an array)
-
- case message
- when 'getpresets'; results[index]=AMF.putdata(index,getpresets())
- when 'whichways'; results[index]=AMF.putdata(index,whichways(*args))
- when 'whichways_deleted'; results[index]=AMF.putdata(index,whichways_deleted(*args))
- when 'getway'; results[index]=AMF.putdata(index,getway(args[0].to_i))
- when 'getrelation'; results[index]=AMF.putdata(index,getrelation(args[0].to_i))
- when 'getway_old'; results[index]=AMF.putdata(index,getway_old(args[0].to_i,args[1].to_i))
- when 'getway_history'; results[index]=AMF.putdata(index,getway_history(args[0].to_i))
- when 'getnode_history'; results[index]=AMF.putdata(index,getnode_history(args[0].to_i))
- when 'findrelations'; results[index]=AMF.putdata(index,findrelations(*args))
- when 'getpoi'; results[index]=AMF.putdata(index,getpoi(*args))
- end
- end
+ headers.times do # Read each header
+ name=AMF.getstring(req) # |
+ req.getc # | skip boolean
+ value=AMF.getvalue(req) # |
+ header["name"]=value # |
+ end
+
+ bodies=AMF.getint(req) # Read number of bodies
+ bodies.times do # Read each body
+ message=AMF.getstring(req) # | get message name
+ index=AMF.getstring(req) # | get index in response sequence
+ bytes=AMF.getlong(req) # | get total size in bytes
+ args=AMF.getvalue(req) # | get response (probably an array)
+ logger.info("Executing AMF #{message}:#{index}")
+
+ case message
+ when 'getpresets'; results[index]=AMF.putdata(index,getpresets())
+ when 'whichways'; results[index]=AMF.putdata(index,whichways(*args))
+ when 'whichways_deleted'; results[index]=AMF.putdata(index,whichways_deleted(*args))
+ when 'getway'; results[index]=AMF.putdata(index,getway(args[0].to_i))
+ when 'getrelation'; results[index]=AMF.putdata(index,getrelation(args[0].to_i))
+ when 'getway_old'; results[index]=AMF.putdata(index,getway_old(args[0].to_i,args[1]))
+ when 'getway_history'; results[index]=AMF.putdata(index,getway_history(args[0].to_i))
+ when 'getnode_history'; results[index]=AMF.putdata(index,getnode_history(args[0].to_i))
+ when 'findgpx'; results[index]=AMF.putdata(index,findgpx(*args))
+ when 'findrelations'; results[index]=AMF.putdata(index,findrelations(*args))
+ when 'getpoi'; results[index]=AMF.putdata(index,getpoi(*args))
+ end
+ end
+ logger.info("encoding AMF results")
sendresponse(results)
end
end
# Get a way including nodes and tags.
- # Returns 0 (success), a Potlatch-style array of points, and a hash of tags.
+ # Returns the way id, a Potlatch-style array of points, a hash of tags, and the version number.
def getway(wayid) #:doc:
- if POTLATCH_USE_SQL then
- points = sql_get_nodes_in_way(wayid)
- tags = sql_get_tags_in_way(wayid)
- else
- # Ideally we would do ":include => :nodes" here but if we do that
- # then rails only seems to return the first copy of a node when a
- # way includes a node more than once
- way = Way.find(wayid)
- points = way.nodes.collect do |node|
- nodetags=node.tags_as_hash
- nodetags.delete('created_by')
- [node.lon, node.lat, node.id, nodetags]
- end
- tags = way.tags
- end
-
- [wayid, points, tags]
+ if POTLATCH_USE_SQL then
+ points = sql_get_nodes_in_way(wayid)
+ tags = sql_get_tags_in_way(wayid)
+ version = sql_get_way_version(wayid)
+ else
+ # Ideally we would do ":include => :nodes" here but if we do that
+ # then rails only seems to return the first copy of a node when a
+ # way includes a node more than once
+ begin
+ way = Way.find(wayid)
+ rescue ActiveRecord::RecordNotFound
+ return [wayid,[],{}]
+ end
+
+ # check case where way has been deleted or doesn't exist
+ return [wayid,[],{}] if way.nil? or !way.visible
+
+ points = way.nodes.collect do |node|
+ nodetags=node.tags
+ nodetags.delete('created_by')
+ [node.lon, node.lat, node.id, nodetags, node.version]
+ end
+ tags = way.tags
+ version = way.version
+ end
+
+ [wayid, points, tags, version]
end
--
++
# Get an old version of a way, and all constituent nodes.
#
- # For undelete (version=0), always uses the most recent version of each node,
- # even if it's moved. For revert (version=1+), uses the node in existence
+ # For undelete (version<0), always uses the most recent version of each node,
+ # even if it's moved. For revert (version >= 0), uses the node in existence
# at the time, generating a new id if it's still visible and has been moved/
# retagged.
-
- def getway_old(id, version) #:doc:
- if version < 0
- old_way = OldWay.find(:first, :conditions => ['visible = 1 AND id = ?', id], :order => 'version DESC')
- points = old_way.get_nodes_undelete
- else
- old_way = OldWay.find(:first, :conditions => ['id = ? AND version = ?', id, version])
- points = old_way.get_nodes_revert
- end
-
- old_way.tags['history'] = "Retrieved from v#{old_way.version}"
-
- [0, id, points, old_way.tags, old_way.version]
+ #
+ # Returns:
+ # 0. success code,
+ # 1. id,
+ # 2. array of points,
+ # 3. hash of tags,
+ # 4. version,
+ # 5. is this the current, visible version? (boolean)
+
+ def getway_old(id, timestamp) #:doc:
+ if timestamp == ''
+ # undelete
+ old_way = OldWay.find(:first, :conditions => ['visible = ? AND id = ?', true, id], :order => 'version DESC')
+ points = old_way.get_nodes_undelete unless old_way.nil?
+ else
+ begin
+ # revert
+ timestamp = DateTime.strptime(timestamp.to_s, "%d %b %Y, %H:%M:%S")
+ old_way = OldWay.find(:first, :conditions => ['id = ? AND timestamp <= ?', id, timestamp], :order => 'timestamp DESC')
+ unless old_way.nil?
+ points = old_way.get_nodes_revert(timestamp)
+ if !old_way.visible
+ return [-1, "Sorry, the way was deleted at that time - please revert to a previous version."]
+ end
+ end
+ rescue ArgumentError
+ # thrown by date parsing method. leave old_way as nil for
+ # the superb error handler below.
+ end
+ end
+
+ if old_way.nil?
+ # *** FIXME: shouldn't this be returning an error?
+ return [-1, id, [], {}, -1,0]
+ else
+ curway=Way.find(id)
+ old_way.tags['history'] = "Retrieved from v#{old_way.version}"
+ return [0, id, points, old_way.tags, curway.version, (curway.version==old_way.version and curway.visible)]
+ end
end
- # Find history of a way. Returns 'way', id, and
- # an array of previous versions.
+ # Find history of a way.
+ # Returns 'way', id, and an array of previous versions:
+ # - formerly [old_way.version, old_way.timestamp.strftime("%d %b %Y, %H:%M"), old_way.visible ? 1 : 0, user, uid]
+ # - now [timestamp,user,uid]
+ #
+ # Heuristic: Find all nodes that have ever been part of the way;
+ # get a list of their revision dates; add revision dates of the way;
+ # sort and collapse list (to within 2 seconds); trim all dates before the
+ # start date of the way.
def getway_history(wayid) #:doc:
- history = Way.find(wayid).old_ways.reverse.collect do |old_way|
- user = old_way.user.data_public? ? old_way.user.display_name : 'anonymous'
- uid = old_way.user.data_public? ? old_way.user.id : 0
- [old_way.version, old_way.timestamp.strftime("%d %b %Y, %H:%M"), old_way.visible ? 1 : 0, user, uid]
- end
- ['way',wayid,history]
+ begin
+ # Find list of revision dates for way and all constituent nodes
+ revdates=[]
+ revusers={}
+ Way.find(wayid).old_ways.collect do |a|
+ revdates.push(a.timestamp)
+ unless revusers.has_key?(a.timestamp.to_i) then revusers[a.timestamp.to_i]=change_user(a) end
+ a.nds.each do |n|
+ Node.find(n).old_nodes.collect do |o|
+ revdates.push(o.timestamp)
+ unless revusers.has_key?(o.timestamp.to_i) then revusers[o.timestamp.to_i]=change_user(o) end
+ end
+ end
+ end
+ waycreated=revdates[0]
+ revdates.uniq!
+ revdates.sort!
+ revdates.reverse!
+
+ # Remove any dates (from nodes) before first revision date of way
+ revdates.delete_if { |d| d<waycreated }
+ # Remove any elements where 2 seconds doesn't elapse before next one
+ revdates.delete_if { |d| revdates.include?(d+1) or revdates.include?(d+2) }
+ # Collect all in one nested array
+ revdates.collect! {|d| [d.strftime("%d %b %Y, %H:%M:%S")] + revusers[d.to_i] }
+
+ return ['way',wayid,revdates]
+ rescue ActiveRecord::RecordNotFound
+ return ['way', wayid, []]
+ end
end
-
- # Find history of a node. Returns 'node', id, and
- # an array of previous versions.
+
+ # Find history of a node. Returns 'node', id, and an array of previous versions as above.
def getnode_history(nodeid) #:doc:
- history = Node.find(nodeid).old_nodes.reverse.collect do |old_node|
- user = old_node.user.data_public? ? old_node.user.display_name : 'anonymous'
- uid = old_node.user.data_public? ? old_node.user.id : 0
- [old_node.timestamp.to_i, old_node.timestamp.strftime("%d %b %Y, %H:%M"), old_node.visible ? 1 : 0, user, uid]
- end
+ begin
+ history = Node.find(nodeid).old_nodes.reverse.collect do |old_node|
+ [old_node.timestamp.strftime("%d %b %Y, %H:%M:%S")] + change_user(old_node)
+ end
+ return ['node', nodeid, history]
+ rescue ActiveRecord::RecordNotFound
+ return ['node', nodeid, []]
+ end
+ end
- ['node',nodeid,history]
+ def change_user(obj)
+ user_object = obj.changeset.user
+ user = user_object.data_public? ? user_object.display_name : 'anonymous'
+ uid = user_object.data_public? ? user_object.id : 0
+ [user,uid]
+ end
+
+ # Find GPS traces with specified name/id.
+ # Returns array listing GPXs, each one comprising id, name and description.
+
+ def findgpx(searchterm, usertoken)
+ user = getuser(usertoken)
+ if !uid then return -1,"You must be logged in to search for GPX traces." end
+
+ gpxs = []
+ if searchterm.to_i>0 then
+ gpx = Trace.find(searchterm.to_i, :conditions => ["visible=? AND (public=? OR user_id=?)",true,true,user.id] )
+ if gpx then
+ gpxs.push([gpx.id, gpx.name, gpx.description])
+ end
+ else
+ Trace.find(:all, :limit => 21, :conditions => ["visible=? AND (public=? OR user_id=?) AND MATCH(name) AGAINST (?)",true,true,user.id,searchterm] ).each do |gpx|
+ gpxs.push([gpx.id, gpx.name, gpx.description])
+ end
+ end
+ gpxs
end
# Get a relation with all tags and members.