Fix the changeset feed to put the description in as raw HTML so we
[rails.git] / app / controllers / relation_controller.rb
1 class RelationController < ApplicationController
2   require 'xml/libxml'
3
4   before_filter :authorize, :only => [:create, :update, :delete]
5   before_filter :require_public_data, :only => [:create, :update, :delete]
6   before_filter :check_api_writable, :only => [:create, :update, :delete]
7   before_filter :check_api_readable, :except => [:create, :update, :delete]
8   after_filter :compress_output
9   around_filter :api_call_handle_error, :api_call_timeout
10
11   def create
12     assert_method :put
13
14     relation = Relation.from_xml(request.raw_post, true)
15     
16     # We assume that an exception has been thrown if there was an error 
17     # generating the relation
18     #if relation
19     relation.create_with_history @user
20     render :text => relation.id.to_s, :content_type => "text/plain"
21     #else
22     # render :text => "Couldn't get turn the input into a relation.", :status => :bad_request
23     #end
24   end
25
26   def read
27     relation = Relation.find(params[:id])
28     response.headers['Last-Modified'] = relation.timestamp.rfc822
29     if relation.visible
30       render :text => relation.to_xml.to_s, :content_type => "text/xml"
31     else
32       render :text => "", :status => :gone
33     end
34   end
35
36   def update
37     logger.debug request.raw_post
38
39     relation = Relation.find(params[:id])
40     new_relation = Relation.from_xml(request.raw_post)
41     
42     if new_relation and new_relation.id == relation.id
43       relation.update_from new_relation, @user
44       render :text => relation.version.to_s, :content_type => "text/plain"
45     else
46       render :nothing => true, :status => :bad_request
47     end
48   end
49
50   def delete
51     relation = Relation.find(params[:id])
52     new_relation = Relation.from_xml(request.raw_post)
53     if new_relation and new_relation.id == relation.id
54       relation.delete_with_history!(new_relation, @user)
55       render :text => relation.version.to_s, :content_type => "text/plain"
56     else
57       render :nothing => true, :status => :bad_request
58     end
59   end
60
61   # -----------------------------------------------------------------
62   # full
63   # 
64   # input parameters: id
65   #
66   # returns XML representation of one relation object plus all its
67   # members, plus all nodes part of member ways
68   # -----------------------------------------------------------------
69   def full
70     relation = Relation.find(params[:id])
71     
72     if relation.visible
73       
74       # first find the ids of nodes, ways and relations referenced by this
75       # relation - note that we exclude this relation just in case.
76       
77       node_ids = relation.members.select { |m| m[0] == 'Node' }.map { |m| m[1] }
78       way_ids = relation.members.select { |m| m[0] == 'Way' }.map { |m| m[1] }
79       relation_ids = relation.members.select { |m| m[0] == 'Relation' and m[1] != relation.id }.map { |m| m[1] }
80       
81       # next load the relations and the ways.
82       
83       relations = Relation.find(relation_ids, :include => [:relation_tags])
84       ways = Way.find(way_ids, :include => [:way_nodes, :way_tags])
85       
86       # now additionally collect nodes referenced by ways. Note how we 
87       # recursively evaluate ways but NOT relations.
88       
89       way_node_ids = ways.collect { |way|
90         way.way_nodes.collect { |way_node| way_node.node_id }
91       }
92       node_ids += way_node_ids.flatten
93       nodes = Node.find(node_ids.uniq, :include => :node_tags)
94       
95       # create XML.
96       doc = OSM::API.new.get_xml_doc
97       visible_nodes = {}
98       visible_members = { "Node" => {}, "Way" => {}, "Relation" => {} }
99       changeset_cache = {}
100       user_display_name_cache = {}
101       
102       nodes.each do |node|
103         if node.visible? # should be unnecessary if data is consistent.
104           doc.root << node.to_xml_node(changeset_cache, user_display_name_cache)
105           visible_nodes[node.id] = node
106           visible_members["Node"][node.id] = true
107         end
108       end
109       ways.each do |way|
110         if way.visible? # should be unnecessary if data is consistent.
111           doc.root << way.to_xml_node(visible_nodes, changeset_cache, user_display_name_cache)
112           visible_members["Way"][way.id] = true
113         end
114       end
115       relations.each do |rel|
116         if rel.visible? # should be unnecessary if data is consistent.
117           doc.root << rel.to_xml_node(nil, changeset_cache, user_display_name_cache)
118           visible_members["Relation"][rel.id] = true
119         end
120       end
121       # finally add self and output
122       doc.root << relation.to_xml_node(visible_members, changeset_cache, user_display_name_cache)
123       render :text => doc.to_s, :content_type => "text/xml"
124       
125     else
126       render :nothing => true, :status => :gone
127     end
128   end
129
130   def relations
131     ids = params['relations'].split(',').collect { |w| w.to_i }
132
133     if ids.length > 0
134       doc = OSM::API.new.get_xml_doc
135
136       Relation.find(ids).each do |relation|
137         doc.root << relation.to_xml_node
138       end
139
140       render :text => doc.to_s, :content_type => "text/xml"
141     else
142       render :text => "You need to supply a comma separated list of ids.", :status => :bad_request
143     end
144   end
145
146   def relations_for_way
147     relations_for_object("Way")
148   end
149
150   def relations_for_node
151     relations_for_object("Node")
152   end
153
154   def relations_for_relation
155     relations_for_object("Relation")
156   end
157
158   def relations_for_object(objtype)
159     relationids = RelationMember.find(:all, :conditions => ['member_type=? and member_id=?', objtype, params[:id]]).collect { |ws| ws.id[0] }.uniq
160
161     doc = OSM::API.new.get_xml_doc
162
163     Relation.find(relationids).each do |relation|
164       doc.root << relation.to_xml_node if relation.visible
165     end
166
167     render :text => doc.to_s, :content_type => "text/xml"
168   end
169 end