Merge remote-tracking branch 'official/master' into saltation
[gitorious:taladars-gitorious-saltation.git] / app / controllers / repositories_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) 2007, 2008 Johan Sørensen <johan@johansorensen.com>
7 #   Copyright (C) 2008 David A. Cuadrado <krawek@gmail.com>
8 #   Copyright (C) 2008 Tor Arne Vestbø <tavestbo@trolltech.com>
9 #   Copyright (C) 2009 Fabio Akita <fabio.akita@gmail.com>
10 #
11 #   This program is free software: you can redistribute it and/or modify
12 #   it under the terms of the GNU Affero General Public License as published by
13 #   the Free Software Foundation, either version 3 of the License, or
14 #   (at your option) any later version.
15 #
16 #   This program is distributed in the hope that it will be useful,
17 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
18 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 #   GNU Affero General Public License for more details.
20 #
21 #   You should have received a copy of the GNU Affero General Public License
22 #   along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 #++
24
25 class RepositoriesController < ApplicationController
26   before_filter :login_required,
27     :except => [:index, :show, :writable_by, :viewable_by, :config, :search_clones]
28   before_filter :find_repository_owner
29   before_filter :require_owner_adminship, :only => [:new, :create]
30   before_filter :find_and_require_repository_adminship,
31     :only => [:edit, :update, :confirm_delete, :destroy]
32   before_filter :find_and_require_repository_view_right, 
33     :except => [:index, :create, :new, :destroy, :config, :writable_by, :viewable_by]
34   before_filter :require_user_has_ssh_keys, :only => [:clone, :create_clone]
35   before_filter :only_projects_can_add_new_repositories, :only => [:new, :create]
36   skip_session :only => [:writable_by, :viewable_by, :config]
37   renders_in_site_specific_context :except => [:writable_by, :viewable_by, :config]
38
39   def index
40     if term = params[:filter]
41       @repositories = @project.search_repositories(term)
42     else
43       @repositories = @owner.repositories.regular.paginate(:all, :include => [:user, :events, :project], :page => params[:page])
44     end
45     @repositories.delete_if { |r| !r.can_be_viewed_by?(current_user) } if VisibilityFeatureEnabled
46     respond_to do |wants|
47       wants.html
48       wants.xml {render :xml => @repositories.to_xml}
49       wants.json {render :json => to_json(@repositories)}
50     end
51   end
52
53   def show
54     @repository = @owner.repositories.find_by_name_in_project!(params[:id], @containing_project)
55     @root = @repository
56     @events = @repository.events.top.paginate(:all, :page => params[:page],
57       :order => "created_at desc")
58
59     @atom_auto_discovery_url = repo_owner_path(@repository, :project_repository_path,
60                                   @repository.project, @repository, :format => :atom)
61     response.headers['Refresh'] = "5" unless @repository.ready
62
63     respond_to do |format|
64       format.html
65       format.xml  { render :xml => @repository }
66       format.atom {  }
67     end
68   end
69
70   def new
71     @repository = @project.repositories.new
72     @root = Breadcrumb::NewRepository.new(@project)
73     @repository.kind = Repository::KIND_PROJECT_REPO
74     @repository.owner = @project.owner
75     if @project.repositories.mainlines.count == 0
76       @repository.name = @project.slug
77     end
78   end
79
80   def create
81     @repository = @project.repositories.new(params[:repository])
82     @root = Breadcrumb::NewRepository.new(@project)
83     @repository.kind = Repository::KIND_PROJECT_REPO
84     @repository.owner = @project.owner
85     @repository.user = current_user
86     @repository.merge_requests_enabled = params[:repository][:merge_requests_enabled]
87     @repository.private_repo = VisibilityFeatureEnabled ? params[:repository][:private_repo] : false
88
89     if @repository.save
90       flash[:success] = I18n.t("repositories_controller.create_success")
91       redirect_to [@repository.project_or_owner, @repository]
92     else
93       render :action => "new"
94     end
95   end
96
97   undef_method :clone
98
99   def clone
100     @repository_to_clone = @repository
101     @root = Breadcrumb::CloneRepository.new(@repository_to_clone)
102     unless @repository_to_clone.has_commits?
103       flash[:error] = I18n.t "repositories_controller.new_clone_error"
104       redirect_to [@owner, @repository_to_clone]
105       return
106     end
107     @repository = Repository.new_by_cloning(@repository_to_clone, current_user.login)
108   end
109
110   def create_clone
111     @repository_to_clone = @repository
112     @root = Breadcrumb::CloneRepository.new(@repository_to_clone)
113     unless @repository_to_clone.has_commits?
114       respond_to do |format|
115         format.html do
116           flash[:error] = I18n.t "repositories_controller.create_clone_error"
117           redirect_to [@owner, @repository_to_clone]
118         end
119         format.xml do
120           render :text => I18n.t("repositories_controller.create_clone_error"),
121             :location => [@owner, @repository_to_clone], :status => :unprocessable_entity
122         end
123       end
124       return
125     end
126
127     @repository = Repository.new_by_cloning(@repository_to_clone)
128     @repository.name = params[:repository][:name]
129     @repository.user = current_user
130     case params[:repository][:owner_type]
131     when "User"
132       @repository.owner = current_user
133       @repository.kind = Repository::KIND_USER_REPO
134     when "Group"
135       @repository.owner = current_user.groups.find(params[:repository][:owner_id])
136       @repository.kind = Repository::KIND_TEAM_REPO
137     end
138
139     respond_to do |format|
140       if @repository.save
141         @owner.create_event(Action::CLONE_REPOSITORY, @repository, current_user, @repository_to_clone.id)
142
143         location = repo_owner_path(@repository, :project_repository_path, @owner, @repository)
144         format.html { redirect_to location }
145         format.xml  { render :xml => @repository, :status => :created, :location => location }
146       else
147         format.html { render :action => "clone" }
148         format.xml  { render :xml => @repository.errors, :status => :unprocessable_entity }
149       end
150     end
151   end
152
153   def edit
154     @root = Breadcrumb::EditRepository.new(@repository)
155     @groups = current_user.groups
156     @heads = @repository.git.heads
157   end
158
159   def search_clones
160     @repository = @owner.repositories.find_by_name_in_project!(params[:id], @containing_project)
161     @repositories = @repository.search_clones(params[:filter])
162     render :json => to_json(@repositories)
163   end
164
165   def update
166     @root = Breadcrumb::EditRepository.new(@repository)
167     @groups = current_user.groups
168     @heads = @repository.git.heads
169
170     Repository.transaction do
171       unless params[:repository][:owner_id].blank?
172         @repository.change_owner_to!(current_user.groups.find(params[:repository][:owner_id]))
173       end
174
175       @repository.head = params[:repository][:head]
176
177       @repository.log_changes_with_user(current_user) do
178         @repository.replace_value(:name, params[:repository][:name])
179         @repository.replace_value(:description, params[:repository][:description], true)
180       end
181       @repository.private_repo = params[:repository][:private_repo] if VisibilityFeatureEnabled
182       @repository.deny_force_pushing = params[:repository][:deny_force_pushing]
183       @repository.notify_committers_on_new_merge_request = params[:repository][:notify_committers_on_new_merge_request]
184       @repository.merge_requests_enabled = params[:repository][:merge_requests_enabled]
185       @repository.save!
186       flash[:success] = "Repository updated"
187       redirect_to [@repository.project_or_owner, @repository]
188     end
189   rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotFound
190     render :action => "edit"
191   end
192
193   # Used internally to check write permissions by gitorious
194   def writable_by
195     @repository = @owner.cloneable_repositories.find_by_name_in_project!(params[:id], @containing_project)
196     user = User.find_by_login(params[:username])
197
198     if user && result = /^refs\/merge-requests\/(\d+)$/.match(params[:git_path].to_s)
199       # git_path is a merge request
200       begin
201         if merge_request = @repository.merge_requests.find_by_sequence_number!(result[1]) and (merge_request.user == user)
202           render :text => "true" and return
203         end
204       rescue ActiveRecord::RecordNotFound # No such merge request
205       end
206     elsif user && user.can_write_to?(@repository)
207       render :text => "true" and return
208     end
209     render :text => 'false' and return
210   end
211
212   # Used internally to check view rights by gitorious
213   def viewable_by
214     @repository = @owner.repositories.find_by_name_in_project!(params[:id], @containing_project)
215     user = User.find_by_login(params[:username])
216
217     if user && @repository.can_be_viewed_by?(user)
218       render :text => "true" and return
219     end
220     render :text => "false" and return
221   end
222
223
224   def config
225     @repository = @owner.cloneable_repositories.find_by_name_in_project!(params[:id],
226       @containing_project)
227     config_data = "real_path:#{@repository.real_gitdir}\n"
228     config_data << "force_pushing_denied:"
229     config_data << (@repository.deny_force_pushing? ? 'true' : 'false')
230     headers["Cache-Control"] = "public, max-age=600"
231
232     render :text => config_data, :content_type => "text/x-yaml"
233   end
234
235   def confirm_delete
236     @repository = @owner.repositories.find_by_name_in_project!(params[:id], @containing_project)
237     unless @repository.can_be_deleted_by?(current_user)
238       flash[:error] = I18n.t "repositories_controller.adminship_error"
239       redirect_to(@owner) and return
240     end
241   end
242
243   def destroy
244     @repository = @owner.repositories.find_by_name_in_project!(params[:id], @containing_project)
245     if @repository.can_be_deleted_by?(current_user)
246       repo_name = @repository.name
247       flash[:notice] = I18n.t "repositories_controller.destroy_notice"
248       @repository.destroy
249       @repository.project.create_event(Action::DELETE_REPOSITORY, @owner,
250                                         current_user, repo_name)
251     else
252       flash[:error] = I18n.t "repositories_controller.destroy_error"
253     end
254     redirect_to @owner
255   end
256
257   private
258     def require_owner_adminship
259       unless @owner.admin?(current_user)
260         respond_denied_and_redirect_to(@owner)
261         return
262       end
263     end
264
265     def find_and_require_repository_adminship
266       @repository = @owner.repositories.find_by_name_in_project!(params[:id],
267         @containing_project)
268       unless @repository.admin?(current_user)
269         respond_denied_and_redirect_to(repo_owner_path(@repository,
270             :project_repository_path, @owner, @repository))
271         return
272       end
273     end
274
275     def find_and_require_repository_view_right
276       @repository = @owner.repositories.find_by_name_in_project!(params[:id],
277         @containing_project)
278       require_view_right_to_repository
279     end
280
281     def respond_denied_and_redirect_to(target)
282       respond_to do |format|
283         format.html {
284           flash[:error] = I18n.t "repositories_controller.adminship_error"
285           redirect_to(target)
286         }
287         format.xml  {
288           render :text => I18n.t( "repositories_controller.adminship_error"),
289           :status => :forbidden
290         }
291       end
292     end
293
294     def to_json(repositories)
295       repositories.map { |repo|
296         {
297           :name => repo.name,
298           :description => repo.description,
299           :uri => url_for(project_repository_path(@project, repo)),
300           :img => repo.owner.avatar? ?
301             repo.owner.avatar.url(:thumb) :
302             "/images/default_face.gif",
303           :owner => repo.owner.title,
304           :owner_type => repo.owner_type.downcase,
305           :owner_uri => url_for(repo.owner)
306         }
307       }.to_json
308     end
309
310     def only_projects_can_add_new_repositories
311       if !@owner.is_a?(Project)
312         respond_to do |format|
313           format.html {
314             flash[:error] = I18n.t("repositories_controller.only_projects_create_new_error")
315             redirect_to(@owner)
316           }
317           format.xml  {
318             render :text => I18n.t( "repositories_controller.only_projects_create_new_error"),
319                     :status => :forbidden
320           }
321         end
322         return
323       end
324     end
325 end