##// END OF EJS Templates
RepositoriesController cleanup with rescue_from....
Jean-Philippe Lang -
r1541:0223b87612fd
parent child
Show More
@@ -1,324 +1,313
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 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 layout 'base'
26 layout 'base'
27 menu_item :repository
27 menu_item :repository
28 before_filter :find_repository, :except => :edit
28 before_filter :find_repository, :except => :edit
29 before_filter :find_project, :only => :edit
29 before_filter :find_project, :only => :edit
30 before_filter :authorize
30 before_filter :authorize
31 accept_key_auth :revisions
31 accept_key_auth :revisions
32
32
33 rescue_from Redmine::Scm::Adapters::CommandFailed, :with => :show_error_command_failed
34
33 def edit
35 def edit
34 @repository = @project.repository
36 @repository = @project.repository
35 if !@repository
37 if !@repository
36 @repository = Repository.factory(params[:repository_scm])
38 @repository = Repository.factory(params[:repository_scm])
37 @repository.project = @project if @repository
39 @repository.project = @project if @repository
38 end
40 end
39 if request.post? && @repository
41 if request.post? && @repository
40 @repository.attributes = params[:repository]
42 @repository.attributes = params[:repository]
41 @repository.save
43 @repository.save
42 end
44 end
43 render(:update) {|page| page.replace_html "tab-content-repository", :partial => 'projects/settings/repository'}
45 render(:update) {|page| page.replace_html "tab-content-repository", :partial => 'projects/settings/repository'}
44 end
46 end
45
47
46 def destroy
48 def destroy
47 @repository.destroy
49 @repository.destroy
48 redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
50 redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
49 end
51 end
50
52
51 def show
53 def show
52 # check if new revisions have been committed in the repository
54 # check if new revisions have been committed in the repository
53 @repository.fetch_changesets if Setting.autofetch_changesets?
55 @repository.fetch_changesets if Setting.autofetch_changesets?
54 # root entries
56 # root entries
55 @entries = @repository.entries('', @rev)
57 @entries = @repository.entries('', @rev)
56 # latest changesets
58 # latest changesets
57 @changesets = @repository.changesets.find(:all, :limit => 10, :order => "committed_on DESC")
59 @changesets = @repository.changesets.find(:all, :limit => 10, :order => "committed_on DESC")
58 show_error_not_found unless @entries || @changesets.any?
60 show_error_not_found unless @entries || @changesets.any?
59 rescue Redmine::Scm::Adapters::CommandFailed => e
60 show_error_command_failed(e.message)
61 end
61 end
62
62
63 def browse
63 def browse
64 @entries = @repository.entries(@path, @rev)
64 @entries = @repository.entries(@path, @rev)
65 if request.xhr?
65 if request.xhr?
66 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
66 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
67 else
67 else
68 show_error_not_found and return unless @entries
68 show_error_not_found and return unless @entries
69 render :action => 'browse'
69 render :action => 'browse'
70 end
70 end
71 rescue Redmine::Scm::Adapters::CommandFailed => e
72 show_error_command_failed(e.message)
73 end
71 end
74
72
75 def changes
73 def changes
76 @entry = @repository.entry(@path, @rev)
74 @entry = @repository.entry(@path, @rev)
77 show_error_not_found and return unless @entry
75 show_error_not_found and return unless @entry
78 @changesets = @repository.changesets_for_path(@path)
76 @changesets = @repository.changesets_for_path(@path)
79 rescue Redmine::Scm::Adapters::CommandFailed => e
80 show_error_command_failed(e.message)
81 end
77 end
82
78
83 def revisions
79 def revisions
84 @changeset_count = @repository.changesets.count
80 @changeset_count = @repository.changesets.count
85 @changeset_pages = Paginator.new self, @changeset_count,
81 @changeset_pages = Paginator.new self, @changeset_count,
86 per_page_option,
82 per_page_option,
87 params['page']
83 params['page']
88 @changesets = @repository.changesets.find(:all,
84 @changesets = @repository.changesets.find(:all,
89 :limit => @changeset_pages.items_per_page,
85 :limit => @changeset_pages.items_per_page,
90 :offset => @changeset_pages.current.offset)
86 :offset => @changeset_pages.current.offset)
91
87
92 respond_to do |format|
88 respond_to do |format|
93 format.html { render :layout => false if request.xhr? }
89 format.html { render :layout => false if request.xhr? }
94 format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
90 format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
95 end
91 end
96 end
92 end
97
93
98 def entry
94 def entry
99 @entry = @repository.entry(@path, @rev)
95 @entry = @repository.entry(@path, @rev)
100 show_error_not_found and return unless @entry
96 show_error_not_found and return unless @entry
101
97
102 # If the entry is a dir, show the browser
98 # If the entry is a dir, show the browser
103 browse and return if @entry.is_dir?
99 browse and return if @entry.is_dir?
104
100
105 @content = @repository.cat(@path, @rev)
101 @content = @repository.cat(@path, @rev)
106 show_error_not_found and return unless @content
102 show_error_not_found and return unless @content
107 if 'raw' == params[:format] || @content.is_binary_data?
103 if 'raw' == params[:format] || @content.is_binary_data?
108 # Force the download if it's a binary file
104 # Force the download if it's a binary file
109 send_data @content, :filename => @path.split('/').last
105 send_data @content, :filename => @path.split('/').last
110 else
106 else
111 # Prevent empty lines when displaying a file with Windows style eol
107 # Prevent empty lines when displaying a file with Windows style eol
112 @content.gsub!("\r\n", "\n")
108 @content.gsub!("\r\n", "\n")
113 end
109 end
114 rescue Redmine::Scm::Adapters::CommandFailed => e
115 show_error_command_failed(e.message)
116 end
110 end
117
111
118 def annotate
112 def annotate
119 @annotate = @repository.scm.annotate(@path, @rev)
113 @annotate = @repository.scm.annotate(@path, @rev)
120 render_error l(:error_scm_annotate) and return if @annotate.nil? || @annotate.empty?
114 render_error l(:error_scm_annotate) and return if @annotate.nil? || @annotate.empty?
121 rescue Redmine::Scm::Adapters::CommandFailed => e
122 show_error_command_failed(e.message)
123 end
115 end
124
116
125 def revision
117 def revision
126 @changeset = @repository.changesets.find_by_revision(@rev)
118 @changeset = @repository.changesets.find_by_revision(@rev)
127 raise ChangesetNotFound unless @changeset
119 raise ChangesetNotFound unless @changeset
128 @changes_count = @changeset.changes.size
120 @changes_count = @changeset.changes.size
129 @changes_pages = Paginator.new self, @changes_count, 150, params['page']
121 @changes_pages = Paginator.new self, @changes_count, 150, params['page']
130 @changes = @changeset.changes.find(:all,
122 @changes = @changeset.changes.find(:all,
131 :limit => @changes_pages.items_per_page,
123 :limit => @changes_pages.items_per_page,
132 :offset => @changes_pages.current.offset)
124 :offset => @changes_pages.current.offset)
133
125
134 respond_to do |format|
126 respond_to do |format|
135 format.html
127 format.html
136 format.js {render :layout => false}
128 format.js {render :layout => false}
137 end
129 end
138 rescue ChangesetNotFound
130 rescue ChangesetNotFound
139 show_error_not_found
131 show_error_not_found
140 rescue Redmine::Scm::Adapters::CommandFailed => e
141 show_error_command_failed(e.message)
142 end
132 end
143
133
144 def diff
134 def diff
145 if params[:format] == 'diff'
135 if params[:format] == 'diff'
146 @diff = @repository.diff(@path, @rev, @rev_to)
136 @diff = @repository.diff(@path, @rev, @rev_to)
147 show_error_not_found and return unless @diff
137 show_error_not_found and return unless @diff
148 filename = "changeset_r#{@rev}"
138 filename = "changeset_r#{@rev}"
149 filename << "_r#{@rev_to}" if @rev_to
139 filename << "_r#{@rev_to}" if @rev_to
150 send_data @diff.join, :filename => "#{filename}.diff",
140 send_data @diff.join, :filename => "#{filename}.diff",
151 :type => 'text/x-patch',
141 :type => 'text/x-patch',
152 :disposition => 'attachment'
142 :disposition => 'attachment'
153 else
143 else
154 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
144 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
155 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
145 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
156
146
157 # Save diff type as user preference
147 # Save diff type as user preference
158 if User.current.logged? && @diff_type != User.current.pref[:diff_type]
148 if User.current.logged? && @diff_type != User.current.pref[:diff_type]
159 User.current.pref[:diff_type] = @diff_type
149 User.current.pref[:diff_type] = @diff_type
160 User.current.preference.save
150 User.current.preference.save
161 end
151 end
162
152
163 @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}")
153 @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}")
164 unless read_fragment(@cache_key)
154 unless read_fragment(@cache_key)
165 @diff = @repository.diff(@path, @rev, @rev_to)
155 @diff = @repository.diff(@path, @rev, @rev_to)
166 show_error_not_found unless @diff
156 show_error_not_found unless @diff
167 end
157 end
168 end
158 end
169 rescue Redmine::Scm::Adapters::CommandFailed => e
170 show_error_command_failed(e.message)
171 end
159 end
172
160
173 def stats
161 def stats
174 end
162 end
175
163
176 def graph
164 def graph
177 data = nil
165 data = nil
178 case params[:graph]
166 case params[:graph]
179 when "commits_per_month"
167 when "commits_per_month"
180 data = graph_commits_per_month(@repository)
168 data = graph_commits_per_month(@repository)
181 when "commits_per_author"
169 when "commits_per_author"
182 data = graph_commits_per_author(@repository)
170 data = graph_commits_per_author(@repository)
183 end
171 end
184 if data
172 if data
185 headers["Content-Type"] = "image/svg+xml"
173 headers["Content-Type"] = "image/svg+xml"
186 send_data(data, :type => "image/svg+xml", :disposition => "inline")
174 send_data(data, :type => "image/svg+xml", :disposition => "inline")
187 else
175 else
188 render_404
176 render_404
189 end
177 end
190 end
178 end
191
179
192 private
180 private
193 def find_project
181 def find_project
194 @project = Project.find(params[:id])
182 @project = Project.find(params[:id])
195 rescue ActiveRecord::RecordNotFound
183 rescue ActiveRecord::RecordNotFound
196 render_404
184 render_404
197 end
185 end
198
186
199 REV_PARAM_RE = %r{^[a-f0-9]*$}
187 REV_PARAM_RE = %r{^[a-f0-9]*$}
200
188
201 def find_repository
189 def find_repository
202 @project = Project.find(params[:id])
190 @project = Project.find(params[:id])
203 @repository = @project.repository
191 @repository = @project.repository
204 render_404 and return false unless @repository
192 render_404 and return false unless @repository
205 @path = params[:path].join('/') unless params[:path].nil?
193 @path = params[:path].join('/') unless params[:path].nil?
206 @path ||= ''
194 @path ||= ''
207 @rev = params[:rev]
195 @rev = params[:rev]
208 @rev_to = params[:rev_to]
196 @rev_to = params[:rev_to]
209 raise InvalidRevisionParam unless @rev.to_s.match(REV_PARAM_RE) && @rev.to_s.match(REV_PARAM_RE)
197 raise InvalidRevisionParam unless @rev.to_s.match(REV_PARAM_RE) && @rev.to_s.match(REV_PARAM_RE)
210 rescue ActiveRecord::RecordNotFound
198 rescue ActiveRecord::RecordNotFound
211 render_404
199 render_404
212 rescue InvalidRevisionParam
200 rescue InvalidRevisionParam
213 show_error_not_found
201 show_error_not_found
214 end
202 end
215
203
216 def show_error_not_found
204 def show_error_not_found
217 render_error l(:error_scm_not_found)
205 render_error l(:error_scm_not_found)
218 end
206 end
219
207
220 def show_error_command_failed(msg)
208 # Handler for Redmine::Scm::Adapters::CommandFailed exception
221 render_error l(:error_scm_command_failed, msg)
209 def show_error_command_failed(exception)
210 render_error l(:error_scm_command_failed, exception.message)
222 end
211 end
223
212
224 def graph_commits_per_month(repository)
213 def graph_commits_per_month(repository)
225 @date_to = Date.today
214 @date_to = Date.today
226 @date_from = @date_to << 11
215 @date_from = @date_to << 11
227 @date_from = Date.civil(@date_from.year, @date_from.month, 1)
216 @date_from = Date.civil(@date_from.year, @date_from.month, 1)
228 commits_by_day = repository.changesets.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
217 commits_by_day = repository.changesets.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
229 commits_by_month = [0] * 12
218 commits_by_month = [0] * 12
230 commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
219 commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
231
220
232 changes_by_day = repository.changes.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
221 changes_by_day = repository.changes.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
233 changes_by_month = [0] * 12
222 changes_by_month = [0] * 12
234 changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
223 changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
235
224
236 fields = []
225 fields = []
237 month_names = l(:actionview_datehelper_select_month_names_abbr).split(',')
226 month_names = l(:actionview_datehelper_select_month_names_abbr).split(',')
238 12.times {|m| fields << month_names[((Date.today.month - 1 - m) % 12)]}
227 12.times {|m| fields << month_names[((Date.today.month - 1 - m) % 12)]}
239
228
240 graph = SVG::Graph::Bar.new(
229 graph = SVG::Graph::Bar.new(
241 :height => 300,
230 :height => 300,
242 :width => 500,
231 :width => 500,
243 :fields => fields.reverse,
232 :fields => fields.reverse,
244 :stack => :side,
233 :stack => :side,
245 :scale_integers => true,
234 :scale_integers => true,
246 :step_x_labels => 2,
235 :step_x_labels => 2,
247 :show_data_values => false,
236 :show_data_values => false,
248 :graph_title => l(:label_commits_per_month),
237 :graph_title => l(:label_commits_per_month),
249 :show_graph_title => true
238 :show_graph_title => true
250 )
239 )
251
240
252 graph.add_data(
241 graph.add_data(
253 :data => commits_by_month[0..11].reverse,
242 :data => commits_by_month[0..11].reverse,
254 :title => l(:label_revision_plural)
243 :title => l(:label_revision_plural)
255 )
244 )
256
245
257 graph.add_data(
246 graph.add_data(
258 :data => changes_by_month[0..11].reverse,
247 :data => changes_by_month[0..11].reverse,
259 :title => l(:label_change_plural)
248 :title => l(:label_change_plural)
260 )
249 )
261
250
262 graph.burn
251 graph.burn
263 end
252 end
264
253
265 def graph_commits_per_author(repository)
254 def graph_commits_per_author(repository)
266 commits_by_author = repository.changesets.count(:all, :group => :committer)
255 commits_by_author = repository.changesets.count(:all, :group => :committer)
267 commits_by_author.sort! {|x, y| x.last <=> y.last}
256 commits_by_author.sort! {|x, y| x.last <=> y.last}
268
257
269 changes_by_author = repository.changes.count(:all, :group => :committer)
258 changes_by_author = repository.changes.count(:all, :group => :committer)
270 h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
259 h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
271
260
272 fields = commits_by_author.collect {|r| r.first}
261 fields = commits_by_author.collect {|r| r.first}
273 commits_data = commits_by_author.collect {|r| r.last}
262 commits_data = commits_by_author.collect {|r| r.last}
274 changes_data = commits_by_author.collect {|r| h[r.first] || 0}
263 changes_data = commits_by_author.collect {|r| h[r.first] || 0}
275
264
276 fields = fields + [""]*(10 - fields.length) if fields.length<10
265 fields = fields + [""]*(10 - fields.length) if fields.length<10
277 commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
266 commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
278 changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
267 changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
279
268
280 # Remove email adress in usernames
269 # Remove email adress in usernames
281 fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
270 fields = fields.collect {|c| c.gsub(%r{<.+@.+>}, '') }
282
271
283 graph = SVG::Graph::BarHorizontal.new(
272 graph = SVG::Graph::BarHorizontal.new(
284 :height => 300,
273 :height => 300,
285 :width => 500,
274 :width => 500,
286 :fields => fields,
275 :fields => fields,
287 :stack => :side,
276 :stack => :side,
288 :scale_integers => true,
277 :scale_integers => true,
289 :show_data_values => false,
278 :show_data_values => false,
290 :rotate_y_labels => false,
279 :rotate_y_labels => false,
291 :graph_title => l(:label_commits_per_author),
280 :graph_title => l(:label_commits_per_author),
292 :show_graph_title => true
281 :show_graph_title => true
293 )
282 )
294
283
295 graph.add_data(
284 graph.add_data(
296 :data => commits_data,
285 :data => commits_data,
297 :title => l(:label_revision_plural)
286 :title => l(:label_revision_plural)
298 )
287 )
299
288
300 graph.add_data(
289 graph.add_data(
301 :data => changes_data,
290 :data => changes_data,
302 :title => l(:label_change_plural)
291 :title => l(:label_change_plural)
303 )
292 )
304
293
305 graph.burn
294 graph.burn
306 end
295 end
307
296
308 end
297 end
309
298
310 class Date
299 class Date
311 def months_ago(date = Date.today)
300 def months_ago(date = Date.today)
312 (date.year - self.year)*12 + (date.month - self.month)
301 (date.year - self.year)*12 + (date.month - self.month)
313 end
302 end
314
303
315 def weeks_ago(date = Date.today)
304 def weeks_ago(date = Date.today)
316 (date.year - self.year)*52 + (date.cweek - self.cweek)
305 (date.year - self.year)*52 + (date.cweek - self.cweek)
317 end
306 end
318 end
307 end
319
308
320 class String
309 class String
321 def with_leading_slash
310 def with_leading_slash
322 starts_with?('/') ? self : "/#{self}"
311 starts_with?('/') ? self : "/#{self}"
323 end
312 end
324 end
313 end
General Comments 0
You need to be logged in to leave comments. Login now