Merge api06 branch to trunk.
authorTom Hughes <tom@compton.nu>
Mon, 20 Apr 2009 09:12:03 +0000 (09:12 +0000)
committerTom Hughes <tom@compton.nu>
Mon, 20 Apr 2009 09:12:03 +0000 (09:12 +0000)
1  2 
app/controllers/amf_controller.rb
app/controllers/export_controller.rb
app/views/user/view.rhtml

index 7f85280b79d29d5b320460d01f028f14727a3fd0,de3c7583b42d9977f255a581afbc5fe9265121b2..b0b3f13cf6c1947914caa7583e3fef58ff4bc392
@@@ -36,42 -48,45 +48,45 @@@ class AmfController < ApplicationContro
    # ** 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.
index a773d4b72d2c177cadad071b0de2a77777f3f8f0,754cc4b8216b15acd900d3646cdb399c31d6faba..ab25fcbc67a2ed407955953287d3550c99c46812
@@@ -7,13 -8,18 +8,18 @@@ class ExportController < ApplicationCon
      format = params[:format]
  
      if format == "osm"
+       #redirect to API map get
        redirect_to "http://api.openstreetmap.org/api/#{API_VERSION}/map?bbox=#{bbox}"
 -      
++
      elsif format == "mapnik"
+       #redirect to a special 'export' cgi script
        format = params[:mapnik_format]
        scale = params[:mapnik_scale]
  
        redirect_to "http://tile.openstreetmap.org/cgi-bin/export?bbox=#{bbox}&scale=#{scale}&format=#{format}"
 -      
++
      elsif format == "osmarender"
+       #redirect to the t@h 'MapOf' service
        format = params[:osmarender_format]
        zoom = params[:osmarender_zoom].to_i
        width = bbox.slippy_width(zoom).to_i
index 438de836d463be146c4364213471b9674fb1244c,a62be6a55d94ad9a9a35a0b861dc78f6cae71b13..c76ca23ae620055bdbe1db9aca8569bfaf371468
      <% end %>
    <% end %>
  <% end %>
 -<% end %>
+ <br/>
+ <br/>
+ <% if @user and @this_user.id == @user.id %>
+ <%= link_to 'change your settings', :controller => 'user', :action => 'account', :display_name => @user.display_name %>
++<% end %>