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