fixed "full" and "relationships_for_object" modes
[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 :check_availability, :only => [:create, :update, :delete]
6
7   after_filter :compress_output
8
9   def create
10     if request.put?
11       relation = Relation.from_xml(request.raw_post, true)
12
13       if relation
14         if !relation.preconditions_ok?
15           render :nothing => true, :status => :precondition_failed
16         else
17           relation.user_id = @user.id
18
19           if relation.save_with_history
20             render :text => relation.id.to_s, :content_type => "text/plain"
21           else
22             render :text => "save error", :status => :internal_server_error
23           end
24         end
25       else
26         render :nothing => true, :status => :bad_request
27       end
28     else
29       render :nothing => true, :status => :method_not_allowed
30     end
31   end
32
33   def read
34     begin
35       relation = Relation.find(params[:id])
36
37       if relation.visible
38         render :text => relation.to_xml.to_s, :content_type => "text/xml"
39       else
40         render :nothing => true, :status => :gone
41       end
42     rescue ActiveRecord::RecordNotFound
43       render :nothing => true, :status => :not_found
44     rescue
45       render :nothing => true, :status => :internal_server_error
46     end
47   end
48
49   def update
50     begin
51       relation = Relation.find(params[:id])
52
53       if relation.visible
54         new_relation = Relation.from_xml(request.raw_post)
55
56         if new_relation and new_relation.id == relation.id
57           if !new_relation.preconditions_ok?
58             render :nothing => true, :status => :precondition_failed
59           else
60             relation.user_id = @user.id
61             relation.tags = new_relation.tags
62             relation.members = new_relation.members
63             relation.visible = true
64
65             if relation.save_with_history
66               render :nothing => true
67             else
68               render :nothing => true, :status => :internal_server_error
69             end
70           end
71         else
72           render :nothing => true, :status => :bad_request
73         end
74       else
75         render :nothing => true, :status => :gone
76       end
77     rescue ActiveRecord::RecordNotFound
78       render :nothing => true, :status => :not_found
79     rescue
80       render :nothing => true, :status => :internal_server_error
81     end
82   end
83
84   def delete
85 #XXX check if member somewhere!
86     begin
87       relation = Relation.find(params[:id])
88
89       if relation.visible
90         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]])
91           render :nothing => true, :status => :precondition_failed
92         else
93           relation.user_id = @user.id
94           relation.tags = []
95           relation.members = []
96           relation.visible = false
97
98           if relation.save_with_history
99             render :nothing => true
100           else
101             render :nothing => true, :status => :internal_server_error
102           end
103         end
104       else
105         render :nothing => true, :status => :gone
106       end
107     rescue ActiveRecord::RecordNotFound
108       render :nothing => true, :status => :not_found
109     rescue
110       render :nothing => true, :status => :internal_server_error
111     end
112   end
113
114   # -----------------------------------------------------------------
115   # full
116   # 
117   # input parameters: id
118   #
119   # returns XML representation of one relation object plus all its
120   # members, plus all nodes part of member ways
121   # -----------------------------------------------------------------
122   def full
123     begin
124       relation = Relation.find(params[:id])
125
126       if relation.visible
127
128         # first collect nodes, ways, and relations referenced by this relation.
129         
130         ways = Way.find_by_sql("select w.* from current_ways w,current_relation_members rm where "+
131             "rm.member_type='way' and rm.member_id=w.id and rm.id=#{relation.id}");
132         nodes = Node.find_by_sql("select n.* from current_nodes n,current_relation_members rm where "+
133             "rm.member_type='node' and rm.member_id=n.id and rm.id=#{relation.id}");
134         # note query is built to exclude self just in case.
135         relations = Relation.find_by_sql("select r.* from current_relations r,current_relation_members rm where "+
136             "rm.member_type='relation' and rm.member_id=r.id and rm.id=#{relation.id} and r.id<>rm.id");
137
138         # now additionally collect nodes referenced by ways. Note how we recursively 
139         # evaluate ways but NOT relations.
140
141         node_ids = nodes.collect {|node| node.id }
142         way_node_ids = ways.collect { |way|
143            way.way_nodes.collect { |way_node| way_node.node_id }
144         }
145         way_node_ids.flatten!
146         way_node_ids.uniq
147         way_node_ids -= node_ids
148         nodes += Node.find(way_node_ids)
149     
150         # create XML.
151         doc = OSM::API.new.get_xml_doc
152         user_display_name_cache = {}
153
154         nodes.each do |node|
155           if node.visible? # should be unnecessary if data is consistent.
156             doc.root << node.to_xml_node(user_display_name_cache)
157           end
158         end
159         ways.each do |way|
160           if way.visible? # should be unnecessary if data is consistent.
161             doc.root << way.to_xml_node(user_display_name_cache)
162           end
163         end
164         relations.each do |rel|
165           if rel.visible? # should be unnecessary if data is consistent.
166             doc.root << rel.to_xml_node(user_display_name_cache)
167           end
168         end
169         # finally add self and output
170         doc.root << relation.to_xml_node(user_display_name_cache)
171         render :text => doc.to_s, :content_type => "text/xml"
172
173       else
174
175         render :nothing => true, :status => :gone
176       end
177
178     rescue ActiveRecord::RecordNotFound
179       render :nothing => true, :status => :not_found
180
181     rescue
182       render :nothing => true, :status => :internal_server_error
183     end
184   end
185
186   def relations
187     ids = params['relations'].split(',').collect { |w| w.to_i }
188
189     if ids.length > 0
190       doc = OSM::API.new.get_xml_doc
191
192       Relation.find(ids).each do |relation|
193         doc.root << relation.to_xml_node
194       end
195
196       render :text => doc.to_s, :content_type => "text/xml"
197     else
198       render :nothing => true, :status => :bad_request
199     end
200   end
201
202   def relations_for_way
203     relations_for_object("way")
204   end
205   def relations_for_node
206     relations_for_object("node")
207   end
208   def relations_for_relation
209     relations_for_object("relation")
210   end
211
212   def relations_for_object(objtype)
213     relationids = RelationMember.find(:all, :conditions => ['member_type=? and member_id=?', objtype, params[:id]]).collect { |ws| ws.id }.uniq
214
215     if relationids.length > 0
216       doc = OSM::API.new.get_xml_doc
217
218       Relation.find(relationids).each do |relation|
219         doc.root << relation.to_xml_node
220       end
221
222       render :text => doc.to_s, :content_type => "text/xml"
223     else
224       render :nothing => true, :status => :not_found
225     end
226   end
227 end