Merge remote-tracking branch 'official/master' into saltation
[gitorious:taladars-gitorious-saltation.git] / app / controllers / merge_requests_controller.rb
1 # encoding: utf-8
2 #--
3 #   Copyright (C) 2010 Marko Peltola <marko@markopeltola.com>
4 #   Copyright (C) 2010 Tero Hänninen <tero.j.hanninen@jyu.fi>
5 #   Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies)
6 #   Copyright (C) 2008 Johan Sørensen <johan@johansorensen.com>
7 #   Copyright (C) 2008 David A. Cuadrado <krawek@gmail.com>
8 #   Copyright (C) 2008 Tim Dysinger <tim@dysinger.net>
9 #   Copyright (C) 2008 Tor Arne Vestbø <tavestbo@trolltech.com>
10 #   Copyright (C) 2009 Fabio Akita <fabio.akita@gmail.com>
11 #
12 #   This program is free software: you can redistribute it and/or modify
13 #   it under the terms of the GNU Affero General Public License as published by
14 #   the Free Software Foundation, either version 3 of the License, or
15 #   (at your option) any later version.
16 #
17 #   This program is distributed in the hope that it will be useful,
18 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
19 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 #   GNU Affero General Public License for more details.
21 #
22 #   You should have received a copy of the GNU Affero General Public License
23 #   along with this program.  If not, see <http://www.gnu.org/licenses/>.
24 #++
25
26 class MergeRequestsController < ApplicationController
27   before_filter :login_required, :except => [:index, :show, :direct_access,
28                                              :commit_status, :version]
29   before_filter :find_repository_owner, :except => [:oauth_return, :direct_access]
30   before_filter :find_repository, :except => [:oauth_return, :direct_access]
31   before_filter :require_view_right_to_repository, :except => [:oauth_return, :direct_access]
32   before_filter :find_merge_request,
33     :except => [:index, :show, :new, :create, :commit_list, :target_branches,
34                 :oauth_return, :direct_access, :terms_accepted]
35   before_filter :assert_merge_request_ownership,
36     :except => [:index, :show, :new, :create, :resolve, :commit_list,
37                 :target_branches, :oauth_return, :direct_access, :commit_status,
38                 :version, :terms_accepted]
39   before_filter :assert_merge_request_resolvable, :only => [:resolve]
40   renders_in_site_specific_context
41   
42   def index
43     @root = Breadcrumb::MergeRequests.new(@repository)
44     @open_merge_requests = @repository.merge_requests.from_filter(params[:status]) \
45       .paginate(:all, {
46         :page => params[:page],
47         :per_page => params[:per_page] || 50,
48         :order => "created_at desc"
49       })
50
51     @status_tags = @repository.merge_request_status_tags
52     @comment_count = @repository.comments.count
53     @atom_auto_discovery_url = url_for(:overwrite_params => { :format => "atom" })
54
55     respond_to do |wants|
56       wants.html
57       wants.xml  { render :xml => @open_merge_requests.to_xml }
58       wants.atom {  }
59     end
60   end
61   
62   def commit_list
63     @merge_request = @repository.proposed_merge_requests.new(params[:merge_request])
64     @merge_request.user = current_user
65     @commits = @merge_request.potential_commits
66     render :partial => "commit_list", :layout => false
67   end
68
69   def commit_status
70     @merge_request = @repository.merge_requests.public.find_by_sequence_number!(params[:id],
71                                                      :include => :target_repository)
72     result = @merge_request.commit_merged?(params[:commit_id]) ? 'true' : 'false'
73     render :text => result, :layout => false
74   end
75   
76   def target_branches
77     @merge_request = @repository.proposed_merge_requests.new(params[:merge_request])
78     @merge_request.user = current_user
79     @target_branches = @merge_request.target_branches_for_selection
80     render :partial => "target_branches", :layout => false
81   end
82   
83   def show
84     @merge_request = @repository.merge_requests.public.find_by_sequence_number!(params[:id],
85                       :include => [:source_repository, :target_repository])
86
87     @version = @merge_request.current_version_number
88     begin
89       @commits = @merge_request.commits_to_be_merged
90       @commit_comments = @merge_request.source_repository.comments.with_shas(@commits.map(&:id))
91     rescue Grit::Git::GitTimeout
92       @commits = []
93       @commit_comments = []
94       @git_timeout_occured = true
95       flash[:error] = "A Git timeout occured. Only metadata is being displayed"
96     end
97     respond_to do |wants|
98       wants.html {
99         if @merge_request.legacy?
100           render :template => 'merge_requests/legacy' and return
101         end # else render show.html as usual
102       }
103       wants.xml {render :xml => @merge_request.to_xml}
104       wants.patch {
105         render :text => @commits.collect(&:to_patch).join("\n"),
106           :content_type => "text/plain"
107       }
108     end
109   end
110   
111   def version
112     @merge_request = @repository.merge_requests.public.find_by_sequence_number!(params[:id],
113                       :include => [:source_repository, :target_repository])
114     render :partial => 'version', :layout => false, :locals => {
115       :version => @merge_request.version_number(params[:version].to_i)
116     }
117   end
118   
119   def new
120     @merge_request = @repository.proposed_merge_requests.new
121     @merge_request.user = current_user
122     @repositories = @owner.repositories.find(:all,
123       :conditions => ["id != ? AND kind != ? AND merge_requests_enabled = ?", @repository.id, Repository::KIND_TRACKING_REPO, true])
124     if first = @repository.parent || @repositories.first
125       @merge_request.target_repository_id = first.id
126     end
127     get_branches_and_commits_for_selection
128   end
129   
130   # This is a static URL the user returns to after accepting the terms
131   # for a merge request
132   def oauth_return
133     redirect_back_or_default '/'
134   end
135   
136   def terms_accepted
137     @merge_request = @repository.merge_requests.find_by_sequence_number!(params[:id])
138     if @merge_request.terms_accepted
139       @merge_request.add_creation_event(@owner, current_user)
140       if @merge_request.has_contribution_notice?
141         flash[:notice] = @merge_request.contribution_notice
142       end
143       flash[:success] = I18n.t("merge_requests_controller.create_success",
144         :name => @merge_request.target_repository.name)
145     else
146       flash[:error] = I18n.t("merge_requests_controller.need_contribution_agreement")
147     end
148     redirect_to project_repository_merge_request_path(@repository.project,
149       @repository, @merge_request)
150   end
151   
152   def create
153     @merge_request = @repository.proposed_merge_requests.new(params[:merge_request])
154     @merge_request.user = current_user
155     if @merge_request.save
156       merge_request_created
157     else
158       respond_to do |format|
159         format.html {
160           @repositories = @owner.repositories.find(:all,
161             :conditions => ["id != ?", @repository.id])
162           get_branches_and_commits_for_selection
163           render :action => "new"
164         }
165         format.xml { render :xml => @merge_request.errors, :status => :unprocessable_entity }
166       end
167     end
168   end
169   
170   def edit
171     @repositories = @owner.repositories.find(:all, :conditions => ["id != ?", @repository.id])
172     get_branches_and_commits_for_selection
173   end
174   
175   def update
176     @merge_request.attributes = params[:merge_request]
177     if @merge_request.save
178       @merge_request.publish_notification
179       flash[:success] = I18n.t "merge_requests_controller.update_success"
180       redirect_to [@owner, @repository, @merge_request]
181     else
182       @repositories = @owner.repositories.find(:all,
183         :conditions => ["id != ?", @repository.id])
184       get_branches_and_commits_for_selection
185       render :action => "edit"
186     end
187   end
188   
189   def destroy
190     @merge_request.destroy
191     flash[:success] = I18n.t "merge_requests_controller.destroy_success"
192     redirect_to [@owner, @repository]
193   end
194   
195   def direct_access
196     # One of the very rare occasions we find by id
197     merge_request = MergeRequest.find(params[:id])
198     project = merge_request.target_repository.project
199     repository = merge_request.target_repository
200     redirect_to [project, repository, merge_request]
201   end
202   
203   protected    
204     def find_repository
205       @repository = @owner.repositories.find_by_name_in_project!(params[:repository_id],
206         @containing_project)
207       @project = @repository.project
208     end
209     
210     def merge_request_created
211       if @merge_request.acceptance_of_terms_required?
212         request_token = obtain_oauth_request_token
213         @merge_request.oauth_request_token = request_token
214         @merge_request.save
215         returning_page = terms_accepted_project_repository_merge_request_path(
216             @repository.project, 
217             @merge_request.target_repository, 
218             @merge_request)
219         store_location(returning_page)
220         @redirection_path = request_token.authorize_url
221       else
222         flash[:success] = I18n.t("merge_requests_controller.create_success",
223                                    :name => @merge_request.target_repository.name)
224         @owner.create_event(Action::REQUEST_MERGE, @merge_request, current_user)
225         @merge_request.confirmed_by_user
226         @redirection_path =  repo_owner_path(@merge_request.reload.target_repository,
227           :project_repository_merge_request_path, @repository.project, 
228           @merge_request.target_repository, @merge_request)
229       end
230
231       respond_to do |format|
232         format.html {
233           redirect_to @redirection_path
234           return
235         }
236         format.xml { render :xml => @merge_request, :status => :created }      
237       end
238     end
239     
240     def find_merge_request
241       @merge_request = @repository.merge_requests.public.find_by_sequence_number!(params[:id])
242     end
243     
244     def obtain_oauth_request_token
245       request_token = @merge_request.oauth_consumer.get_request_token(
246         'user_login' => current_user.login,
247         'merge_request_id' => @merge_request.id)
248       return request_token
249     end
250     
251     def assert_merge_request_resolvable
252       unless @merge_request.resolvable_by?(current_user)
253         respond_to do |format|
254           flash[:error] = I18n.t "merge_requests_controller.assert_resolvable_error"
255           format.html { redirect_to([@owner, @repository, @merge_request]) }
256           format.xml  {
257             render :text => I18n.t("merge_requests_controller.assert_resolvable_error"),
258               :status => :forbidden
259           }
260         end
261         return
262       end
263     end
264     
265     def assert_merge_request_ownership
266       if @merge_request.user != current_user
267         respond_to do |format|
268           flash[:error] = I18n.t "merge_requests_controller.assert_ownership_error"
269           format.html { redirect_to([@owner, @repository]) }
270           format.xml  {
271             render :text => I18n.t("merge_requests_controller.assert_ownership_error"),
272               :status => :forbidden
273           }
274         end
275         return
276       end
277     end
278     
279     def get_branches_and_commits_for_selection
280       @source_branches = @repository.git.branches
281       @target_branches = @merge_request.target_branches_for_selection
282       @commits = @merge_request.commits_for_selection
283     end
284   
285 end