3114: Fix href in json response for generic #create action too. See 7485476.
[arvados.git] / apps / workbench / app / controllers / projects_controller.rb
1 class ProjectsController < ApplicationController
2   def model_class
3     Group
4   end
5
6   def find_object_by_uuid
7     if current_user and params[:uuid] == current_user.uuid
8       @object = current_user.dup
9       @object.uuid = current_user.uuid
10       class << @object
11         def name
12           'Home'
13         end
14         def description
15           ''
16         end
17         def attribute_editable? attr, *args
18           case attr
19           when 'description', 'name'
20             false
21           else
22             super
23           end
24         end
25       end
26     else
27       super
28     end
29   end
30
31   def index_pane_list
32     %w(Projects)
33   end
34
35   def show_pane_list
36     if @user_is_manager
37       %w(Data_collections Jobs_and_pipelines Pipeline_templates Subprojects Other_objects Sharing Advanced)
38     else
39       %w(Data_collections Jobs_and_pipelines Pipeline_templates Subprojects Other_objects Advanced)
40     end
41   end
42
43   def remove_item
44     params[:item_uuids] = [params[:item_uuid]]
45     remove_items
46     render template: 'projects/remove_items'
47   end
48
49   def remove_items
50     @removed_uuids = []
51     links = []
52     params[:item_uuids].collect { |uuid| ArvadosBase.find uuid }.each do |item|
53       if (item.class == Link and
54           item.link_class == 'name' and
55           item.tail_uuid == @object.uuid)
56         # Given uuid is a name link, linking an object to this
57         # project. First follow the link to find the item we're removing,
58         # then delete the link.
59         links << item
60         item = ArvadosBase.find item.head_uuid
61       else
62         # Given uuid is an object. Delete all names.
63         links += Link.where(tail_uuid: @object.uuid,
64                             head_uuid: item.uuid,
65                             link_class: 'name')
66       end
67       links.each do |link|
68         @removed_uuids << link.uuid
69         link.destroy
70       end
71       if item.owner_uuid == @object.uuid
72         # Object is owned by this project. Remove it from the project by
73         # changing owner to the current user.
74         item.update_attributes owner_uuid: current_user.uuid
75         @removed_uuids << item.uuid
76       end
77     end
78   end
79
80   def move_items
81     target_uuid = params['target']
82     uuids_to_add = session[:selected_move_items]
83
84     uuids_to_add.
85       collect { |x| ArvadosBase::resource_class_for_uuid(x) }.
86       uniq.
87       each do |resource_class|
88       resource_class.filter([['uuid','in',uuids_to_add]]).each do |dst|
89         if resource_class == Collection
90           dst = Link.new(owner_uuid: target_uuid,
91                          tail_uuid: target_uuid,
92                          head_uuid: dst.uuid,
93                          link_class: 'name',
94                          name: target_uuid)
95         else
96           dst.owner_uuid = target_uuid
97           dst.tail_uuid = target_uuid if dst.class == Link
98         end
99         dst.save!
100       end
101     end
102     session[:selected_move_items] = nil
103     redirect_to @object
104   end
105
106   def destroy
107     while (objects = Link.filter([['owner_uuid','=',@object.uuid],
108                                   ['tail_uuid','=',@object.uuid]])).any?
109       objects.each do |object|
110         object.destroy
111       end
112     end
113     while (objects = @object.contents(include_linked: false)).any?
114       objects.each do |object|
115         object.update_attributes! owner_uuid: current_user.uuid
116       end
117     end
118     if ArvadosBase::resource_class_for_uuid(@object.owner_uuid) == Group
119       params[:return_to] ||= group_path(@object.owner_uuid)
120     else
121       params[:return_to] ||= projects_path
122     end
123     super
124   end
125
126   def find_objects_for_index
127     @objects = all_projects
128     super
129   end
130
131   def show
132     if !@object
133       return render_not_found("object not found")
134     end
135     @objects = @object.contents(limit: 50,
136                                 include_linked: true,
137                                 filters: params[:filters],
138                                 offset: params[:offset] || 0)
139     @logs = Log.limit(10).filter([['object_uuid', '=', @object.uuid]])
140     @users = User.limit(10000).
141       select(["uuid", "is_active", "first_name", "last_name"]).
142       filter([['is_active', '=', 'true']])
143     @groups = Group.limit(10000).
144       select(["uuid", "name", "description"])
145
146     @user_is_manager = false
147     @share_links = []
148     if @object.uuid != current_user.uuid
149       begin
150         @share_links = Link.permissions_for(@object)
151         @user_is_manager = true
152       rescue ArvadosApiClient::AccessForbiddenException,
153         ArvadosApiClient::NotFoundException
154       end
155     end
156
157     @objects_and_names = get_objects_and_names @objects
158
159     if params[:partial]
160       respond_to do |f|
161         f.json {
162           render json: {
163             content: render_to_string(partial: 'show_contents_rows.html',
164                                       formats: [:html],
165                                       locals: {
166                                         objects_and_names: @objects_and_names,
167                                         project: @object
168                                       }),
169             next_page_href: (next_page_offset and
170                              url_for(offset: next_page_offset, filters: params[:filters], partial: true))
171           }
172         }
173       end
174     else
175       super
176     end
177   end
178
179   def create
180     @new_resource_attrs = (params['project'] || {}).merge(group_class: 'project')
181     @new_resource_attrs[:name] ||= 'New project'
182     super
183   end
184
185   def update
186     @updates = params['project']
187     super
188   end
189
190   helper_method :get_objects_and_names
191   def get_objects_and_names(objects)
192     objects_and_names = []
193     objects.each do |object|
194       if !(name_links = objects.links_for(object, 'name')).empty?
195         name_links.each do |name_link|
196           objects_and_names << [object, name_link]
197         end
198       elsif object.respond_to? :name
199         objects_and_names << [object, object]
200       else
201         objects_and_names << [object,
202                                Link.new(owner_uuid: @object.uuid,
203                                         tail_uuid: @object.uuid,
204                                         head_uuid: object.uuid,
205                                         link_class: "name",
206                                         name: "")]
207       end
208     end
209     objects_and_names
210   end
211
212   def share_with
213     if not params[:uuids].andand.any?
214       @errors = ["No user/group UUIDs specified to share with."]
215       return render_error(status: 422)
216     end
217     results = {"success" => [], "errors" => []}
218     params[:uuids].each do |shared_uuid|
219       begin
220         Link.create(tail_uuid: shared_uuid, link_class: "permission",
221                     name: "can_read", head_uuid: @object.uuid)
222       rescue ArvadosApiClient::ApiError => error
223         error_list = error.api_response.andand[:errors]
224         if error_list.andand.any?
225           results["errors"] += error_list.map { |e| "#{shared_uuid}: #{e}" }
226         else
227           error_code = error.api_status || "Bad status"
228           results["errors"] << "#{shared_uuid}: #{error_code} response"
229         end
230       else
231         results["success"] << shared_uuid
232       end
233     end
234     if results["errors"].empty?
235       results.delete("errors")
236       status = 200
237     else
238       status = 422
239     end
240     respond_to do |f|
241       f.json { render(json: results, status: status) }
242     end
243   end
244 end