# OSM database takes place using this controller. Messages are
# encoded in the Actionscript Message Format (AMF).
#
-# Helper functions are in /lib/potlatch.
+# Helper functions are in /lib/potlatch.rb
#
# Author:: editions Systeme D / Richard Fairhurst 2004-2008
# Licence:: public domain.
# from the AMF message), each method generally takes arguments in the order
# they were sent by the Potlatch SWF. Do not assume typing has been preserved.
# Methods all return an array to the SWF.
+#
+# == API 0.6
+#
+# Note that this requires a patched version of composite_primary_keys 1.1.0
+# (see http://groups.google.com/group/compositekeys/t/a00e7562b677e193)
+# if you are to run with POTLATCH_USE_SQL=false .
#
# == Debugging
#
# return(-1,"message") <-- just puts up a dialogue
# return(-2,"message") <-- also asks the user to e-mail me
#
-# To write to the Rails log, use RAILS_DEFAULT_LOGGER.info("message").
+# To write to the Rails log, use logger.info("message").
+
+# Remaining issues:
+# * version conflict when POIs and ways are reverted
class AmfController < ApplicationController
require 'stringio'
include Potlatch
+ # Help methods for checking boundary sanity and area size
+ include MapBoundary
+
session :off
- before_filter :check_write_availability
+ before_filter :check_api_writable
# Main AMF handlers: process the raw AMF string (using AMF library) and
# calls each action (private method) accordingly.
# ** 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
-
- # Parse request
-
- 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
+ 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
+
+ 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)
+ 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
def amf_write
- req=StringIO.new(request.raw_post+0.chr)
- req.read(2)
- results={}
- renumberednodes={} # Shared across repeated putways
- renumberedways={} # Shared across repeated putways
-
- 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 'putway'; r=putway(renumberednodes,*args)
+ req=StringIO.new(request.raw_post+0.chr)
+ req.read(2)
+ results={}
+ renumberednodes={} # Shared across repeated putways
+ renumberedways={} # Shared across repeated putways
+
+ 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)
+
+ logger.info("Executing AMF #{message}:#{index}")
+ case message
+ when 'putway'; r=putway(renumberednodes,*args)
renumberednodes=r[3]
- if r[1] != r[2]
- renumberedways[r[1]] = r[2]
- end
+ if r[1] != r[2] then renumberedways[r[1]] = r[2] end
results[index]=AMF.putdata(index,r)
- when 'putrelation'; results[index]=AMF.putdata(index,putrelation(renumberednodes, renumberedways, *args))
- when 'deleteway'; results[index]=AMF.putdata(index,deleteway(args[0],args[1].to_i))
- when 'putpoi'; results[index]=AMF.putdata(index,putpoi(*args))
- end
- end
+ when 'putrelation'; results[index]=AMF.putdata(index,putrelation(renumberednodes, renumberedways, *args))
+ when 'deleteway'; results[index]=AMF.putdata(index,deleteway(*args))
+ when 'putpoi'; r=putpoi(*args)
+ if r[1] != r[2] then renumberednodes[r[1]] = r[2] end
+ results[index]=AMF.putdata(index,r)
+ when 'startchangeset'; results[index]=AMF.putdata(index,startchangeset(*args))
+ end
+ end
+ logger.info("encoding AMF results")
sendresponse(results)
end
private
+ # Start new changeset
+
+ def startchangeset(usertoken, cstags, closeid, closecomment)
+ user = getuser(usertoken)
+ if !user then return -1,"You are not logged in, so Potlatch can't write any changes to the database." end
+
+ # close previous changeset and add comment
+ if closeid
+ cs = Changeset.find(closeid)
+ cs.set_closed_time_now
+ if cs.user_id!=user.id
+ return -2,"You cannot close that changeset because you're not the person who opened it."
+ elsif closecomment.empty?
+ cs.save!
+ else
+ cs.tags['comment']=closecomment
+ cs.save_with_tags!
+ end
+ end
+
+ # open a new changeset
+ cs = Changeset.new
+ cs.tags = cstags
+ cs.user_id = user.id
+ # smsm1 doesn't like the next two lines and thinks they need to be abstracted to the model more/better
+ cs.created_at = Time.now.getutc
+ cs.closed_at = cs.created_at + Changeset::IDLE_TIMEOUT
+ cs.save_with_tags!
+ return [0,cs.id]
+ end
+
# Return presets (default tags, localisation etc.):
# uses POTLATCH_PRESETS global, set up in OSM::Potlatch.
def getpresets() #:doc:
- return POTLATCH_PRESETS
+ return POTLATCH_PRESETS
end
+ ##
# Find all the ways, POI nodes (i.e. not part of ways), and relations
# in a given bounding box. Nodes are returned in full; ways and relations
# are IDs only.
-
+ #
+ # return is of the form:
+ # [error_code,
+ # [[way_id, way_version], ...],
+ # [[node_id, lat, lon, [tags, ...], node_version], ...],
+ # [[rel_id, rel_version], ...]]
+ # where the ways are any visible ways which refer to any visible
+ # nodes in the bbox, nodes are any visible nodes in the bbox but not
+ # used in any way, rel is any relation which refers to either a way
+ # or node that we're returning.
def whichways(xmin, ymin, xmax, ymax) #:doc:
- enlarge = [(xmax-xmin)/8,0.01].min
- xmin -= enlarge; ymin -= enlarge
- xmax += enlarge; ymax += enlarge
-
- if POTLATCH_USE_SQL then
- way_ids = sql_find_way_ids_in_area(xmin, ymin, xmax, ymax)
- points = sql_find_pois_in_area(xmin, ymin, xmax, ymax)
- relation_ids = sql_find_relations_in_area_and_ways(xmin, ymin, xmax, ymax, way_ids)
- else
- # find the way ids in an area
- nodes_in_area = Node.find_by_area(ymin, xmin, ymax, xmax, :conditions => "current_nodes.visible = 1", :include => :ways)
- way_ids = nodes_in_area.collect { |node| node.way_ids }.flatten.uniq
-
- # find the node ids in an area that aren't part of ways
- nodes_not_used_in_area = nodes_in_area.select { |node| node.ways.empty? }
- points = nodes_not_used_in_area.collect { |n| [n.id, n.lon, n.lat, n.tags_as_hash] }
-
- # find the relations used by those nodes and ways
- relations = Relation.find_for_nodes(nodes_in_area.collect { |n| n.id }, :conditions => "visible = 1") +
- Relation.find_for_ways(way_ids, :conditions => "visible = 1")
- relation_ids = relations.collect { |relation| relation.id }.uniq
- end
-
- [way_ids, points, relation_ids]
+ enlarge = [(xmax-xmin)/8,0.01].min
+ xmin -= enlarge; ymin -= enlarge
+ xmax += enlarge; ymax += enlarge
+
+ # check boundary is sane and area within defined
+ # see /config/application.yml
+ check_boundaries(xmin, ymin, xmax, ymax)
+
+ if POTLATCH_USE_SQL then
+ ways = sql_find_ways_in_area(xmin, ymin, xmax, ymax)
+ points = sql_find_pois_in_area(xmin, ymin, xmax, ymax)
+ relations = sql_find_relations_in_area_and_ways(xmin, ymin, xmax, ymax, ways.collect {|x| x[0]})
+ else
+ # find the way ids in an area
+ nodes_in_area = Node.find_by_area(ymin, xmin, ymax, xmax, :conditions => ["current_nodes.visible = ?", true], :include => :ways)
+ ways = nodes_in_area.inject([]) { |sum, node|
+ visible_ways = node.ways.select { |w| w.visible? }
+ sum + visible_ways.collect { |w| [w.id,w.version] }
+ }.uniq
+ ways.delete([])
+
+ # find the node ids in an area that aren't part of ways
+ nodes_not_used_in_area = nodes_in_area.select { |node| node.ways.empty? }
+ points = nodes_not_used_in_area.collect { |n| [n.id, n.lon, n.lat, n.tags, n.version] }.uniq
+
+ # find the relations used by those nodes and ways
+ relations = Relation.find_for_nodes(nodes_in_area.collect { |n| n.id }, :conditions => {:visible => true}) +
+ Relation.find_for_ways(ways.collect { |w| w[0] }, :conditions => {:visible => true})
+ relations = relations.collect { |relation| [relation.id,relation.version] }.uniq
+ end
+
+ [0, ways, points, relations]
+
+ rescue Exception => err
+ [-2,"Sorry - I can't get the map for that area."]
end
# Find deleted ways in current bounding box (similar to whichways, but ways
# with a deleted node only - not POIs or relations).
def whichways_deleted(xmin, ymin, xmax, ymax) #:doc:
- xmin -= 0.01; ymin -= 0.01
- xmax += 0.01; ymax += 0.01
-
- nodes_in_area = Node.find_by_area(ymin, xmin, ymax, xmax, :conditions => "current_nodes.visible = 0 AND current_ways.visible = 0", :include => :ways_via_history)
- way_ids = nodes_in_area.collect { |node| node.ways_via_history_ids }.flatten.uniq
-
- [way_ids]
+ enlarge = [(xmax-xmin)/8,0.01].min
+ xmin -= enlarge; ymin -= enlarge
+ xmax += enlarge; ymax += enlarge
+
+ # check boundary is sane and area within defined
+ # see /config/application.yml
+ begin
+ check_boundaries(xmin, ymin, xmax, ymax)
+ rescue Exception => err
+ return [-2,"Sorry - I can't get the map for that area."]
+ end
+
+ nodes_in_area = Node.find_by_area(ymin, xmin, ymax, xmax, :conditions => ["current_ways.visible = ?", false], :include => :ways_via_history)
+ way_ids = nodes_in_area.collect { |node| node.ways_via_history_ids }.flatten.uniq
+
+ [0,way_ids]
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.
# Returns:
# 0. relation id,
# 1. hash of tags,
- # 2. list of members.
+ # 2. list of members,
+ # 3. version.
def getrelation(relid) #:doc:
- rel = Relation.find(relid)
-
- [relid, rel.tags, rel.members]
+ begin
+ rel = Relation.find(relid)
+ rescue ActiveRecord::RecordNotFound
+ return [relid, {}, []]
+ end
+
+ return [relid, {}, [], nil] if rel.nil? or !rel.visible
+ [relid, rel.tags, rel.members, rel.version]
end
# Find relations with specified name/id.
# Returns array of relations, each in same form as getrelation.
def findrelations(searchterm)
- rels = []
- if searchterm.to_i>0 then
- rel = Relation.find(searchterm.to_i)
- if rel and rel.visible then
- rels.push([rel.id, rel.tags, rel.members])
- end
- else
- RelationTag.find(:all, :limit => 11, :conditions => ["match(v) against (?)", searchterm] ).each do |t|
- if t.relation.visible then
+ rels = []
+ if searchterm.to_i>0 then
+ rel = Relation.find(searchterm.to_i)
+ if rel and rel.visible then
+ rels.push([rel.id, rel.tags, rel.members])
+ end
+ else
+ RelationTag.find(:all, :limit => 11, :conditions => ["match(v) against (?)", searchterm] ).each do |t|
+ if t.relation.visible then
rels.push([t.relation.id, t.relation.tags, t.relation.members])
end
end
end
- rels
+ rels
end
# Save a relation.
# 1. original relation id (unchanged),
# 2. new relation id.
- def putrelation(renumberednodes, renumberedways, usertoken, relid, tags, members, visible) #:doc:
- uid = getuserid(usertoken)
- if !uid then return -1,"You are not logged in, so the relation could not be saved." end
-
- relid = relid.to_i
- visible = visible.to_i
-
- # create a new relation, or find the existing one
- if relid <= 0
- rel = Relation.new
- else
- rel = Relation.find(relid)
- end
-
- # check the members are all positive, and correctly type
- typedmembers = []
- members.each do |m|
- mid = m[1].to_i
- if mid < 0
- mid = renumberednodes[mid] if m[0] == 'node'
- mid = renumberedways[mid] if m[0] == 'way'
- end
- if mid
- typedmembers << [m[0], mid, m[2]]
- end
- end
-
- # assign new contents
- rel.members = typedmembers
- rel.tags = tags
- rel.visible = visible
- rel.user_id = uid
-
- # check it then save it
- # BUG: the following is commented out because it always fails on my
- # install. I think it's a Rails bug.
-
- #if !rel.preconditions_ok?
- # return -2, "Relation preconditions failed"
- #else
- rel.save_with_history!
- #end
-
- [0, relid, rel.id]
+ def putrelation(renumberednodes, renumberedways, usertoken, changeset_id, version, relid, tags, members, visible) #:doc:
+ user = getuser(usertoken)
+ if !user then return -1,"You are not logged in, so the relation could not be saved." end
+
+ relid = relid.to_i
+ visible = (visible.to_i != 0)
+
+ new_relation = nil
+ relation = nil
+ Relation.transaction do
+ # create a new relation, or find the existing one
+ if relid > 0
+ relation = Relation.find(relid)
+ end
+ # We always need a new node, based on the data that has been sent to us
+ new_relation = Relation.new
+
+ # check the members are all positive, and correctly type
+ typedmembers = []
+ members.each do |m|
+ mid = m[1].to_i
+ if mid < 0
+ mid = renumberednodes[mid] if m[0] == 'node'
+ mid = renumberedways[mid] if m[0] == 'way'
+ end
+ if mid
+ typedmembers << [m[0], mid, m[2]]
+ end
+ end
+
+ # assign new contents
+ new_relation.members = typedmembers
+ new_relation.tags = tags
+ new_relation.visible = visible
+ new_relation.changeset_id = changeset_id
+ new_relation.version = version
+
+ # NOTE: id or relid here? id doesn't seem to be set above
+ if relid <= 0
+ # We're creating the node
+ new_relation.create_with_history(user)
+ elsif visible
+ # We're updating the node
+ relation.update_from(new_relation, user)
+ else
+ # We're deleting the node
+ relation.delete_with_history!(new_relation, user)
+ end
+ end # transaction
+
+ if id <= 0
+ return [0, relid, new_relation.id, new_relation.version]
+ else
+ return [0, relid, relation.id, relation.version]
+ end
+ rescue OSM::APIChangesetAlreadyClosedError => ex
+ return [-1, "The changeset #{ex.changeset.id} was closed at #{ex.changeset.closed_at}."]
+ rescue OSM::APIVersionMismatchError => ex
+ # Really need to check to see whether this is a server load issue, and the
+ # last version was in the same changeset, or belongs to the same user, then
+ # we can return something different
+ return [-3, "Sorry, someone else has changed this relation since you started editing. Please click the 'Edit' tab to reload the area."]
+ rescue OSM::APIAlreadyDeletedError => ex
+ return [-1, "The relation has already been deleted."]
+ rescue OSM::APIError => ex
+ # Some error that we don't specifically catch
+ return [-2, "Something really bad happened :-( ."]
end
# Save a way to the database, including all nodes. Any nodes in the previous
# version and no longer used are deleted.
#
+ # Parameters:
+ # 0. hash of renumbered nodes (added by amf_controller)
+ # 1. current user token (for authentication)
+ # 2. current changeset
+ # 3. new way version
+ # 4. way ID
+ # 5. list of nodes in way
+ # 6. hash of way tags
+ # 7. array of nodes to change (each one is [lon,lat,id,version,tags])
+ #
# Returns:
# 0. '0' (code for success),
# 1. original way id (unchanged),
# 2. new way id,
- # 3. hash of renumbered nodes (old id=>new id)
+ # 3. hash of renumbered nodes (old id=>new id),
+ # 4. way version,
+ # 5. hash of node versions (node=>version)
- def putway(renumberednodes, usertoken, originalway, points, attributes) #:doc:
+ def putway(renumberednodes, usertoken, changeset_id, wayversion, originalway, pointlist, attributes, nodes) #:doc:
- # -- Initialise and carry out checks
+ # -- Initialise
- uid = getuserid(usertoken)
- if !uid then return -1,"You are not logged in, so the way could not be saved." end
-
- originalway = originalway.to_i
-
- points.each do |a|
- if a[2] == 0 or a[2].nil? then return -2,"Server error - node with id 0 found in way #{originalway}." end
- if a[1] == 90 then return -2,"Server error - node with lat -90 found in way #{originalway}." end
- end
-
- if points.length < 2 then return -2,"Server error - way is only #{points.length} points long." end
-
- # -- Get unique nodes
-
- if originalway < 0
- way = Way.new
- uniques = []
- else
- way = Way.find(originalway)
- uniques = way.unshared_node_ids
- end
-
- # -- Compare nodes and save changes to any that have changed
-
- nodes = []
-
- points.each do |n|
- lon = n[0].to_f
- lat = n[1].to_f
- id = n[2].to_i
- savenode = false
-
- if renumberednodes[id]
- id = renumberednodes[id]
- elsif id < 0
- # Create new node
- node = Node.new
- savenode = true
- else
- node = Node.find(id)
- nodetags=node.tags_as_hash
- nodetags.delete('created_by')
- if !fpcomp(lat, node.lat) or !fpcomp(lon, node.lon) or
- n[4] != nodetags or !node.visible?
- savenode = true
- end
- end
-
- if savenode
- node.user_id = uid
- node.lat = lat
+ user = getuser(usertoken)
+ if !user then return -1,"You are not logged in, so the way could not be saved." end
+ if pointlist.length < 2 then return -2,"Server error - way is only #{points.length} points long." end
+
+ originalway = originalway.to_i
+ pointlist.collect! {|a| a.to_i }
+
+ way=nil # this is returned, so scope it outside the transaction
+ nodeversions = {}
+ Way.transaction do
+
+ # -- Get unique nodes
+
+ if originalway <= 0
+ uniques = []
+ else
+ way = Way.find(originalway)
+ uniques = way.unshared_node_ids
+ end
+
+ #-- Update each changed node
+
+ nodes.each do |a|
+ lon = a[0].to_f
+ lat = a[1].to_f
+ id = a[2].to_i
+ version = a[3].to_i
+ if id == 0 then return -2,"Server error - node with id 0 found in way #{originalway}." end
+ if lat== 90 then return -2,"Server error - node with latitude -90 found in way #{originalway}." end
+ if renumberednodes[id] then id = renumberednodes[id] end
+
+ node = Node.new
+ node.changeset_id = changeset_id
+ node.lat = lat
node.lon = lon
- node.tags = Tags.join(n[4])
- node.visible = true
- node.save_with_history!
-
- if id != node.id
- renumberednodes[id] = node.id
- id = node.id
- end
- end
-
- uniques = uniques - [id]
- nodes.push(id)
- end
-
- # -- Save revised way
-
- way.tags = attributes
- way.nds = nodes
- way.user_id = uid
- way.visible = true
- way.save_with_history!
-
- # -- Delete any unique nodes
-
- uniques.each do |n|
- deleteitemrelations(n, 'node')
-
- node = Node.find(n)
- node.user_id = uid
- node.visible = false
- node.save_with_history!
- end
-
- [0, originalway, way.id, renumberednodes]
+ node.tags = a[4]
+ node.tags.delete('created_by')
+ node.version = version
+ if id <= 0
+ # We're creating the node
+ node.create_with_history(user)
+ renumberednodes[id] = node.id
+ nodeversions[node.id] = node.version
+ else
+ # We're updating an existing node
+ previous=Node.find(id)
+ previous.update_from(node, user)
+ nodeversions[previous.id] = previous.version
+ end
+ end
+
+ # -- Save revised way
+
+ pointlist.collect! {|a|
+ renumberednodes[a] ? renumberednodes[a]:a
+ } # renumber nodes
+ new_way = Way.new
+ new_way.tags = attributes
+ new_way.nds = pointlist
+ new_way.changeset_id = changeset_id
+ new_way.version = wayversion
+ if originalway <= 0
+ new_way.create_with_history(user)
+ way=new_way # so we can get way.id and way.version
+ elsif way.tags!=attributes or way.nds!=pointlist or !way.visible?
+ way.update_from(new_way, user)
+ end
+
+ # -- Delete any unique nodes no longer used
+
+ uniques=uniques-pointlist
+ uniques.each do |n|
+ node = Node.find(n)
+ deleteitemrelations(user, changeset_id, id, 'node', node.version)
+ new_node = Node.new
+ new_node.changeset_id = changeset_id
+ new_node.version = node.version
+ node.delete_with_history!(new_node, user)
+ end
+
+ end # transaction
+
+ [0, originalway, way.id, renumberednodes, way.version, nodeversions]
+ rescue OSM::APIChangesetAlreadyClosedError => ex
+ return [-2, "Sorry, your changeset #{ex.changeset.id} has been closed (at #{ex.changeset.closed_at})."]
+ rescue OSM::APIVersionMismatchError => ex
+ # Really need to check to see whether this is a server load issue, and the
+ # last version was in the same changeset, or belongs to the same user, then
+ # we can return something different
+ return [-3, "Sorry, someone else has changed this way since you started editing. Please click the 'Edit' tab to reload the area."]
+ rescue OSM::APITooManyWayNodesError => ex
+ return [-1, "You have tried to upload a really long way with #{ex.provided} points: only #{ex.max} are allowed."]
+ rescue OSM::APIAlreadyDeletedError => ex
+ return [-1, "The point has already been deleted."]
+ rescue OSM::APIError => ex
+ # Some error that we don't specifically catch
+ return [-2, "Something really bad happened :-(."]
end
# Save POI to the database.
# Refuses save if the node has since become part of a way.
- # Returns:
+ # Returns array with:
# 0. 0 (success),
# 1. original node id (unchanged),
- # 2. new node id.
-
- def putpoi(usertoken, id, lon, lat, tags, visible) #:doc:
- uid = getuserid(usertoken)
- if !uid then return -1,"You are not logged in, so the point could not be saved." end
-
- id = id.to_i
- visible = (visible.to_i == 1)
-
- if id > 0 then
- node = Node.find(id)
-
- if !visible then
- unless node.ways.empty? then return -1,"The point has since become part of a way, so you cannot save it as a POI." end
- deleteitemrelations(id, 'node')
- end
- else
- node = Node.new
- end
-
- node.user_id = uid
- node.lat = lat
- node.lon = lon
- node.tags = Tags.join(tags)
- node.visible = visible
- node.save_with_history!
-
- [0, id, node.id]
+ # 2. new node id,
+ # 3. version.
+
+ def putpoi(usertoken, changeset_id, version, id, lon, lat, tags, visible) #:doc:
+ user = getuser(usertoken)
+ if !user then return -1,"You are not logged in, so the point could not be saved." end
+
+ id = id.to_i
+ visible = (visible.to_i == 1)
+ node = nil
+ new_node = nil
+ Node.transaction do
+ if id > 0 then
+ node = Node.find(id)
+
+ if !visible then
+ unless node.ways.empty? then return -1,"The point has since become part of a way, so you cannot save it as a POI." end
+ end
+ end
+ # We always need a new node, based on the data that has been sent to us
+ new_node = Node.new
+
+ new_node.changeset_id = changeset_id
+ new_node.version = version
+ new_node.lat = lat
+ new_node.lon = lon
+ new_node.tags = tags
+ if id <= 0
+ # We're creating the node
+ new_node.create_with_history(user)
+ elsif visible
+ # We're updating the node
+ node.update_from(new_node, user)
+ else
+ # We're deleting the node
+ node.delete_with_history!(new_node, user)
+ end
+ end # transaction
+
+ if id <= 0
+ return [0, id, new_node.id, new_node.version]
+ else
+ return [0, id, node.id, node.version]
+ end
+ rescue OSM::APIChangesetAlreadyClosedError => ex
+ return [-1, "The changeset #{ex.changeset.id} was closed at #{ex.changeset.closed_at}"]
+ rescue OSM::APIVersionMismatchError => ex
+ # Really need to check to see whether this is a server load issue, and the
+ # last version was in the same changeset, or belongs to the same user, then
+ # we can return something different
+ return [-3, "Sorry, someone else has changed this point since you started editing. Please click the 'Edit' tab to reload the area."]
+ rescue OSM::APIAlreadyDeletedError => ex
+ return [-1, "The point has already been deleted"]
+ rescue OSM::APIError => ex
+ # Some error that we don't specifically catch
+ return [-2, "Something really bad happened :-()"]
end
# Read POI from database
# (only called on revert: POIs are usually read by whichways).
#
- # Returns array of id, long, lat, hash of tags.
+ # Returns array of id, long, lat, hash of tags, version.
def getpoi(id,timestamp) #:doc:
- if timestamp>0 then
- n = OldNode.find(id, :conditions=>['UNIX_TIMESTAMP(timestamp)=?',timestamp])
- else
- n = Node.find(id)
- end
-
- if n
- return [n.id, n.lon, n.lat, n.tags_as_hash]
- else
- return [nil, nil, nil, '']
- end
+ if timestamp == '' then
+ n = Node.find(id)
+ else
+ n = OldNode.find(id, :conditions=>['timestamp=?',DateTime.strptime(timestamp, "%d %b %Y, %H:%M:%S")])
+ end
+
+ if n
+ return [n.id, n.lon, n.lat, n.tags, n.version]
+ else
+ return [nil, nil, nil, {}, nil]
+ end
end
# Delete way and all constituent nodes. Also removes from any relations.
+ # Params:
+ # * The user token
+ # * the changeset id
+ # * the id of the way to change
+ # * the version of the way that was downloaded
+ # * a hash of the id and versions of all the nodes that are in the way, if any
+ # of the nodes have been changed by someone else then, there is a problem!
# Returns 0 (success), unchanged way id.
- def deleteway(usertoken, way_id) #:doc:
- uid = getuserid(usertoken)
- if !uid then return -1,"You are not logged in, so the way could not be deleted." end
-
- # FIXME: would be good not to make two history entries when removing
- # two nodes from the same relation
- user = User.find(uid)
- way = Way.find(way_id)
- way.unshared_node_ids.each do |n|
- deleteitemrelations(n, 'node')
- end
- deleteitemrelations(way_id, 'way')
-
- way.delete_with_relations_and_nodes_and_history(user)
-
- [0, way_id]
+ def deleteway(usertoken, changeset_id, way_id, way_version, node_id_version) #:doc:
+ user = getuser(usertoken)
+ unless user then return -1,"You are not logged in, so the way could not be deleted." end
+
+ way_id = way_id.to_i
+ # Need a transaction so that if one item fails to delete, the whole delete fails.
+ Way.transaction do
+
+ # delete the way
+ old_way = Way.find(way_id)
+ delete_way = Way.new
+ delete_way.version = way_version
+ delete_way.changeset_id = changeset_id
+ old_way.delete_with_history!(delete_way, user)
+
+ old_way.unshared_node_ids.each do |node_id|
+ # delete the node
+ node = Node.find(node_id)
+ delete_node = Node.new
+ delete_node.changeset_id = changeset_id
+ if node_id_version[node_id.to_s]
+ delete_node.version = node_id_version[node_id.to_s]
+ else
+ # in case the node wasn't passed (i.e. if it was previously removed
+ # from the way in Potlatch)
+ deleteitemrelations(user, changeset_id, node_id, 'node', node.version)
+ delete_node.version = node.version
+ end
+ node.delete_with_history!(delete_node, user)
+ end
+ end # transaction
+ [0, way_id]
+ rescue OSM::APIChangesetAlreadyClosedError => ex
+ return [-1, "The changeset #{ex.changeset.id} was closed at #{ex.changeset.closed_at}"]
+ rescue OSM::APIVersionMismatchError => ex
+ # Really need to check to see whether this is a server load issue, and the
+ # last version was in the same changeset, or belongs to the same user, then
+ # we can return something different
+ return [-3, "Sorry, someone else has changed this way since you started editing. Please click the 'Edit' tab to reload the area."]
+ rescue OSM::APIAlreadyDeletedError => ex
+ return [-1, "The way has already been deleted."]
+ rescue OSM::APIError => ex
+ # Some error that we don't specifically catch
+ return [-2, "Something really bad happened :-( ."]
end
# Support functions
# Remove a node or way from all relations
+ # This is only used by putway and deleteway when deleting nodes removed
+ # from a way (because Potlatch itself doesn't keep track of these -
+ # possible FIXME).
- def deleteitemrelations(objid, type) #:doc:
- relations = RelationMember.find(:all,
+ def deleteitemrelations(user, changeset_id, objid, type, version) #:doc:
+ relations = RelationMember.find(:all,
:conditions => ['member_type = ? and member_id = ?', type, objid],
:include => :relation).collect { |rm| rm.relation }.uniq
- relations.each do |rel|
- rel.members.delete_if { |x| x[0] == type and x[1] == objid }
- rel.save_with_history!
- end
- end
-
- # Break out node tags into a hash
- # (should become obsolete as of API 0.6)
-
- def tagstring_to_hash(a) #:doc:
- tags={}
- Tags.split(a) do |k, v|
- tags[k]=v
- end
- tags
+ relations.each do |rel|
+ rel.members.delete_if { |x| x[0] == type and x[1] == objid }
+ new_rel = Relation.new
+ new_rel.tags = rel.tags
+ new_rel.visible = rel.visible
+ new_rel.version = rel.version
+ new_rel.members = rel.members
+ new_rel.changeset_id = changeset_id
+ rel.update_from(new_rel, user)
+ end
end
# Authenticate token
# (can also be of form user:pass)
-
- def getuserid(token) #:doc:
- if (token =~ /^(.+)\:(.+)$/) then
- user = User.authenticate(:username => $1, :password => $2)
- else
- user = User.authenticate(:token => token)
- end
-
- return user ? user.id : nil;
- end
-
- # Compare two floating-point numbers to within 0.0000001
-
- def fpcomp(a,b) #:doc:
- return ((a/0.0000001).round==(b/0.0000001).round)
+ # When we are writing to the api, we need the actual user model,
+ # not just the id, hence this abstraction
+
+ def getuser(token) #:doc:
+ if (token =~ /^(.+)\:(.+)$/) then
+ user = User.authenticate(:username => $1, :password => $2)
+ else
+ user = User.authenticate(:token => token)
+ end
+ return user
end
# Send AMF response
def sendresponse(results)
- a,b=results.length.divmod(256)
- render :content_type => "application/x-amf", :text => proc { |response, output|
- output.write 0.chr+0.chr+0.chr+0.chr+a.chr+b.chr
- results.each do |k,v|
- output.write(v)
- end
- }
+ a,b=results.length.divmod(256)
+ render :content_type => "application/x-amf", :text => proc { |response, output|
+ # ** move amf writing loop into here -
+ # basically we read the messages in first (into an array of some sort),
+ # then iterate through that array within here, and do all the AMF writing
+ output.write 0.chr+0.chr+0.chr+0.chr+a.chr+b.chr
+ results.each do |k,v|
+ output.write(v)
+ end
+ }
end
# ====================================================================
# Alternative SQL queries for getway/whichways
- def sql_find_way_ids_in_area(xmin,ymin,xmax,ymax)
- sql=<<-EOF
- SELECT DISTINCT current_way_nodes.id AS wayid
- FROM current_way_nodes
- INNER JOIN current_nodes ON current_nodes.id=current_way_nodes.node_id
- INNER JOIN current_ways ON current_ways.id =current_way_nodes.id
- WHERE current_nodes.visible=1
- AND current_ways.visible=1
- AND #{OSM.sql_for_area(ymin, xmin, ymax, xmax, "current_nodes.")}
- EOF
- return ActiveRecord::Base.connection.select_all(sql).collect { |a| a['wayid'].to_i }
+ def sql_find_ways_in_area(xmin,ymin,xmax,ymax)
+ sql=<<-EOF
+ SELECT DISTINCT current_ways.id AS wayid,current_ways.version AS version
+ FROM current_way_nodes
+ INNER JOIN current_nodes ON current_nodes.id=current_way_nodes.node_id
+ INNER JOIN current_ways ON current_ways.id =current_way_nodes.id
+ WHERE current_nodes.visible=TRUE
+ AND current_ways.visible=TRUE
+ AND #{OSM.sql_for_area(ymin, xmin, ymax, xmax, "current_nodes.")}
+ EOF
+ return ActiveRecord::Base.connection.select_all(sql).collect { |a| [a['wayid'].to_i,a['version'].to_i] }
end
def sql_find_pois_in_area(xmin,ymin,xmax,ymax)
- sql=<<-EOF
- SELECT current_nodes.id,current_nodes.latitude*0.0000001 AS lat,current_nodes.longitude*0.0000001 AS lon,current_nodes.tags
+ pois=[]
+ sql=<<-EOF
+ SELECT current_nodes.id,current_nodes.latitude*0.0000001 AS lat,current_nodes.longitude*0.0000001 AS lon,current_nodes.version
FROM current_nodes
- LEFT OUTER JOIN current_way_nodes cwn ON cwn.node_id=current_nodes.id
- WHERE current_nodes.visible=1
+ LEFT OUTER JOIN current_way_nodes cwn ON cwn.node_id=current_nodes.id
+ WHERE current_nodes.visible=TRUE
AND cwn.id IS NULL
AND #{OSM.sql_for_area(ymin, xmin, ymax, xmax, "current_nodes.")}
- EOF
- return ActiveRecord::Base.connection.select_all(sql).collect { |n| [n['id'].to_i,n['lon'].to_f,n['lat'].to_f,tagstring_to_hash(n['tags'])] }
+ EOF
+ ActiveRecord::Base.connection.select_all(sql).each do |row|
+ poitags={}
+ ActiveRecord::Base.connection.select_all("SELECT k,v FROM current_node_tags WHERE id=#{row['id']}").each do |n|
+ poitags[n['k']]=n['v']
+ end
+ pois << [row['id'].to_i, row['lon'].to_f, row['lat'].to_f, poitags, row['version'].to_i]
+ end
+ pois
end
def sql_find_relations_in_area_and_ways(xmin,ymin,xmax,ymax,way_ids)
- # ** It would be more Potlatchy to get relations for nodes within ways
- # during 'getway', not here
- sql=<<-EOF
- SELECT DISTINCT cr.id AS relid
- FROM current_relations cr
- INNER JOIN current_relation_members crm ON crm.id=cr.id
- INNER JOIN current_nodes cn ON crm.member_id=cn.id AND crm.member_type='node'
- WHERE #{OSM.sql_for_area(ymin, xmin, ymax, xmax, "cn.")}
- EOF
- unless way_ids.empty?
- sql+=<<-EOF
- UNION
- SELECT DISTINCT cr.id AS relid
- FROM current_relations cr
- INNER JOIN current_relation_members crm ON crm.id=cr.id
- WHERE crm.member_type='way'
- AND crm.member_id IN (#{way_ids.join(',')})
- EOF
- end
- return ActiveRecord::Base.connection.select_all(sql).collect { |a| a['relid'].to_i }.uniq
+ # ** It would be more Potlatchy to get relations for nodes within ways
+ # during 'getway', not here
+ sql=<<-EOF
+ SELECT DISTINCT cr.id AS relid,cr.version AS version
+ FROM current_relations cr
+ INNER JOIN current_relation_members crm ON crm.id=cr.id
+ INNER JOIN current_nodes cn ON crm.member_id=cn.id AND crm.member_type='node'
+ WHERE #{OSM.sql_for_area(ymin, xmin, ymax, xmax, "cn.")}
+ EOF
+ unless way_ids.empty?
+ sql+=<<-EOF
+ UNION
+ SELECT DISTINCT cr.id AS relid,cr.version AS version
+ FROM current_relations cr
+ INNER JOIN current_relation_members crm ON crm.id=cr.id
+ WHERE crm.member_type='way'
+ AND crm.member_id IN (#{way_ids.join(',')})
+ EOF
+ end
+ return ActiveRecord::Base.connection.select_all(sql).collect { |a| [a['relid'].to_i,a['version'].to_i] }
end
def sql_get_nodes_in_way(wayid)
- points=[]
- sql=<<-EOF
- SELECT latitude*0.0000001 AS lat,longitude*0.0000001 AS lon,current_nodes.id,tags
- FROM current_way_nodes,current_nodes
- WHERE current_way_nodes.id=#{wayid.to_i}
+ points=[]
+ sql=<<-EOF
+ SELECT latitude*0.0000001 AS lat,longitude*0.0000001 AS lon,current_nodes.id
+ FROM current_way_nodes,current_nodes
+ WHERE current_way_nodes.id=#{wayid.to_i}
AND current_way_nodes.node_id=current_nodes.id
- AND current_nodes.visible=1
- ORDER BY sequence_id
+ AND current_nodes.visible=TRUE
+ ORDER BY sequence_id
EOF
- ActiveRecord::Base.connection.select_all(sql).each do |row|
- nodetags=tagstring_to_hash(row['tags'])
- nodetags.delete('created_by')
- points << [row['lon'].to_f,row['lat'].to_f,row['id'].to_i,nodetags]
- end
- points
+ ActiveRecord::Base.connection.select_all(sql).each do |row|
+ nodetags={}
+ ActiveRecord::Base.connection.select_all("SELECT k,v FROM current_node_tags WHERE id=#{row['id']}").each do |n|
+ nodetags[n['k']]=n['v']
+ end
+ nodetags.delete('created_by')
+ points << [row['lon'].to_f,row['lat'].to_f,row['id'].to_i,nodetags]
+ end
+ points
end
def sql_get_tags_in_way(wayid)
- tags={}
- ActiveRecord::Base.connection.select_all("SELECT k,v FROM current_way_tags WHERE id=#{wayid.to_i}").each do |row|
- tags[row['k']]=row['v']
- end
- tags
+ tags={}
+ ActiveRecord::Base.connection.select_all("SELECT k,v FROM current_way_tags WHERE id=#{wayid.to_i}").each do |row|
+ tags[row['k']]=row['v']
+ end
+ tags
end
+ def sql_get_way_version(wayid)
+ ActiveRecord::Base.connection.select_one("SELECT version FROM current_ways WHERE id=#{wayid.to_i}")
+ end
end
-# Local Variables:
-# indent-tabs-mode: t
-# tab-width: 4
-# End:
class ApiController < ApplicationController
session :off
- before_filter :check_read_availability, :except => [:capabilities]
+ before_filter :check_api_readable, :except => [:capabilities]
after_filter :compress_output
# Help methods for checking boundary sanity and area size
@@count = COUNT
# The maximum area you're allowed to request, in square degrees
- MAX_REQUEST_AREA = 0.25
+ MAX_REQUEST_AREA = APP_CONFIG['max_request_area']
# Number of GPS trace/trackpoints returned per-page
- TRACEPOINTS_PER_PAGE = 5000
+ TRACEPOINTS_PER_PAGE = APP_CONFIG['tracepoints_per_page']
-
+ # Get an XML response containing a list of tracepoints that have been uploaded
+ # within the specified bounding box, and in the specified page.
def trackpoints
@@count+=1
#retrieve the page number
render :text => doc.to_s, :content_type => "text/xml"
end
+ # This is probably the most common call of all. It is used for getting the
+ # OSM data for a specified bounding box, usually for editing. First the
+ # bounding box (bbox) is checked to make sure that it is sane. All nodes
+ # are searched, then all the ways that reference those nodes are found.
+ # All Nodes that are referenced by those ways are fetched and added to the list
+ # of nodes.
+ # Then all the relations that reference the already found nodes and ways are
+ # fetched. All the nodes and ways that are referenced by those ways are then
+ # fetched. Finally all the xml is returned.
def map
GC.start
@@count+=1
return
end
- @nodes = Node.find_by_area(min_lat, min_lon, max_lat, max_lon, :conditions => "visible = 1", :limit => APP_CONFIG['max_number_of_nodes']+1)
+ # FIXME um why is this area using a different order for the lat/lon from above???
+ @nodes = Node.find_by_area(min_lat, min_lon, max_lat, max_lon, :conditions => {:visible => true}, :limit => APP_CONFIG['max_number_of_nodes']+1)
# get all the nodes, by tag not yet working, waiting for change from NickB
# need to be @nodes (instance var) so tests in /spec can be performed
#@nodes = Node.search(bbox, params[:tag])
node_ids = @nodes.collect(&:id)
if node_ids.length > APP_CONFIG['max_number_of_nodes']
- report_error("You requested too many nodes (limit is 50,000). Either request a smaller area, or use planet.osm")
+ report_error("You requested too many nodes (limit is #{APP_CONFIG['max_number_of_nodes']}). Either request a smaller area, or use planet.osm")
return
end
if node_ids.length == 0
- render :text => "<osm version='0.5'></osm>", :content_type => "text/xml"
+ render :text => "<osm version='#{API_VERSION}' generator='#{GENERATOR}'></osm>", :content_type => "text/xml"
return
end
end
end
- relations = Relation.find_for_nodes(visible_nodes.keys, :conditions => "visible = 1") +
- Relation.find_for_ways(way_ids, :conditions => "visible = 1")
+ relations = Relation.find_for_nodes(visible_nodes.keys, :conditions => {:visible => true}) +
+ Relation.find_for_ways(way_ids, :conditions => {:visible => true})
# 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)
- relations += Relation.find_for_relations(relations.collect { |r| r.id }, :conditions => "visible = 1")
+ relations += Relation.find_for_relations(relations.collect { |r| r.id }, :conditions => {:visible => true})
# 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.
end
end
+ # Get a list of the tiles that have changed within a specified time
+ # period
def changes
zoom = (params[:zoom] || '12').to_i
endtime = Time.parse(params[:end])
else
hours = (params[:hours] || '1').to_i.hours
- endtime = Time.now
+ endtime = Time.now.getutc
starttime = endtime - hours
end
if zoom >= 1 and zoom <= 16 and
- endtime >= starttime and endtime - starttime <= 24.hours
+ endtime > starttime and endtime - starttime <= 24.hours
mask = (1 << zoom) - 1
tiles = Node.count(:conditions => ["timestamp BETWEEN ? AND ?", starttime, endtime],
render :text => doc.to_s, :content_type => "text/xml"
else
- render :nothing => true, :status => :bad_request
+ render :text => "Requested zoom is invalid, or the supplied start is after the end time, or the start duration is more than 24 hours", :status => :bad_request
end
end
+ # External apps that use the api are able to query the api to find out some
+ # parameters of the API. It currently returns:
+ # * minimum and maximum API versions that can be used.
+ # * maximum area that can be requested in a bbox request in square degrees
+ # * number of tracepoints that are returned in each tracepoints page
def capabilities
doc = OSM::API.new.get_xml_doc
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;
api << area
+ tracepoints = XML::Node.new 'tracepoints'
+ tracepoints['per_page'] = APP_CONFIG['tracepoints_per_page'].to_s
+ api << tracepoints
+ waynodes = XML::Node.new 'waynodes'
+ waynodes['maximum'] = APP_CONFIG['max_number_of_way_nodes'].to_s
+ api << waynodes
doc.root << api
# Likewise, all the methods added will be available for all controllers.
class ApplicationController < ActionController::Base
- if OSM_STATUS == :database_offline
+ if OSM_STATUS == :database_readonly or OSM_STATUS == :database_offline
session :off
end
def authorize_web
if session[:user]
- @user = User.find(session[:user], :conditions => "visible = 1")
+ @user = User.find(session[:user], :conditions => {:visible => true})
elsif session[:token]
@user = User.authenticate(:token => session[:token])
session[:user] = @user.id
redirect_to :controller => 'user', :action => 'login', :referer => request.request_uri unless @user
end
- def authorize(realm='Web Password', errormessage="Couldn't authenticate you")
+ ##
+ # sets up the @user object for use by other methods. this is mostly called
+ # from the authorize method, but can be called elsewhere if authorisation
+ # is optional.
+ def setup_user_auth
username, passwd = get_auth_data # parse from headers
# authenticate per-scheme
if username.nil?
else
@user = User.authenticate(:username => username, :password => passwd) # basic auth
end
+ end
+
+ def authorize(realm='Web Password', errormessage="Couldn't authenticate you")
+ # make the @user object from any auth sources we have
+ setup_user_auth
# handle authenticate pass/fail
unless @user
end
end
- def check_database_availability(need_api = false)
+ def check_database_readable(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
+ def check_database_writable(need_api = false)
+ if OSM_STATUS == :database_offline or OSM_STATUS == :database_readonly or
+ (need_api and (OSM_STATUS == :api_offline or OSM_STATUS == :api_readonly))
+ redirect_to :controller => 'site', :action => 'offline'
+ end
+ end
+
+ def check_api_readable
if OSM_STATUS == :database_offline or OSM_STATUS == :api_offline
response.headers['Error'] = "Database offline for maintenance"
render :nothing => true, :status => :service_unavailable
end
end
- def check_write_availability
- if OSM_STATUS == :database_offline or OSM_STATUS == :api_offline or OSM_STATUS == :api_readonly
+ def check_api_writable
+ if OSM_STATUS == :database_offline or OSM_STATUS == :database_readonly 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
# 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
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
layout 'site'
before_filter :authorize_web
- before_filter { |c| c.check_database_availability(true) }
+ before_filter { |c| c.check_database_readable(true) }
def start
end
- def index
- @nodes = Node.find(:all, :order => "timestamp DESC", :limit=> 20)
- end
+
def relation
- begin
- @relation = Relation.find(params[:id])
-
- @name = @relation.tags['name'].to_s
- if @name.length == 0:
- @name = "#" + @relation.id.to_s
- end
-
- @title = 'Relation | ' + (@name)
- @next = Relation.find(:first, :order => "id ASC", :conditions => [ "visible = true AND id > :id", { :id => @relation.id }] )
- @prev = Relation.find(:first, :order => "id DESC", :conditions => [ "visible = true AND id < :id", { :id => @relation.id }] )
- rescue ActiveRecord::RecordNotFound
- render :nothing => true, :status => :not_found
+ @relation = Relation.find(params[:id])
+
+ @name = @relation.tags['name'].to_s
+ if @name.length == 0:
+ @name = "#" + @relation.id.to_s
end
+
+ @title = 'Relation | ' + (@name)
+ @next = Relation.find(:first, :order => "id ASC", :conditions => [ "visible = true AND id > :id", { :id => @relation.id }] )
+ @prev = Relation.find(:first, :order => "id DESC", :conditions => [ "visible = true AND id < :id", { :id => @relation.id }] )
+ rescue ActiveRecord::RecordNotFound
+ @type = "relation"
+ render :action => "not_found", :status => :not_found
end
def relation_history
- begin
- @relation = Relation.find(params[:id])
+ @relation = Relation.find(params[:id])
- @name = @relation.tags['name'].to_s
- if @name.length == 0:
- @name = "#" + @relation.id.to_s
- end
-
- @title = 'Relation History | ' + (@name)
- rescue ActiveRecord::RecordNotFound
- render :nothing => true, :status => :not_found
+ @name = @relation.tags['name'].to_s
+ if @name.length == 0:
+ @name = "#" + @relation.id.to_s
end
+
+ @title = 'Relation History | ' + (@name)
+ rescue ActiveRecord::RecordNotFound
+ @type = "relation"
+ render :action => "not_found", :status => :not_found
end
def way
- begin
- @way = Way.find(params[:id])
+ @way = Way.find(params[:id])
- @name = @way.tags['name'].to_s
- if @name.length == 0:
- @name = "#" + @way.id.to_s
- end
-
- @title = 'Way | ' + (@name)
- @next = Way.find(:first, :order => "id ASC", :conditions => [ "visible = true AND id > :id", { :id => @way.id }] )
- @prev = Way.find(:first, :order => "id DESC", :conditions => [ "visible = true AND id < :id", { :id => @way.id }] )
- rescue ActiveRecord::RecordNotFound
- render :nothing => true, :status => :not_found
+ @name = @way.tags['name'].to_s
+ if @name.length == 0:
+ @name = "#" + @way.id.to_s
end
+
+ @title = 'Way | ' + (@name)
+ @next = Way.find(:first, :order => "id ASC", :conditions => [ "visible = true AND id > :id", { :id => @way.id }] )
+ @prev = Way.find(:first, :order => "id DESC", :conditions => [ "visible = true AND id < :id", { :id => @way.id }] )
+ rescue ActiveRecord::RecordNotFound
+ @type = "way"
+ render :action => "not_found", :status => :not_found
end
def way_history
- begin
- @way = Way.find(params[:id])
+ @way = Way.find(params[:id])
- @name = @way.tags['name'].to_s
- if @name.length == 0:
- @name = "#" + @way.id.to_s
- end
-
- @title = 'Way History | ' + (@name)
- rescue ActiveRecord::RecordNotFound
- render :nothing => true, :status => :not_found
+ @name = @way.tags['name'].to_s
+ if @name.length == 0:
+ @name = "#" + @way.id.to_s
end
+
+ @title = 'Way History | ' + (@name)
+ rescue ActiveRecord::RecordNotFound
+ @type = "way"
+ render :action => "not_found", :status => :not_found
end
def node
- begin
- @node = Node.find(params[:id])
+ @node = Node.find(params[:id])
- @name = @node.tags_as_hash['name'].to_s
- if @name.length == 0:
- @name = "#" + @node.id.to_s
- end
-
- @title = 'Node | ' + (@name)
- @next = Node.find(:first, :order => "id ASC", :conditions => [ "visible = true AND id > :id", { :id => @node.id }] )
- @prev = Node.find(:first, :order => "id DESC", :conditions => [ "visible = true AND id < :id", { :id => @node.id }] )
- rescue ActiveRecord::RecordNotFound
- render :nothing => true, :status => :not_found
+ @name = @node.tags_as_hash['name'].to_s
+ if @name.length == 0:
+ @name = "#" + @node.id.to_s
end
+
+ @title = 'Node | ' + (@name)
+ @next = Node.find(:first, :order => "id ASC", :conditions => [ "visible = true AND id > :id", { :id => @node.id }] )
+ @prev = Node.find(:first, :order => "id DESC", :conditions => [ "visible = true AND id < :id", { :id => @node.id }] )
+ rescue ActiveRecord::RecordNotFound
+ @type = "node"
+ render :action => "not_found", :status => :not_found
end
def node_history
- begin
- @node = Node.find(params[:id])
+ @node = Node.find(params[:id])
- @name = @node.tags_as_hash['name'].to_s
- if @name.length == 0:
- @name = "#" + @node.id.to_s
- end
-
- @title = 'Node History | ' + (@name)
- rescue ActiveRecord::RecordNotFound
- render :nothing => true, :status => :not_found
+ @name = @node.tags_as_hash['name'].to_s
+ if @name.length == 0:
+ @name = "#" + @node.id.to_s
end
+
+ @title = 'Node History | ' + (@name)
+ rescue ActiveRecord::RecordNotFound
+ @type = "way"
+ render :action => "not_found", :status => :not_found
+ end
+
+ def changeset
+ @changeset = Changeset.find(params[:id])
+ @node_pages, @nodes = paginate(:old_nodes, :conditions => {:changeset_id => @changeset.id}, :per_page => 20, :parameter => 'node_page')
+ @way_pages, @ways = paginate(:old_ways, :conditions => {:changeset_id => @changeset.id}, :per_page => 20, :parameter => 'way_page')
+ @relation_pages, @relations = paginate(:old_relations, :conditions => {:changeset_id => @changeset.id}, :per_page => 20, :parameter => 'relation_page')
+
+ @title = "Changeset | #{@changeset.id}"
+ @next = Changeset.find(:first, :order => "id ASC", :conditions => [ "id > :id", { :id => @changeset.id }] )
+ @prev = Changeset.find(:first, :order => "id DESC", :conditions => [ "id < :id", { :id => @changeset.id }] )
+ rescue ActiveRecord::RecordNotFound
+ @type = "changeset"
+ render :action => "not_found", :status => :not_found
end
end
before_filter :authorize_web
before_filter :require_user, :only => [:new, :edit]
- before_filter :check_database_availability
+ before_filter :check_database_readable
+ before_filter :check_database_writable, :only => [:new, :edit]
def new
@title = 'New diary entry'
redirect_to :controller => 'diary_entry', :action => 'view', :id => params[:id]
end
end
+ rescue ActiveRecord::RecordNotFound
+ render :action => "no_such_entry", :status => :not_found
end
def comment
def list
if params[:display_name]
- @this_user = User.find_by_display_name(params[:display_name], :conditions => "visible = 1")
+ @this_user = User.find_by_display_name(params[:display_name], :conditions => {:visible => true})
if @this_user
@title = @this_user.display_name + "'s diary"
else
@title = "Users' diaries"
@entry_pages, @entries = paginate(:diary_entries, :include => :user,
- :conditions => "users.visible = 1",
+ :conditions => ["users.visible = ?", true],
:order => 'created_at DESC',
:per_page => 20)
end
def rss
if params[:display_name]
- user = User.find_by_display_name(params[:display_name], :conditions => "visible = 1")
+ user = User.find_by_display_name(params[:display_name], :conditions => {:visible => true})
if user
@entries = DiaryEntry.find(:all, :conditions => ['user_id = ?', user.id], :order => 'created_at DESC', :limit => 20)
@title = "OpenStreetMap diary entries for #{user.display_name}"
@description = "Recent OpenStreetmap diary entries from #{user.display_name}"
- @link = "http://www.openstreetmap.org/user/#{user.display_name}/diary"
+ @link = "http://#{SERVER_URL}/user/#{user.display_name}/diary"
render :content_type => Mime::RSS
else
end
else
@entries = DiaryEntry.find(:all, :include => :user,
- :conditions => "users.visible = 1",
+ :conditions => ["users.visible = ?", true],
:order => 'created_at DESC', :limit => 20)
@title = "OpenStreetMap diary entries"
@description = "Recent diary entries from users of OpenStreetMap"
- @link = "http://www.openstreetmap.org/diary"
+ @link = "http://#{SERVER_URL}/diary"
render :content_type => Mime::RSS
end
end
def view
- user = User.find_by_display_name(params[:display_name], :conditions => "visible = 1")
+ user = User.find_by_display_name(params[:display_name], :conditions => {:visible => true})
if user
@entry = DiaryEntry.find(:first, :conditions => ['user_id = ? AND id = ?', user.id, params[:id]])
+ @title = "Users' diaries | #{params[:display_name]}"
else
@not_found_user = params[:display_name]
before_filter :authorize_web
before_filter :require_user
+ before_filter :check_database_readable
+ before_filter :check_database_writable, :only => [:new, :reply, :mark]
+ # Allow the user to write a new message to another user. This action also
+ # deals with the sending of that message to the other user when the user
+ # clicks send.
+ # The user_id param is the id of the user that the message is being sent to.
def new
@title = 'send message'
+ @to_user = User.find(params[:user_id])
if params[:message]
@message = Message.new(params[:message])
- @message.to_user_id = params[:user_id]
+ @message.to_user_id = @to_user.id
@message.from_user_id = @user.id
- @message.sent_on = Time.now
+ @message.sent_on = Time.now.getutc
if @message.save
flash[:notice] = 'Message sent'
else
@title = params[:title]
end
+ rescue ActiveRecord::RecordNotFound
+ render :action => 'no_such_user', :status => :not_found
end
+ # Allow the user to reply to another message.
def reply
message = Message.find(params[:message_id], :conditions => ["to_user_id = ? or from_user_id = ?", @user.id, @user.id ])
@body = "On #{message.sent_on} #{message.sender.display_name} wrote:\n\n#{message.body.gsub(/^/, '> ')}"
@title = "Re: #{message.title.sub(/^Re:\s*/, '')}"
@user_id = message.from_user_id
+ @to_user = User.find(message.to_user_id)
render :action => 'new'
rescue ActiveRecord::RecordNotFound
- render :nothing => true, :status => :not_found
+ render :action => 'no_such_user', :status => :not_found
end
+ # Show a message
def read
@title = 'read message'
@message = Message.find(params[:message_id], :conditions => ["to_user_id = ? or from_user_id = ?", @user.id, @user.id ])
- @message.message_read = 1 if @message.to_user_id == @user.id
+ @message.message_read = true if @message.to_user_id == @user.id
@message.save
rescue ActiveRecord::RecordNotFound
- render :nothing => true, :status => :not_found
+ render :action => 'no_such_user', :status => :not_found
end
+ # Display the list of messages that have been sent to the user.
def inbox
@title = 'inbox'
if @user and params[:display_name] == @user.display_name
end
end
+ # Display the list of messages that the user has sent to other users.
def outbox
@title = 'outbox'
if @user and params[:display_name] == @user.display_name
end
end
+ # Set the message as being read or unread.
def mark
if params[:message_id]
id = params[:message_id]
message = Message.find_by_id(id)
if params[:mark] == 'unread'
- message_read = 0
+ message_read = false
mark_type = 'unread'
else
- message_read = 1
+ message_read = true
mark_type = 'read'
end
message.message_read = message_read
redirect_to :controller => 'message', :action => 'inbox', :display_name => @user.display_name
end
end
+ rescue ActiveRecord::RecordNotFound
+ render :action => 'no_such_user', :status => :not_found
end
end
session :off
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 :check_api_writable, :only => [:create, :update, :delete]
+ before_filter :check_api_readable, :except => [:create, :update, :delete]
after_filter :compress_output
# Create a node from XML.
def create
- if request.put?
- node = Node.from_xml(request.raw_post, true)
-
- if node
- node.user_id = @user.id
- node.visible = true
- node.save_with_history!
+ begin
+ if request.put?
+ node = Node.from_xml(request.raw_post, true)
- render :text => node.id.to_s, :content_type => "text/plain"
+ if node
+ node.create_with_history @user
+ render :text => node.id.to_s, :content_type => "text/plain"
+ else
+ render :nothing => true, :status => :bad_request
+ end
else
- render :nothing => true, :status => :bad_request
+ render :nothing => true, :status => :method_not_allowed
end
- else
- render :nothing => true, :status => :method_not_allowed
+ rescue OSM::APIError => ex
+ render ex.render_opts
end
end
def read
begin
node = Node.find(params[:id])
- if node.visible
+ if node.visible?
response.headers['Last-Modified'] = node.timestamp.rfc822
render :text => node.to_xml.to_s, :content_type => "text/xml"
else
render :nothing => true, :status => :not_found
end
end
-
+
# Update a node from given XML
def update
begin
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!
-
- render :nothing => true
+ node.update_from(new_node, @user)
+ render :text => node.version.to_s, :content_type => "text/plain"
else
render :nothing => true, :status => :bad_request
end
+ rescue OSM::APIError => ex
+ render ex.render_opts
rescue ActiveRecord::RecordNotFound
render :nothing => true, :status => :not_found
end
end
- # Delete a node. Doesn't actually delete it, but retains its history in a wiki-like way.
- # FIXME remove all the fricking SQL
+ # Delete a node. Doesn't actually delete it, but retains its history
+ # in a wiki-like way. We therefore treat it like an update, so the delete
+ # method returns the new version number.
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
+ new_node = Node.from_xml(request.raw_post)
+
+ if new_node and new_node.id == node.id
+ node.delete_with_history!(new_node, @user)
+ render :text => node.version.to_s, :content_type => "text/plain"
else
- render :text => "", :status => :gone
+ render :nothing => true, :status => :bad_request
end
rescue ActiveRecord::RecordNotFound
render :nothing => true, :status => :not_found
+ rescue OSM::APIError => ex
+ render ex.render_opts
end
end
- # WTF does this do?
+ # Dump the details on many nodes whose ids are given in the "nodes" parameter.
def nodes
ids = params['nodes'].split(',').collect { |n| n.to_i }
require 'xml/libxml'
session :off
- before_filter :check_read_availability
+ before_filter :check_api_readable
after_filter :compress_output
def history
render :nothing => true, :status => :internal_server_error
end
end
+
+ def version
+ begin
+ old_node = OldNode.find(:first, :conditions => {:id => params[:id], :version => params[:version]} )
+
+ response.headers['Last-Modified'] = old_node.timestamp.rfc822
+
+ doc = OSM::API.new.get_xml_doc
+ doc.root << old_node.to_xml_node
+
+ render :text => doc.to_s, :content_type => "text/xml"
+ rescue ActiveRecord::RecordNotFound
+ render :nothing => true, :status => :not_found
+ rescue
+ render :nothing => true, :status => :internal_server_error
+ end
+ end
end
require 'xml/libxml'
session :off
- before_filter :check_read_availability
+ before_filter :check_api_readable
after_filter :compress_output
def history
way.old_ways.each do |old_way|
doc.root << old_way.to_xml_node
- end
+ end
render :text => doc.to_s, :content_type => "text/xml"
rescue ActiveRecord::RecordNotFound
render :nothing => true, :status => :internal_server_error
end
end
+
+ def version
+ begin
+ old_way = OldWay.find(:first, :conditions => {:id => params[:id], :version => params[:version]} )
+
+ response.headers['Last-Modified'] = old_way.timestamp.rfc822
+
+ doc = OSM::API.new.get_xml_doc
+ doc.root << old_way.to_xml_node
+
+ render :text => doc.to_s, :content_type => "text/xml"
+ rescue ActiveRecord::RecordNotFound
+ render :nothing => true, :status => :not_found
+ rescue
+ render :nothing => true, :status => :internal_server_error
+ end
+ end
end
session :off
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 :check_api_writable, :only => [:create, :update, :delete]
+ before_filter :check_api_readable, :except => [:create, :update, :delete]
after_filter :compress_output
def create
- if request.put?
- relation = Relation.from_xml(request.raw_post, true)
-
- if relation
- if !relation.preconditions_ok?
- render :text => "", :status => :precondition_failed
- else
- relation.user_id = @user.id
- relation.save_with_history!
-
- render :text => relation.id.to_s, :content_type => "text/plain"
- end
+ begin
+ if request.put?
+ relation = Relation.from_xml(request.raw_post, true)
+
+ # We assume that an exception has been thrown if there was an error
+ # generating the relation
+ #if relation
+ relation.create_with_history @user
+ render :text => relation.id.to_s, :content_type => "text/plain"
+ #else
+ # render :text => "Couldn't get turn the input into a relation.", :status => :bad_request
+ #end
else
- render :nothing => true, :status => :bad_request
+ render :nothing => true, :status => :method_not_allowed
end
- else
- render :nothing => true, :status => :method_not_allowed
+ rescue OSM::APIError => ex
+ render ex.render_opts
end
end
end
def update
+ logger.debug request.raw_post
begin
relation = Relation.find(params[:id])
new_relation = Relation.from_xml(request.raw_post)
if new_relation and new_relation.id == relation.id
- if !new_relation.preconditions_ok?
- render :text => "", :status => :precondition_failed
- else
- relation.user_id = @user.id
- relation.tags = new_relation.tags
- relation.members = new_relation.members
- relation.visible = true
- relation.save_with_history!
-
- render :nothing => true
- end
+ relation.update_from new_relation, @user
+ render :text => relation.version.to_s, :content_type => "text/plain"
else
render :nothing => true, :status => :bad_request
end
rescue ActiveRecord::RecordNotFound
render :nothing => true, :status => :not_found
- rescue
- render :nothing => true, :status => :internal_server_error
+ rescue OSM::APIError => ex
+ render ex.render_opts
end
end
def delete
-#XXX check if member somewhere!
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
+ new_relation = Relation.from_xml(request.raw_post)
+ if new_relation and new_relation.id == relation.id
+ relation.delete_with_history!(new_relation, @user)
+ render :text => relation.version.to_s, :content_type => "text/plain"
else
- render :text => "", :status => :gone
+ render :nothing => true, :status => :bad_request
end
+ rescue OSM::APIError => ex
+ render ex.render_opts
rescue ActiveRecord::RecordNotFound
render :nothing => true, :status => :not_found
- rescue
- render :nothing => true, :status => :internal_server_error
end
end
# first collect nodes, ways, and relations referenced by this relation.
ways = Way.find_by_sql("select w.* from current_ways w,current_relation_members rm where "+
- "rm.member_type='way' and rm.member_id=w.id and rm.id=#{relation.id}");
+ "rm.member_type='Way' and rm.member_id=w.id and rm.id=#{relation.id}");
nodes = Node.find_by_sql("select n.* from current_nodes n,current_relation_members rm where "+
- "rm.member_type='node' and rm.member_id=n.id and rm.id=#{relation.id}");
+ "rm.member_type='Node' and rm.member_id=n.id and rm.id=#{relation.id}");
# note query is built to exclude self just in case.
relations = Relation.find_by_sql("select r.* from current_relations r,current_relation_members rm where "+
- "rm.member_type='relation' and rm.member_id=r.id and rm.id=#{relation.id} and r.id<>rm.id");
+ "rm.member_type='Relation' and rm.member_id=r.id and rm.id=#{relation.id} and r.id<>rm.id");
# now additionally collect nodes referenced by ways. Note how we recursively
# evaluate ways but NOT relations.
render :text => doc.to_s, :content_type => "text/xml"
else
-
- render :text => "", :status => :gone
+ render :nothing => true, :status => :gone
end
rescue ActiveRecord::RecordNotFound
render :text => doc.to_s, :content_type => "text/xml"
else
- render :nothing => true, :status => :bad_request
+ render :text => "You need to supply a comma separated list of ids.", :status => :bad_request
end
+ rescue ActiveRecord::RecordNotFound
+ render :text => "Could not find one of the relations", :status => :not_found
end
def relations_for_way
- relations_for_object("way")
+ relations_for_object("Way")
end
def relations_for_node
- relations_for_object("node")
+ relations_for_object("Node")
end
def relations_for_relation
- relations_for_object("relation")
+ relations_for_object("Relation")
end
def relations_for_object(objtype)
- relationids = RelationMember.find(:all, :conditions => ['member_type=? and member_id=?', objtype, params[:id]]).collect { |ws| ws.id }.uniq
+ relationids = RelationMember.find(:all, :conditions => ['member_type=? and member_id=?', objtype, params[:id]]).collect { |ws| ws.id[0] }.uniq
doc = OSM::API.new.get_xml_doc
Relation.find(relationids).each do |relation|
- doc.root << relation.to_xml_node
+ doc.root << relation.to_xml_node if relation.visible
end
render :text => doc.to_s, :content_type => "text/xml"
class TraceController < ApplicationController
layout 'site'
- before_filter :authorize_web
- before_filter :require_user, :only => [:mine, :edit, :delete, :make_public]
+ before_filter :authorize_web
+ before_filter :require_user, :only => [:mine, :create, :edit, :delete, :make_public]
before_filter :authorize, :only => [:api_details, :api_data, :api_create]
- before_filter :check_database_availability, :except => [:api_details, :api_data, :api_create]
- before_filter :check_read_availability, :only => [:api_details, :api_data, :api_create]
+ before_filter :check_database_readable, :except => [:api_details, :api_data, :api_create]
+ before_filter :check_database_writable, :only => [:create, :edit, :delete, :make_public]
+ before_filter :check_api_readable, :only => [:api_details, :api_data]
+ before_filter :check_api_writable, :only => [:api_create]
# Counts and selects pages of GPX traces for various criteria (by user, tags, public etc.).
# target_user - if set, specifies the user to fetch traces for. if not set will fetch all traces
# from display name, pick up user id if one user's traces only
display_name = params[:display_name]
if target_user.nil? and !display_name.blank?
- target_user = User.find(:first, :conditions => [ "visible = 1 and display_name = ?", display_name])
+ target_user = User.find(:first, :conditions => [ "visible = ? and display_name = ?", true, display_name])
end
# set title
# 4 - user's traces, not logged in as that user = all user's public traces
if target_user.nil? # all traces
if @user
- conditions = ["(gpx_files.public = 1 OR gpx_files.user_id = ?)", @user.id] #1
+ conditions = ["(gpx_files.public = ? OR gpx_files.user_id = ?)", true, @user.id] #1
else
- conditions = ["gpx_files.public = 1"] #2
+ conditions = ["gpx_files.public = ?", true] #2
end
else
if @user and @user == target_user
conditions = ["gpx_files.user_id = ?", @user.id] #3 (check vs user id, so no join + can't pick up non-public traces by changing name)
else
- conditions = ["gpx_files.public = 1 AND gpx_files.user_id = ?", target_user.id] #4
+ conditions = ["gpx_files.public = ? AND gpx_files.user_id = ?", true, target_user.id] #4
end
end
conditions[0] += " AND gpx_files.id IN (#{files.join(',')})"
end
- conditions[0] += " AND gpx_files.visible = 1"
+ conditions[0] += " AND gpx_files.visible = ?"
+ conditions << true
@trace_pages, @traces = paginate(:traces,
:include => [:user, :tags],
end
def create
- logger.info(params[:trace][:gpx_file].class.name)
- if params[:trace][:gpx_file].respond_to?(:read)
- do_create(params[:trace][:gpx_file], params[:trace][:tagstring],
- params[:trace][:description], params[:trace][:public])
+ if params[:trace]
+ logger.info(params[:trace][:gpx_file].class.name)
+ if params[:trace][:gpx_file].respond_to?(:read)
+ do_create(params[:trace][:gpx_file], params[:trace][:tagstring],
+ params[:trace][:description], params[:trace][:public])
- if @trace.id
- logger.info("id is #{@trace.id}")
- flash[:notice] = "Your GPX file has been uploaded and is awaiting insertion in to the database. This will usually happen within half an hour, and an email will be sent to you on completion."
+ if @trace.id
+ logger.info("id is #{@trace.id}")
+ flash[:notice] = "Your GPX file has been uploaded and is awaiting insertion in to the database. This will usually happen within half an hour, and an email will be sent to you on completion."
- redirect_to :action => 'mine'
+ redirect_to :action => 'mine'
+ end
+ else
+ @trace = Trace.new({:name => "Dummy",
+ :tagstring => params[:trace][:tagstring],
+ :description => params[:trace][:description],
+ :public => params[:trace][:public],
+ :inserted => false, :user => @user,
+ :timestamp => Time.now.getutc})
+ @trace.valid?
+ @trace.errors.add(:gpx_file, "can't be blank")
end
- else
- @trace = Trace.new({:name => "Dummy",
- :tagstring => params[:trace][:tagstring],
- :description => params[:trace][:description],
- :public => params[:trace][:public],
- :inserted => false, :user => @user,
- :timestamp => Time.now})
- @trace.valid?
- @trace.errors.add(:gpx_file, "can't be blank")
end
end
end
def georss
- conditions = ["gpx_files.public = 1"]
+ conditions = ["gpx_files.public = ?", true]
if params[:display_name]
conditions[0] += " AND users.display_name = ?"
def api_create
if request.post?
- do_create(params[:file], params[:tags], params[:description], params[:public])
-
- if @trace.id
- render :text => @trace.id.to_s, :content_type => "text/plain"
- elsif @trace.valid?
- render :nothing => true, :status => :internal_server_error
+ tags = params[:tags] || ""
+ description = params[:description] || ""
+ pub = params[:public] || false
+
+ if params[:file].respond_to?(:read)
+ do_create(params[:file], tags, description, pub)
+
+ if @trace.id
+ render :text => @trace.id.to_s, :content_type => "text/plain"
+ elsif @trace.valid?
+ render :nothing => true, :status => :internal_server_error
+ else
+ render :nothing => true, :status => :bad_request
+ end
else
render :nothing => true, :status => :bad_request
end
:public => public,
:inserted => true,
:user => @user,
- :timestamp => Time.now
+ :timestamp => Time.now.getutc
})
# Save the trace object
# Remove the file as we have failed to update the database
FileUtils.rm_f(filename)
end
+
+ # Finally save whether the user marked the trace as being public
+ if @trace.public?
+ if @user.trace_public_default.nil?
+ @user.preferences.create(:k => "gps.trace.public", :v => "default")
+ end
+ else
+ pref = @user.trace_public_default
+ pref.destroy unless pref.nil?
+ end
+
end
end
before_filter :authorize, :only => [:api_details, :api_gpx_files]
before_filter :authorize_web, :except => [:api_details, :api_gpx_files]
before_filter :require_user, :only => [:set_home, :account, :go_public, :make_friend, :remove_friend, :upload_image, :delete_image]
- before_filter :check_database_availability, :except => [:api_details, :api_gpx_files]
- before_filter :check_read_availability, :only => [:api_details, :api_gpx_files]
+ before_filter :check_database_readable, :except => [:api_details, :api_gpx_files]
+ before_filter :check_database_writable, :only => [:login, :new, :set_home, :account, :go_public, :make_friend, :remove_friend, :upload_image, :delete_image]
+ before_filter :check_api_readable, :only => [:api_details, :api_gpx_files]
filter_parameter_logging :password, :pass_crypt, :pass_crypt_confirmation
def lost_password
@title = 'lost password'
if params[:user] and params[:user][:email]
- user = User.find_by_email(params[:user][:email], :conditions => "visible = 1")
+ user = User.find_by_email(params[:user][:email], :conditions => {:visible => true})
if user
token = user.tokens.create
def new
@title = 'create account'
+ # The user is logged in already, so don't show them the signup page, instead
+ # send them to the home page
+ redirect_to :controller => 'site', :action => 'index' if session[:user]
end
def login
+ if session[:user]
+ # The user is logged in already, if the referer param exists, redirect them to that
+ if params[:referer]
+ redirect_to params[:referer]
+ else
+ redirect_to :controller => 'site', :action => 'index'
+ end
+ return
+ end
@title = 'login'
if params[:user]
email_or_display_name = params[:user][:email]
end
def view
- @this_user = User.find_by_display_name(params[:display_name], :conditions => "visible = 1")
+ @this_user = User.find_by_display_name(params[:display_name], :conditions => {:visible => true})
if @this_user
@title = @this_user.display_name
def make_friend
if params[:display_name]
name = params[:display_name]
- new_friend = User.find_by_display_name(name, :conditions => "visible = 1")
+ new_friend = User.find_by_display_name(name, :conditions => {:visible => true})
friend = Friend.new
friend.user_id = @user.id
friend.friend_user_id = new_friend.id
def remove_friend
if params[:display_name]
name = params[:display_name]
- friend = User.find_by_display_name(name, :conditions => "visible = 1")
+ friend = User.find_by_display_name(name, :conditions => {:visible => true})
if @user.is_friends_with?(friend)
Friend.delete_all "user_id = #{@user.id} AND friend_user_id = #{friend.id}"
flash[:notice] = "#{friend.display_name} was removed from your friends."
session :off
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 :check_api_writable, :only => [:create, :update, :delete]
+ before_filter :check_api_readable, :except => [:create, :update, :delete]
after_filter :compress_output
def create
- if request.put?
- way = Way.from_xml(request.raw_post, true)
-
- if way
- if !way.preconditions_ok?
- render :text => "", :status => :precondition_failed
- else
- way.user_id = @user.id
- way.save_with_history!
+ begin
+ if request.put?
+ way = Way.from_xml(request.raw_post, true)
+ if way
+ way.create_with_history @user
render :text => way.id.to_s, :content_type => "text/plain"
+ else
+ render :nothing => true, :status => :bad_request
end
else
- render :nothing => true, :status => :bad_request
+ render :nothing => true, :status => :method_not_allowed
end
- else
- render :nothing => true, :status => :method_not_allowed
+ rescue OSM::APIError => ex
+ logger.warn request.raw_post
+ render ex.render_opts
end
end
else
render :text => "", :status => :gone
end
+ rescue OSM::APIError => ex
+ render ex.render_opts
rescue ActiveRecord::RecordNotFound
render :nothing => true, :status => :not_found
end
new_way = Way.from_xml(request.raw_post)
if new_way and new_way.id == way.id
- if !new_way.preconditions_ok?
- 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
+ way.update_from(new_way, @user)
+ render :text => way.version.to_s, :content_type => "text/plain"
else
render :nothing => true, :status => :bad_request
end
+ rescue OSM::APIError => ex
+ logger.warn request.raw_post
+ render ex.render_opts
rescue ActiveRecord::RecordNotFound
render :nothing => true, :status => :not_found
end
def delete
begin
way = Way.find(params[:id])
- way.delete_with_relations_and_history(@user)
-
- # if we get here, all is fine, otherwise something will catch below.
- render :nothing => true
- rescue OSM::APIAlreadyDeletedError
- render :text => "", :status => :gone
- rescue OSM::APIPreconditionFailedError
- render :text => "", :status => :precondition_failed
+ new_way = Way.from_xml(request.raw_post)
+
+ if new_way and new_way.id == way.id
+ way.delete_with_history!(new_way, @user)
+ render :text => way.version.to_s, :content_type => "text/plain"
+ else
+ render :nothing => true, :status => :bad_request
+ end
+ rescue OSM::APIError => ex
+ render ex.render_opts
rescue ActiveRecord::RecordNotFound
render :nothing => true, :status => :not_found
end
if way.visible
nd_ids = way.nds + [-1]
- nodes = Node.find(:all, :conditions => "visible = 1 AND id IN (#{nd_ids.join(',')})")
+ nodes = Node.find(:all, :conditions => ["visible = ? AND id IN (#{nd_ids.join(',')})", true])
# Render
doc = OSM::API.new.get_xml_doc
end
end
+ ##
+ # returns all the ways which are currently using the node given in the
+ # :id parameter. note that this used to return deleted ways as well, but
+ # this seemed not to be the expected behaviour, so it was removed.
def ways_for_node
- wayids = WayNode.find(:all, :conditions => ['node_id = ?', params[:id]]).collect { |ws| ws.id[0] }.uniq
+ wayids = WayNode.find(:all,
+ :conditions => ['node_id = ?', params[:id]]
+ ).collect { |ws| ws.id[0] }.uniq
doc = OSM::API.new.get_xml_doc
Way.find(wayids).each do |way|
- doc.root << way.to_xml_node
+ doc.root << way.to_xml_node if way.visible
end
render :text => doc.to_s, :content_type => "text/xml"
<%= stylesheet_link_tag 'site' %>
<%= stylesheet_link_tag 'print', :media => "print" %>
<%= tag("link", { :rel => "search", :type => "application/opensearchdescription+xml", :title => "OpenStreetMap Search", :href => "/opensearch/osm.xml" }) %>
+ <%= tag("meta", { :name => "description", :content => "OpenStreetMap is the free wiki world map." }) %>
<%= yield :head %>
<title>OpenStreetMap<%= ' | '+ h(@title) if @title %></title>
</head>
<%
viewclass = ''
editclass = ''
+ historyclass = ''
exportclass = ''
traceclass = ''
viewclass = 'active' if params['controller'] == 'site' and params['action'] == 'index'
editclass = 'active' if params['controller'] == 'site' and params['action'] == 'edit'
+ historyclass = 'active' if params['controller'] == 'changeset' and params['action'] == 'list_bbox'
exportclass = 'active' if params['controller'] == 'site' and params['action'] == 'export'
traceclass = 'active' if params['controller'] == 'trace'
diaryclass = 'active' if params['controller'] == 'diary_entry'
%>
<li><%= link_to 'View', {:controller => 'site', :action => 'index'}, {:id => 'viewanchor', :title => 'view maps', :class => viewclass} %></li>
<li><%= link_to 'Edit', {:controller => 'site', :action => 'edit'}, {:id => 'editanchor', :title => 'edit maps', :class => editclass} %></li>
+ <li><%= link_to 'History', {:controller => 'history' }, {:id => 'historyanchor', :title => 'changeset history', :class => historyclass} %></li>
<% if params['controller'] == 'site' and (params['action'] == 'index' or params['action'] == 'export') %>
<li><%= link_to_remote 'Export', {:url => {:controller => 'export', :action => 'start'}}, {:id => 'exportanchor', :title => 'export map data', :class => exportclass, :href => url_for(:controller => 'site', :action => 'export')} %></li>
<% else %>
The OpenStreetMap database is currently offline while
essential database maintenance work is carried out.
</div>
- <% elsif OSM_STATUS == :api_readonly %>
+ <% elsif OSM_STATUS == :database_readonly or OSM_STATUS == :api_readonly %>
<div id="alert">
The OpenStreetMap database is currently in read-only mode while
essential database maintenance work is carried out.
</div>
<div id="cclogo" class="button" style="width: 88px">
- <a href="http://creativecommons.org/licenses/by-sa/2.0/"><img src="/images/cc_button.png" border="0" alt="" /></a>
+ <%= link_to image_tag("cc_button.png", :alt => "CC by-sa 2.0", :border => "0"), "http://creativecommons.org/licenses/by-sa/2.0/" %>
</div>
</center>
</div>
<p>The OpenStreetMap database is currently offline while
essential database maintenance work is carried out.
</p>
- <% elsif OSM_STATUS == :api_readonly %>
+ <% elsif OSM_STATUS == :database_readonly or OSM_STATUS == :api_readonly %>
<p>The OpenStreetMap database is currently in read-only mode while
essential database maintenance work is carried out.
</p>
<%= render :partial => 'sidebar', :locals => { :onopen => "resizeMap();", :onclose => "resizeMap();" } %>
<%= render :partial => 'search' %>
-<% session[:token] = @user.tokens.create.token unless session[:token] %>
+<%
+session[:token] = @user.tokens.create.token unless session[:token]
-<% if params['mlon'] and params['mlat'] %>
- <% lon = h(params['mlon']) %>
- <% lat = h(params['mlat']) %>
- <% zoom = h(params['zoom'] || '14') %>
-<% elsif @user and params['lon'].nil? and params['lat'].nil? and params['gpx'].nil? %>
- <% lon = @user.home_lon %>
- <% lat = @user.home_lat %>
- <% zoom = '14' %>
-<% else %>
- <% lon = h(params['lon'] || 'null') %>
- <% lat = h(params['lat'] || 'null') %>
- <% zoom = h(params['zoom'] || '14') %>
-<% end %>
+# Decide on a lat lon to initialise potlatch with. Various ways of doing this
+if params['lon'] and params['lat']
+ lon = h(params['lon'])
+ lat = h(params['lat'])
+ zoom = h(params['zoom'])
+
+elsif params['mlon'] and params['mlat']
+ lon = h(params['mlon'])
+ lat = h(params['mlat'])
+ zoom = h(params['zoom'])
+
+elsif params['gpx']
+ #use gpx id to locate (dealt with below)
+
+elsif cookies.key?("_osm_location")
+ lon,lat,zoom,layers = cookies["_osm_location"].split("|")
+
+elsif @user and !@user.home_lon.nil? and !@user.home_lat.nil?
+ lon = @user.home_lon
+ lat = @user.home_lat
+
+else
+ #catch all. Do nothing. lat=nil, lon=nil
+ #Currently this results in potlatch starting up at 0,0 (Atlantic ocean).
+end
+
+zoom='14' if zoom.nil?
+%>
<div id="map">
You need a Flash player to use Potlatch, the
ENV['RAILS_ENV'] ||= 'production'
# Specifies gem version of Rails to use when vendor/rails is not present
-RAILS_GEM_VERSION = '2.0.2' unless defined? RAILS_GEM_VERSION
+RAILS_GEM_VERSION = '2.1.2' unless defined? RAILS_GEM_VERSION
# Set the server URL
SERVER_URL = ENV['OSM_SERVER_URL'] || 'www.openstreetmap.org'
+# Set the generator
+GENERATOR = ENV['OSM_SERVER_GENERATOR'] || 'OpenStreetMap server'
+
# 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 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_readonly - database and site in read-only mode
# :database_offline - database offline with site in emergency mode
#
OSM_STATUS = :online
config.frameworks -= [ :active_record ]
end
+ # Specify gems that this application depends on.
+ # They can then be installed with "rake gems:install" on new installations.
+ # config.gem "bj"
+ # config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net"
+ # config.gem "aws-s3", :lib => "aws/s3"
+ config.gem 'composite_primary_keys', :version => '1.1.0'
+ config.gem 'libxml-ruby', :version => '>= 1.1.1', :lib => 'libxml'
+ config.gem 'rmagick', :lib => 'RMagick'
+ config.gem 'mysql'
+
# Only load the plugins named here, in the order given. By default, all plugins
# in vendor/plugins are loaded in alphabetical order.
# :all can be used as a placeholder for all plugins not explicitly named
# (create the session table with 'rake db:sessions:create')
config.action_controller.session_store = :sql_session_store
+ # We will use the old style of migrations, rather than the newer
+ # timestamped migrations that were introduced with Rails 2.1, as
+ # it will be confusing to have the numbered and timestamped migrations
+ # together in the same folder.
+ config.active_record.timestamped_migrations = false
+
# Use SQL instead of Active Record's schema dumper when creating the test database.
# This is necessary if your schema can't be completely dumped by the schema dumper,
# like if you have constraints or database-specific column types
# config.active_record.observers = :cacher, :garbage_collector
# Make Active Record use UTC-base instead of local time
- # config.active_record.default_timezone = :utc
+ config.active_record.default_timezone = :utc
end