##// END OF EJS Templates
Don't use a disabled SCM as a default repository SCM (#779)....
Jean-Philippe Lang -
r8533:abc0b0ea3e52
parent child
Show More
@@ -1,378 +1,378
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require 'SVG/Graph/Bar'
18 require 'SVG/Graph/Bar'
19 require 'SVG/Graph/BarHorizontal'
19 require 'SVG/Graph/BarHorizontal'
20 require 'digest/sha1'
20 require 'digest/sha1'
21
21
22 class ChangesetNotFound < Exception; end
22 class ChangesetNotFound < Exception; end
23 class InvalidRevisionParam < Exception; end
23 class InvalidRevisionParam < Exception; end
24
24
25 class RepositoriesController < ApplicationController
25 class RepositoriesController < ApplicationController
26 menu_item :repository
26 menu_item :repository
27 menu_item :settings, :only => [:new, :create, :edit, :update, :destroy, :committers]
27 menu_item :settings, :only => [:new, :create, :edit, :update, :destroy, :committers]
28 default_search_scope :changesets
28 default_search_scope :changesets
29
29
30 before_filter :find_project_by_project_id, :only => [:new, :create]
30 before_filter :find_project_by_project_id, :only => [:new, :create]
31 before_filter :find_repository, :only => [:edit, :update, :destroy, :committers]
31 before_filter :find_repository, :only => [:edit, :update, :destroy, :committers]
32 before_filter :find_project_repository, :except => [:new, :create, :edit, :update, :destroy, :committers]
32 before_filter :find_project_repository, :except => [:new, :create, :edit, :update, :destroy, :committers]
33 before_filter :authorize
33 before_filter :authorize
34 accept_rss_auth :revisions
34 accept_rss_auth :revisions
35
35
36 rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed
36 rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed
37
37
38 def new
38 def new
39 scm = params[:repository_scm] || Redmine::Scm::Base.all.first
39 scm = params[:repository_scm] || (Redmine::Scm::Base.all & Setting.enabled_scm).first
40 @repository = Repository.factory(scm)
40 @repository = Repository.factory(scm)
41 @repository.is_default = @project.repository.nil?
41 @repository.is_default = @project.repository.nil?
42 @repository.project = @project
42 @repository.project = @project
43 render :layout => !request.xhr?
43 render :layout => !request.xhr?
44 end
44 end
45
45
46 def create
46 def create
47 @repository = Repository.factory(params[:repository_scm], params[:repository])
47 @repository = Repository.factory(params[:repository_scm], params[:repository])
48 @repository.project = @project
48 @repository.project = @project
49 if request.post? && @repository.save
49 if request.post? && @repository.save
50 redirect_to settings_project_path(@project, :tab => 'repositories')
50 redirect_to settings_project_path(@project, :tab => 'repositories')
51 else
51 else
52 render :action => 'new'
52 render :action => 'new'
53 end
53 end
54 end
54 end
55
55
56 def edit
56 def edit
57 end
57 end
58
58
59 def update
59 def update
60 @repository.attributes = params[:repository]
60 @repository.attributes = params[:repository]
61 @repository.project = @project
61 @repository.project = @project
62 if request.put? && @repository.save
62 if request.put? && @repository.save
63 redirect_to settings_project_path(@project, :tab => 'repositories')
63 redirect_to settings_project_path(@project, :tab => 'repositories')
64 else
64 else
65 render :action => 'edit'
65 render :action => 'edit'
66 end
66 end
67 end
67 end
68
68
69 def committers
69 def committers
70 @committers = @repository.committers
70 @committers = @repository.committers
71 @users = @project.users
71 @users = @project.users
72 additional_user_ids = @committers.collect(&:last).collect(&:to_i) - @users.collect(&:id)
72 additional_user_ids = @committers.collect(&:last).collect(&:to_i) - @users.collect(&:id)
73 @users += User.find_all_by_id(additional_user_ids) unless additional_user_ids.empty?
73 @users += User.find_all_by_id(additional_user_ids) unless additional_user_ids.empty?
74 @users.compact!
74 @users.compact!
75 @users.sort!
75 @users.sort!
76 if request.post? && params[:committers].is_a?(Hash)
76 if request.post? && params[:committers].is_a?(Hash)
77 # Build a hash with repository usernames as keys and corresponding user ids as values
77 # Build a hash with repository usernames as keys and corresponding user ids as values
78 @repository.committer_ids = params[:committers].values.inject({}) {|h, c| h[c.first] = c.last; h}
78 @repository.committer_ids = params[:committers].values.inject({}) {|h, c| h[c.first] = c.last; h}
79 flash[:notice] = l(:notice_successful_update)
79 flash[:notice] = l(:notice_successful_update)
80 redirect_to settings_project_path(@project, :tab => 'repositories')
80 redirect_to settings_project_path(@project, :tab => 'repositories')
81 end
81 end
82 end
82 end
83
83
84 def destroy
84 def destroy
85 @repository.destroy if request.delete?
85 @repository.destroy if request.delete?
86 redirect_to settings_project_path(@project, :tab => 'repositories')
86 redirect_to settings_project_path(@project, :tab => 'repositories')
87 end
87 end
88
88
89 def show
89 def show
90 @repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
90 @repository.fetch_changesets if Setting.autofetch_changesets? && @path.empty?
91
91
92 @entries = @repository.entries(@path, @rev)
92 @entries = @repository.entries(@path, @rev)
93 @changeset = @repository.find_changeset_by_name(@rev)
93 @changeset = @repository.find_changeset_by_name(@rev)
94 if request.xhr?
94 if request.xhr?
95 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
95 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
96 else
96 else
97 (show_error_not_found; return) unless @entries
97 (show_error_not_found; return) unless @entries
98 @changesets = @repository.latest_changesets(@path, @rev)
98 @changesets = @repository.latest_changesets(@path, @rev)
99 @properties = @repository.properties(@path, @rev)
99 @properties = @repository.properties(@path, @rev)
100 @repositories = @project.repositories
100 @repositories = @project.repositories
101 render :action => 'show'
101 render :action => 'show'
102 end
102 end
103 end
103 end
104
104
105 alias_method :browse, :show
105 alias_method :browse, :show
106
106
107 def changes
107 def changes
108 @entry = @repository.entry(@path, @rev)
108 @entry = @repository.entry(@path, @rev)
109 (show_error_not_found; return) unless @entry
109 (show_error_not_found; return) unless @entry
110 @changesets = @repository.latest_changesets(@path, @rev, Setting.repository_log_display_limit.to_i)
110 @changesets = @repository.latest_changesets(@path, @rev, Setting.repository_log_display_limit.to_i)
111 @properties = @repository.properties(@path, @rev)
111 @properties = @repository.properties(@path, @rev)
112 @changeset = @repository.find_changeset_by_name(@rev)
112 @changeset = @repository.find_changeset_by_name(@rev)
113 end
113 end
114
114
115 def revisions
115 def revisions
116 @changeset_count = @repository.changesets.count
116 @changeset_count = @repository.changesets.count
117 @changeset_pages = Paginator.new self, @changeset_count,
117 @changeset_pages = Paginator.new self, @changeset_count,
118 per_page_option,
118 per_page_option,
119 params['page']
119 params['page']
120 @changesets = @repository.changesets.find(:all,
120 @changesets = @repository.changesets.find(:all,
121 :limit => @changeset_pages.items_per_page,
121 :limit => @changeset_pages.items_per_page,
122 :offset => @changeset_pages.current.offset,
122 :offset => @changeset_pages.current.offset,
123 :include => [:user, :repository, :parents])
123 :include => [:user, :repository, :parents])
124
124
125 respond_to do |format|
125 respond_to do |format|
126 format.html { render :layout => false if request.xhr? }
126 format.html { render :layout => false if request.xhr? }
127 format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
127 format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
128 end
128 end
129 end
129 end
130
130
131 def entry
131 def entry
132 @entry = @repository.entry(@path, @rev)
132 @entry = @repository.entry(@path, @rev)
133 (show_error_not_found; return) unless @entry
133 (show_error_not_found; return) unless @entry
134
134
135 # If the entry is a dir, show the browser
135 # If the entry is a dir, show the browser
136 (show; return) if @entry.is_dir?
136 (show; return) if @entry.is_dir?
137
137
138 @content = @repository.cat(@path, @rev)
138 @content = @repository.cat(@path, @rev)
139 (show_error_not_found; return) unless @content
139 (show_error_not_found; return) unless @content
140 if 'raw' == params[:format] ||
140 if 'raw' == params[:format] ||
141 (@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) ||
141 (@content.size && @content.size > Setting.file_max_size_displayed.to_i.kilobyte) ||
142 ! is_entry_text_data?(@content, @path)
142 ! is_entry_text_data?(@content, @path)
143 # Force the download
143 # Force the download
144 send_opt = { :filename => filename_for_content_disposition(@path.split('/').last) }
144 send_opt = { :filename => filename_for_content_disposition(@path.split('/').last) }
145 send_type = Redmine::MimeType.of(@path)
145 send_type = Redmine::MimeType.of(@path)
146 send_opt[:type] = send_type.to_s if send_type
146 send_opt[:type] = send_type.to_s if send_type
147 send_data @content, send_opt
147 send_data @content, send_opt
148 else
148 else
149 # Prevent empty lines when displaying a file with Windows style eol
149 # Prevent empty lines when displaying a file with Windows style eol
150 # TODO: UTF-16
150 # TODO: UTF-16
151 # Is this needs? AttachmentsController reads file simply.
151 # Is this needs? AttachmentsController reads file simply.
152 @content.gsub!("\r\n", "\n")
152 @content.gsub!("\r\n", "\n")
153 @changeset = @repository.find_changeset_by_name(@rev)
153 @changeset = @repository.find_changeset_by_name(@rev)
154 end
154 end
155 end
155 end
156
156
157 def is_entry_text_data?(ent, path)
157 def is_entry_text_data?(ent, path)
158 # UTF-16 contains "\x00".
158 # UTF-16 contains "\x00".
159 # It is very strict that file contains less than 30% of ascii symbols
159 # It is very strict that file contains less than 30% of ascii symbols
160 # in non Western Europe.
160 # in non Western Europe.
161 return true if Redmine::MimeType.is_type?('text', path)
161 return true if Redmine::MimeType.is_type?('text', path)
162 # Ruby 1.8.6 has a bug of integer divisions.
162 # Ruby 1.8.6 has a bug of integer divisions.
163 # http://apidock.com/ruby/v1_8_6_287/String/is_binary_data%3F
163 # http://apidock.com/ruby/v1_8_6_287/String/is_binary_data%3F
164 return false if ent.is_binary_data?
164 return false if ent.is_binary_data?
165 true
165 true
166 end
166 end
167 private :is_entry_text_data?
167 private :is_entry_text_data?
168
168
169 def annotate
169 def annotate
170 @entry = @repository.entry(@path, @rev)
170 @entry = @repository.entry(@path, @rev)
171 (show_error_not_found; return) unless @entry
171 (show_error_not_found; return) unless @entry
172
172
173 @annotate = @repository.scm.annotate(@path, @rev)
173 @annotate = @repository.scm.annotate(@path, @rev)
174 if @annotate.nil? || @annotate.empty?
174 if @annotate.nil? || @annotate.empty?
175 (render_error l(:error_scm_annotate); return)
175 (render_error l(:error_scm_annotate); return)
176 end
176 end
177 ann_buf_size = 0
177 ann_buf_size = 0
178 @annotate.lines.each do |buf|
178 @annotate.lines.each do |buf|
179 ann_buf_size += buf.size
179 ann_buf_size += buf.size
180 end
180 end
181 if ann_buf_size > Setting.file_max_size_displayed.to_i.kilobyte
181 if ann_buf_size > Setting.file_max_size_displayed.to_i.kilobyte
182 (render_error l(:error_scm_annotate_big_text_file); return)
182 (render_error l(:error_scm_annotate_big_text_file); return)
183 end
183 end
184 @changeset = @repository.find_changeset_by_name(@rev)
184 @changeset = @repository.find_changeset_by_name(@rev)
185 end
185 end
186
186
187 def revision
187 def revision
188 raise ChangesetNotFound if @rev.blank?
188 raise ChangesetNotFound if @rev.blank?
189 @changeset = @repository.find_changeset_by_name(@rev)
189 @changeset = @repository.find_changeset_by_name(@rev)
190 raise ChangesetNotFound unless @changeset
190 raise ChangesetNotFound unless @changeset
191
191
192 respond_to do |format|
192 respond_to do |format|
193 format.html
193 format.html
194 format.js {render :layout => false}
194 format.js {render :layout => false}
195 end
195 end
196 rescue ChangesetNotFound
196 rescue ChangesetNotFound
197 show_error_not_found
197 show_error_not_found
198 end
198 end
199
199
200 def diff
200 def diff
201 if params[:format] == 'diff'
201 if params[:format] == 'diff'
202 @diff = @repository.diff(@path, @rev, @rev_to)
202 @diff = @repository.diff(@path, @rev, @rev_to)
203 (show_error_not_found; return) unless @diff
203 (show_error_not_found; return) unless @diff
204 filename = "changeset_r#{@rev}"
204 filename = "changeset_r#{@rev}"
205 filename << "_r#{@rev_to}" if @rev_to
205 filename << "_r#{@rev_to}" if @rev_to
206 send_data @diff.join, :filename => "#{filename}.diff",
206 send_data @diff.join, :filename => "#{filename}.diff",
207 :type => 'text/x-patch',
207 :type => 'text/x-patch',
208 :disposition => 'attachment'
208 :disposition => 'attachment'
209 else
209 else
210 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
210 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
211 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
211 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
212
212
213 # Save diff type as user preference
213 # Save diff type as user preference
214 if User.current.logged? && @diff_type != User.current.pref[:diff_type]
214 if User.current.logged? && @diff_type != User.current.pref[:diff_type]
215 User.current.pref[:diff_type] = @diff_type
215 User.current.pref[:diff_type] = @diff_type
216 User.current.preference.save
216 User.current.preference.save
217 end
217 end
218 @cache_key = "repositories/diff/#{@repository.id}/" +
218 @cache_key = "repositories/diff/#{@repository.id}/" +
219 Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}-#{current_language}")
219 Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}-#{current_language}")
220 unless read_fragment(@cache_key)
220 unless read_fragment(@cache_key)
221 @diff = @repository.diff(@path, @rev, @rev_to)
221 @diff = @repository.diff(@path, @rev, @rev_to)
222 show_error_not_found unless @diff
222 show_error_not_found unless @diff
223 end
223 end
224
224
225 @changeset = @repository.find_changeset_by_name(@rev)
225 @changeset = @repository.find_changeset_by_name(@rev)
226 @changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil
226 @changeset_to = @rev_to ? @repository.find_changeset_by_name(@rev_to) : nil
227 @diff_format_revisions = @repository.diff_format_revisions(@changeset, @changeset_to)
227 @diff_format_revisions = @repository.diff_format_revisions(@changeset, @changeset_to)
228 end
228 end
229 end
229 end
230
230
231 def stats
231 def stats
232 end
232 end
233
233
234 def graph
234 def graph
235 data = nil
235 data = nil
236 case params[:graph]
236 case params[:graph]
237 when "commits_per_month"
237 when "commits_per_month"
238 data = graph_commits_per_month(@repository)
238 data = graph_commits_per_month(@repository)
239 when "commits_per_author"
239 when "commits_per_author"
240 data = graph_commits_per_author(@repository)
240 data = graph_commits_per_author(@repository)
241 end
241 end
242 if data
242 if data
243 headers["Content-Type"] = "image/svg+xml"
243 headers["Content-Type"] = "image/svg+xml"
244 send_data(data, :type => "image/svg+xml", :disposition => "inline")
244 send_data(data, :type => "image/svg+xml", :disposition => "inline")
245 else
245 else
246 render_404
246 render_404
247 end
247 end
248 end
248 end
249
249
250 private
250 private
251
251
252 def find_repository
252 def find_repository
253 @repository = Repository.find(params[:id])
253 @repository = Repository.find(params[:id])
254 @project = @repository.project
254 @project = @repository.project
255 rescue ActiveRecord::RecordNotFound
255 rescue ActiveRecord::RecordNotFound
256 render_404
256 render_404
257 end
257 end
258
258
259 REV_PARAM_RE = %r{\A[a-f0-9]*\Z}i
259 REV_PARAM_RE = %r{\A[a-f0-9]*\Z}i
260
260
261 def find_project_repository
261 def find_project_repository
262 @project = Project.find(params[:id])
262 @project = Project.find(params[:id])
263 if params[:repository_id].present?
263 if params[:repository_id].present?
264 @repository = @project.repositories.find_by_identifier_param(params[:repository_id])
264 @repository = @project.repositories.find_by_identifier_param(params[:repository_id])
265 else
265 else
266 @repository = @project.repository
266 @repository = @project.repository
267 end
267 end
268 (render_404; return false) unless @repository
268 (render_404; return false) unless @repository
269 @path = params[:path].join('/') unless params[:path].nil?
269 @path = params[:path].join('/') unless params[:path].nil?
270 @path ||= ''
270 @path ||= ''
271 @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].to_s.strip
271 @rev = params[:rev].blank? ? @repository.default_branch : params[:rev].to_s.strip
272 @rev_to = params[:rev_to]
272 @rev_to = params[:rev_to]
273
273
274 unless @rev.to_s.match(REV_PARAM_RE) && @rev_to.to_s.match(REV_PARAM_RE)
274 unless @rev.to_s.match(REV_PARAM_RE) && @rev_to.to_s.match(REV_PARAM_RE)
275 if @repository.branches.blank?
275 if @repository.branches.blank?
276 raise InvalidRevisionParam
276 raise InvalidRevisionParam
277 end
277 end
278 end
278 end
279 rescue ActiveRecord::RecordNotFound
279 rescue ActiveRecord::RecordNotFound
280 render_404
280 render_404
281 rescue InvalidRevisionParam
281 rescue InvalidRevisionParam
282 show_error_not_found
282 show_error_not_found
283 end
283 end
284
284
285 def show_error_not_found
285 def show_error_not_found
286 render_error :message => l(:error_scm_not_found), :status => 404
286 render_error :message => l(:error_scm_not_found), :status => 404
287 end
287 end
288
288
289 # Handler for Redmine::Scm::Adapters::CommandFailed exception
289 # Handler for Redmine::Scm::Adapters::CommandFailed exception
290 def show_error_command_failed(exception)
290 def show_error_command_failed(exception)
291 render_error l(:error_scm_command_failed, exception.message)
291 render_error l(:error_scm_command_failed, exception.message)
292 end
292 end
293
293
294 def graph_commits_per_month(repository)
294 def graph_commits_per_month(repository)
295 @date_to = Date.today
295 @date_to = Date.today
296 @date_from = @date_to << 11
296 @date_from = @date_to << 11
297 @date_from = Date.civil(@date_from.year, @date_from.month, 1)
297 @date_from = Date.civil(@date_from.year, @date_from.month, 1)
298 commits_by_day = repository.changesets.count(
298 commits_by_day = repository.changesets.count(
299 :all, :group => :commit_date,
299 :all, :group => :commit_date,
300 :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
300 :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
301 commits_by_month = [0] * 12
301 commits_by_month = [0] * 12
302 commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
302 commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
303
303
304 changes_by_day = repository.changes.count(
304 changes_by_day = repository.changes.count(
305 :all, :group => :commit_date,
305 :all, :group => :commit_date,
306 :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
306 :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
307 changes_by_month = [0] * 12
307 changes_by_month = [0] * 12
308 changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
308 changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
309
309
310 fields = []
310 fields = []
311 12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)}
311 12.times {|m| fields << month_name(((Date.today.month - 1 - m) % 12) + 1)}
312
312
313 graph = SVG::Graph::Bar.new(
313 graph = SVG::Graph::Bar.new(
314 :height => 300,
314 :height => 300,
315 :width => 800,
315 :width => 800,
316 :fields => fields.reverse,
316 :fields => fields.reverse,
317 :stack => :side,
317 :stack => :side,
318 :scale_integers => true,
318 :scale_integers => true,
319 :step_x_labels => 2,
319 :step_x_labels => 2,
320 :show_data_values => false,
320 :show_data_values => false,
321 :graph_title => l(:label_commits_per_month),
321 :graph_title => l(:label_commits_per_month),
322 :show_graph_title => true
322 :show_graph_title => true
323 )
323 )
324
324
325 graph.add_data(
325 graph.add_data(
326 :data => commits_by_month[0..11].reverse,
326 :data => commits_by_month[0..11].reverse,
327 :title => l(:label_revision_plural)
327 :title => l(:label_revision_plural)
328 )
328 )
329
329
330 graph.add_data(
330 graph.add_data(
331 :data => changes_by_month[0..11].reverse,
331 :data => changes_by_month[0..11].reverse,
332 :title => l(:label_change_plural)
332 :title => l(:label_change_plural)
333 )
333 )
334
334
335 graph.burn
335 graph.burn
336 end
336 end
337
337
338 def graph_commits_per_author(repository)
338 def graph_commits_per_author(repository)
339 commits_by_author = repository.changesets.count(:all, :group => :committer)
339 commits_by_author = repository.changesets.count(:all, :group => :committer)
340 commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}
340 commits_by_author.to_a.sort! {|x, y| x.last <=> y.last}
341
341
342 changes_by_author = repository.changes.count(:all, :group => :committer)
342 changes_by_author = repository.changes.count(:all, :group => :committer)
343 h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
343 h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
344
344
345 fields = commits_by_author.collect {|r| r.first}
345 fields = commits_by_author.collect {|r| r.first}
346 commits_data = commits_by_author.collect {|r| r.last}
346 commits_data = commits_by_author.collect {|r| r.last}
347 changes_data = commits_by_author.collect {|r| h[r.first] || 0}
347 changes_data = commits_by_author.collect {|r| h[r.first] || 0}
348
348
349 fields = fields + [""]*(10 - fields.length) if fields.length<10
349 fields = fields + [""]*(10 - fields.length) if fields.length<10
350 commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
350 commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
351 changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
351 changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
352
352
353 # Remove email adress in usernames
353 # Remove email adress in usernames
354 fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
354 fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
355
355
356 graph = SVG::Graph::BarHorizontal.new(
356 graph = SVG::Graph::BarHorizontal.new(
357 :height => 400,
357 :height => 400,
358 :width => 800,
358 :width => 800,
359 :fields => fields,
359 :fields => fields,
360 :stack => :side,
360 :stack => :side,
361 :scale_integers => true,
361 :scale_integers => true,
362 :show_data_values => false,
362 :show_data_values => false,
363 :rotate_y_labels => false,
363 :rotate_y_labels => false,
364 :graph_title => l(:label_commits_per_author),
364 :graph_title => l(:label_commits_per_author),
365 :show_graph_title => true
365 :show_graph_title => true
366 )
366 )
367 graph.add_data(
367 graph.add_data(
368 :data => commits_data,
368 :data => commits_data,
369 :title => l(:label_revision_plural)
369 :title => l(:label_revision_plural)
370 )
370 )
371 graph.add_data(
371 graph.add_data(
372 :data => changes_data,
372 :data => changes_data,
373 :title => l(:label_change_plural)
373 :title => l(:label_change_plural)
374 )
374 )
375 graph.burn
375 graph.burn
376 end
376 end
377 end
377 end
378
378
@@ -1,203 +1,219
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19 require 'repositories_controller'
19 require 'repositories_controller'
20
20
21 # Re-raise errors caught by the controller.
21 # Re-raise errors caught by the controller.
22 class RepositoriesController; def rescue_action(e) raise e end; end
22 class RepositoriesController; def rescue_action(e) raise e end; end
23
23
24 class RepositoriesControllerTest < ActionController::TestCase
24 class RepositoriesControllerTest < ActionController::TestCase
25 fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules,
25 fixtures :projects, :users, :roles, :members, :member_roles, :enabled_modules,
26 :repositories, :issues, :issue_statuses, :changesets, :changes,
26 :repositories, :issues, :issue_statuses, :changesets, :changes,
27 :issue_categories, :enumerations, :custom_fields, :custom_values, :trackers
27 :issue_categories, :enumerations, :custom_fields, :custom_values, :trackers
28
28
29 def setup
29 def setup
30 @controller = RepositoriesController.new
30 @controller = RepositoriesController.new
31 @request = ActionController::TestRequest.new
31 @request = ActionController::TestRequest.new
32 @response = ActionController::TestResponse.new
32 @response = ActionController::TestResponse.new
33 User.current = nil
33 User.current = nil
34 end
34 end
35
35
36 def test_new
36 def test_new
37 @request.session[:user_id] = 1
37 @request.session[:user_id] = 1
38 get :new, :project_id => 'subproject1'
38 get :new, :project_id => 'subproject1'
39 assert_response :success
39 assert_response :success
40 assert_template 'new'
40 assert_template 'new'
41 assert_kind_of Repository::Subversion, assigns(:repository)
41 assert_kind_of Repository::Subversion, assigns(:repository)
42 assert assigns(:repository).new_record?
42 assert assigns(:repository).new_record?
43 assert_tag 'input', :attributes => {:name => 'repository[url]'}
43 assert_tag 'input', :attributes => {:name => 'repository[url]'}
44 end
44 end
45
45
46 def test_new_should_propose_enabled_scm_only
47 @request.session[:user_id] = 1
48 with_settings :enabled_scm => ['Mercurial', 'Git'] do
49 get :new, :project_id => 'subproject1'
50 end
51 assert_response :success
52 assert_template 'new'
53 assert_kind_of Repository::Mercurial, assigns(:repository)
54 assert_tag 'select', :attributes => {:name => 'repository_scm'},
55 :children => {:count => 3}
56 assert_tag 'select', :attributes => {:name => 'repository_scm'},
57 :child => {:tag => 'option', :attributes => {:value => 'Mercurial', :selected => 'selected'}}
58 assert_tag 'select', :attributes => {:name => 'repository_scm'},
59 :child => {:tag => 'option', :attributes => {:value => 'Git', :selected => nil}}
60 end
61
46 def test_create
62 def test_create
47 @request.session[:user_id] = 1
63 @request.session[:user_id] = 1
48 assert_difference 'Repository.count' do
64 assert_difference 'Repository.count' do
49 post :create, :project_id => 'subproject1',
65 post :create, :project_id => 'subproject1',
50 :repository_scm => 'Subversion',
66 :repository_scm => 'Subversion',
51 :repository => {:url => 'file:///test', :is_default => '1', :identifier => ''}
67 :repository => {:url => 'file:///test', :is_default => '1', :identifier => ''}
52 end
68 end
53 assert_response 302
69 assert_response 302
54 repository = Repository.first(:order => 'id DESC')
70 repository = Repository.first(:order => 'id DESC')
55 assert_kind_of Repository::Subversion, repository
71 assert_kind_of Repository::Subversion, repository
56 assert_equal 'file:///test', repository.url
72 assert_equal 'file:///test', repository.url
57 end
73 end
58
74
59 def test_create_with_failure
75 def test_create_with_failure
60 @request.session[:user_id] = 1
76 @request.session[:user_id] = 1
61 assert_no_difference 'Repository.count' do
77 assert_no_difference 'Repository.count' do
62 post :create, :project_id => 'subproject1',
78 post :create, :project_id => 'subproject1',
63 :repository_scm => 'Subversion',
79 :repository_scm => 'Subversion',
64 :repository => {:url => 'invalid'}
80 :repository => {:url => 'invalid'}
65 end
81 end
66 assert_response :success
82 assert_response :success
67 assert_template 'new'
83 assert_template 'new'
68 assert_kind_of Repository::Subversion, assigns(:repository)
84 assert_kind_of Repository::Subversion, assigns(:repository)
69 assert assigns(:repository).new_record?
85 assert assigns(:repository).new_record?
70 end
86 end
71
87
72 def test_edit
88 def test_edit
73 @request.session[:user_id] = 1
89 @request.session[:user_id] = 1
74 get :edit, :id => 11
90 get :edit, :id => 11
75 assert_response :success
91 assert_response :success
76 assert_template 'edit'
92 assert_template 'edit'
77 assert_equal Repository.find(11), assigns(:repository)
93 assert_equal Repository.find(11), assigns(:repository)
78 assert_tag 'input', :attributes => {:name => 'repository[url]', :value => 'svn://localhost/test'}
94 assert_tag 'input', :attributes => {:name => 'repository[url]', :value => 'svn://localhost/test'}
79 end
95 end
80
96
81 def test_update
97 def test_update
82 @request.session[:user_id] = 1
98 @request.session[:user_id] = 1
83 put :update, :id => 11, :repository => {:password => 'test_update'}
99 put :update, :id => 11, :repository => {:password => 'test_update'}
84 assert_response 302
100 assert_response 302
85 assert_equal 'test_update', Repository.find(11).password
101 assert_equal 'test_update', Repository.find(11).password
86 end
102 end
87
103
88 def test_update_with_failure
104 def test_update_with_failure
89 @request.session[:user_id] = 1
105 @request.session[:user_id] = 1
90 put :update, :id => 11, :repository => {:password => 'x'*260}
106 put :update, :id => 11, :repository => {:password => 'x'*260}
91 assert_response :success
107 assert_response :success
92 assert_template 'edit'
108 assert_template 'edit'
93 assert_equal Repository.find(11), assigns(:repository)
109 assert_equal Repository.find(11), assigns(:repository)
94 end
110 end
95
111
96 def test_destroy
112 def test_destroy
97 @request.session[:user_id] = 1
113 @request.session[:user_id] = 1
98 assert_difference 'Repository.count', -1 do
114 assert_difference 'Repository.count', -1 do
99 delete :destroy, :id => 11
115 delete :destroy, :id => 11
100 end
116 end
101 assert_response 302
117 assert_response 302
102 assert_nil Repository.find_by_id(11)
118 assert_nil Repository.find_by_id(11)
103 end
119 end
104
120
105 def test_revisions
121 def test_revisions
106 get :revisions, :id => 1
122 get :revisions, :id => 1
107 assert_response :success
123 assert_response :success
108 assert_template 'revisions'
124 assert_template 'revisions'
109 assert_equal Repository.find(10), assigns(:repository)
125 assert_equal Repository.find(10), assigns(:repository)
110 assert_not_nil assigns(:changesets)
126 assert_not_nil assigns(:changesets)
111 end
127 end
112
128
113 def test_revisions_for_other_repository
129 def test_revisions_for_other_repository
114 repository = Repository::Subversion.create!(:project_id => 1, :identifier => 'foo', :url => 'file:///foo')
130 repository = Repository::Subversion.create!(:project_id => 1, :identifier => 'foo', :url => 'file:///foo')
115
131
116 get :revisions, :id => 1, :repository_id => 'foo'
132 get :revisions, :id => 1, :repository_id => 'foo'
117 assert_response :success
133 assert_response :success
118 assert_template 'revisions'
134 assert_template 'revisions'
119 assert_equal repository, assigns(:repository)
135 assert_equal repository, assigns(:repository)
120 assert_not_nil assigns(:changesets)
136 assert_not_nil assigns(:changesets)
121 end
137 end
122
138
123 def test_revisions_for_invalid_repository
139 def test_revisions_for_invalid_repository
124 get :revisions, :id => 1, :repository_id => 'foo'
140 get :revisions, :id => 1, :repository_id => 'foo'
125 assert_response 404
141 assert_response 404
126 end
142 end
127
143
128 def test_revision
144 def test_revision
129 get :revision, :id => 1, :rev => 1
145 get :revision, :id => 1, :rev => 1
130 assert_response :success
146 assert_response :success
131 assert_not_nil assigns(:changeset)
147 assert_not_nil assigns(:changeset)
132 assert_equal "1", assigns(:changeset).revision
148 assert_equal "1", assigns(:changeset).revision
133 end
149 end
134
150
135 def test_revision_with_before_nil_and_afer_normal
151 def test_revision_with_before_nil_and_afer_normal
136 get :revision, {:id => 1, :rev => 1}
152 get :revision, {:id => 1, :rev => 1}
137 assert_response :success
153 assert_response :success
138 assert_template 'revision'
154 assert_template 'revision'
139 assert_no_tag :tag => "div", :attributes => { :class => "contextual" },
155 assert_no_tag :tag => "div", :attributes => { :class => "contextual" },
140 :child => { :tag => "a", :attributes => { :href => '/projects/ecookbook/repository/revisions/0'}
156 :child => { :tag => "a", :attributes => { :href => '/projects/ecookbook/repository/revisions/0'}
141 }
157 }
142 assert_tag :tag => "div", :attributes => { :class => "contextual" },
158 assert_tag :tag => "div", :attributes => { :class => "contextual" },
143 :child => { :tag => "a", :attributes => { :href => '/projects/ecookbook/repository/revisions/2'}
159 :child => { :tag => "a", :attributes => { :href => '/projects/ecookbook/repository/revisions/2'}
144 }
160 }
145 end
161 end
146
162
147 def test_graph_commits_per_month
163 def test_graph_commits_per_month
148 get :graph, :id => 1, :graph => 'commits_per_month'
164 get :graph, :id => 1, :graph => 'commits_per_month'
149 assert_response :success
165 assert_response :success
150 assert_equal 'image/svg+xml', @response.content_type
166 assert_equal 'image/svg+xml', @response.content_type
151 end
167 end
152
168
153 def test_graph_commits_per_author
169 def test_graph_commits_per_author
154 get :graph, :id => 1, :graph => 'commits_per_author'
170 get :graph, :id => 1, :graph => 'commits_per_author'
155 assert_response :success
171 assert_response :success
156 assert_equal 'image/svg+xml', @response.content_type
172 assert_equal 'image/svg+xml', @response.content_type
157 end
173 end
158
174
159 def test_get_committers
175 def test_get_committers
160 @request.session[:user_id] = 2
176 @request.session[:user_id] = 2
161 # add a commit with an unknown user
177 # add a commit with an unknown user
162 Changeset.create!(
178 Changeset.create!(
163 :repository => Project.find(1).repository,
179 :repository => Project.find(1).repository,
164 :committer => 'foo',
180 :committer => 'foo',
165 :committed_on => Time.now,
181 :committed_on => Time.now,
166 :revision => 100,
182 :revision => 100,
167 :comments => 'Committed by foo.'
183 :comments => 'Committed by foo.'
168 )
184 )
169
185
170 get :committers, :id => 10
186 get :committers, :id => 10
171 assert_response :success
187 assert_response :success
172 assert_template 'committers'
188 assert_template 'committers'
173
189
174 assert_tag :td, :content => 'dlopper',
190 assert_tag :td, :content => 'dlopper',
175 :sibling => { :tag => 'td',
191 :sibling => { :tag => 'td',
176 :child => { :tag => 'select', :attributes => { :name => %r{^committers\[\d+\]\[\]$} },
192 :child => { :tag => 'select', :attributes => { :name => %r{^committers\[\d+\]\[\]$} },
177 :child => { :tag => 'option', :content => 'Dave Lopper',
193 :child => { :tag => 'option', :content => 'Dave Lopper',
178 :attributes => { :value => '3', :selected => 'selected' }}}}
194 :attributes => { :value => '3', :selected => 'selected' }}}}
179 assert_tag :td, :content => 'foo',
195 assert_tag :td, :content => 'foo',
180 :sibling => { :tag => 'td',
196 :sibling => { :tag => 'td',
181 :child => { :tag => 'select', :attributes => { :name => %r{^committers\[\d+\]\[\]$} }}}
197 :child => { :tag => 'select', :attributes => { :name => %r{^committers\[\d+\]\[\]$} }}}
182 assert_no_tag :td, :content => 'foo',
198 assert_no_tag :td, :content => 'foo',
183 :sibling => { :tag => 'td',
199 :sibling => { :tag => 'td',
184 :descendant => { :tag => 'option', :attributes => { :selected => 'selected' }}}
200 :descendant => { :tag => 'option', :attributes => { :selected => 'selected' }}}
185 end
201 end
186
202
187 def test_post_committers
203 def test_post_committers
188 @request.session[:user_id] = 2
204 @request.session[:user_id] = 2
189 # add a commit with an unknown user
205 # add a commit with an unknown user
190 c = Changeset.create!(
206 c = Changeset.create!(
191 :repository => Project.find(1).repository,
207 :repository => Project.find(1).repository,
192 :committer => 'foo',
208 :committer => 'foo',
193 :committed_on => Time.now,
209 :committed_on => Time.now,
194 :revision => 100,
210 :revision => 100,
195 :comments => 'Committed by foo.'
211 :comments => 'Committed by foo.'
196 )
212 )
197 assert_no_difference "Changeset.count(:conditions => 'user_id = 3')" do
213 assert_no_difference "Changeset.count(:conditions => 'user_id = 3')" do
198 post :committers, :id => 10, :committers => { '0' => ['foo', '2'], '1' => ['dlopper', '3']}
214 post :committers, :id => 10, :committers => { '0' => ['foo', '2'], '1' => ['dlopper', '3']}
199 assert_response 302
215 assert_response 302
200 assert_equal User.find(2), c.reload.user
216 assert_equal User.find(2), c.reload.user
201 end
217 end
202 end
218 end
203 end
219 end
General Comments 0
You need to be logged in to leave comments. Login now