##// END OF EJS Templates
Pretty URL for the repository browser (Cyril Mougel)...
Jean-Philippe Lang -
r867:d46e3a501e7b
parent child
Show More
@@ -1,257 +1,257
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 RepositoriesController < ApplicationController
22 class RepositoriesController < ApplicationController
23 layout 'base'
23 layout 'base'
24 before_filter :find_repository, :except => :edit
24 before_filter :find_repository, :except => :edit
25 before_filter :find_project, :only => :edit
25 before_filter :find_project, :only => :edit
26 before_filter :authorize
26 before_filter :authorize
27 accept_key_auth :revisions
27 accept_key_auth :revisions
28
28
29 def edit
29 def edit
30 @repository = @project.repository
30 @repository = @project.repository
31 if !@repository
31 if !@repository
32 @repository = Repository.factory(params[:repository_scm])
32 @repository = Repository.factory(params[:repository_scm])
33 @repository.project = @project
33 @repository.project = @project
34 end
34 end
35 if request.post?
35 if request.post?
36 @repository.attributes = params[:repository]
36 @repository.attributes = params[:repository]
37 @repository.save
37 @repository.save
38 end
38 end
39 render(:update) {|page| page.replace_html "tab-content-repository", :partial => 'projects/settings/repository'}
39 render(:update) {|page| page.replace_html "tab-content-repository", :partial => 'projects/settings/repository'}
40 end
40 end
41
41
42 def destroy
42 def destroy
43 @repository.destroy
43 @repository.destroy
44 redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
44 redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
45 end
45 end
46
46
47 def show
47 def show
48 # check if new revisions have been committed in the repository
48 # check if new revisions have been committed in the repository
49 @repository.fetch_changesets if Setting.autofetch_changesets?
49 @repository.fetch_changesets if Setting.autofetch_changesets?
50 # get entries for the browse frame
50 # get entries for the browse frame
51 @entries = @repository.entries('')
51 @entries = @repository.entries('')
52 # latest changesets
52 # latest changesets
53 @changesets = @repository.changesets.find(:all, :limit => 10, :order => "committed_on DESC")
53 @changesets = @repository.changesets.find(:all, :limit => 10, :order => "committed_on DESC")
54 show_error and return unless @entries || @changesets.any?
54 show_error and return unless @entries || @changesets.any?
55 end
55 end
56
56
57 def browse
57 def browse
58 @entries = @repository.entries(@path, @rev)
58 @entries = @repository.entries(@path, @rev)
59 if request.xhr?
59 if request.xhr?
60 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
60 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
61 else
61 else
62 show_error unless @entries
62 show_error unless @entries
63 end
63 end
64 end
64 end
65
65
66 def changes
66 def changes
67 @entry = @repository.scm.entry(@path, @rev)
67 @entry = @repository.scm.entry(@path, @rev)
68 show_error and return unless @entry
68 show_error and return unless @entry
69 @changesets = @repository.changesets_for_path(@path)
69 @changesets = @repository.changesets_for_path(@path)
70 end
70 end
71
71
72 def revisions
72 def revisions
73 @changeset_count = @repository.changesets.count
73 @changeset_count = @repository.changesets.count
74 @changeset_pages = Paginator.new self, @changeset_count,
74 @changeset_pages = Paginator.new self, @changeset_count,
75 25,
75 25,
76 params['page']
76 params['page']
77 @changesets = @repository.changesets.find(:all,
77 @changesets = @repository.changesets.find(:all,
78 :limit => @changeset_pages.items_per_page,
78 :limit => @changeset_pages.items_per_page,
79 :offset => @changeset_pages.current.offset)
79 :offset => @changeset_pages.current.offset)
80
80
81 respond_to do |format|
81 respond_to do |format|
82 format.html { render :layout => false if request.xhr? }
82 format.html { render :layout => false if request.xhr? }
83 format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
83 format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
84 end
84 end
85 end
85 end
86
86
87 def entry
87 def entry
88 @content = @repository.scm.cat(@path, @rev)
88 @content = @repository.scm.cat(@path, @rev)
89 show_error and return unless @content
89 show_error and return unless @content
90 if 'raw' == params[:format]
90 if 'raw' == params[:format]
91 send_data @content, :filename => @path.split('/').last
91 send_data @content, :filename => @path.split('/').last
92 end
92 end
93 end
93 end
94
94
95 def revision
95 def revision
96 @changeset = @repository.changesets.find_by_revision(@rev)
96 @changeset = @repository.changesets.find_by_revision(@rev)
97 show_error and return unless @changeset
97 show_error and return unless @changeset
98 @changes_count = @changeset.changes.size
98 @changes_count = @changeset.changes.size
99 @changes_pages = Paginator.new self, @changes_count, 150, params['page']
99 @changes_pages = Paginator.new self, @changes_count, 150, params['page']
100 @changes = @changeset.changes.find(:all,
100 @changes = @changeset.changes.find(:all,
101 :limit => @changes_pages.items_per_page,
101 :limit => @changes_pages.items_per_page,
102 :offset => @changes_pages.current.offset)
102 :offset => @changes_pages.current.offset)
103
103
104 render :action => "revision", :layout => false if request.xhr?
104 render :action => "revision", :layout => false if request.xhr?
105 end
105 end
106
106
107 def diff
107 def diff
108 @rev_to = params[:rev_to] ? params[:rev_to].to_i : (@rev - 1)
108 @rev_to = params[:rev_to] ? params[:rev_to].to_i : (@rev - 1)
109 @diff_type = ('sbs' == params[:type]) ? 'sbs' : 'inline'
109 @diff_type = ('sbs' == params[:type]) ? 'sbs' : 'inline'
110
110
111 @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}")
111 @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}")
112 unless read_fragment(@cache_key)
112 unless read_fragment(@cache_key)
113 @diff = @repository.diff(@path, @rev, @rev_to, type)
113 @diff = @repository.diff(@path, @rev, @rev_to, @diff_type)
114 show_error and return unless @diff
114 show_error and return unless @diff
115 end
115 end
116 end
116 end
117
117
118 def stats
118 def stats
119 end
119 end
120
120
121 def graph
121 def graph
122 data = nil
122 data = nil
123 case params[:graph]
123 case params[:graph]
124 when "commits_per_month"
124 when "commits_per_month"
125 data = graph_commits_per_month(@repository)
125 data = graph_commits_per_month(@repository)
126 when "commits_per_author"
126 when "commits_per_author"
127 data = graph_commits_per_author(@repository)
127 data = graph_commits_per_author(@repository)
128 end
128 end
129 if data
129 if data
130 headers["Content-Type"] = "image/svg+xml"
130 headers["Content-Type"] = "image/svg+xml"
131 send_data(data, :type => "image/svg+xml", :disposition => "inline")
131 send_data(data, :type => "image/svg+xml", :disposition => "inline")
132 else
132 else
133 render_404
133 render_404
134 end
134 end
135 end
135 end
136
136
137 private
137 private
138 def find_project
138 def find_project
139 @project = Project.find(params[:id])
139 @project = Project.find(params[:id])
140 rescue ActiveRecord::RecordNotFound
140 rescue ActiveRecord::RecordNotFound
141 render_404
141 render_404
142 end
142 end
143
143
144 def find_repository
144 def find_repository
145 @project = Project.find(params[:id])
145 @project = Project.find(params[:id])
146 @repository = @project.repository
146 @repository = @project.repository
147 render_404 and return false unless @repository
147 render_404 and return false unless @repository
148 @path = params[:path].squeeze('/') if params[:path]
148 @path = params[:path].join('/') unless params[:path].nil?
149 @path ||= ''
149 @path ||= ''
150 @rev = params[:rev].to_i if params[:rev]
150 @rev = params[:rev].to_i if params[:rev]
151 rescue ActiveRecord::RecordNotFound
151 rescue ActiveRecord::RecordNotFound
152 render_404
152 render_404
153 end
153 end
154
154
155 def show_error
155 def show_error
156 flash.now[:error] = l(:notice_scm_error)
156 flash.now[:error] = l(:notice_scm_error)
157 render :nothing => true, :layout => true
157 render :nothing => true, :layout => true
158 end
158 end
159
159
160 def graph_commits_per_month(repository)
160 def graph_commits_per_month(repository)
161 @date_to = Date.today
161 @date_to = Date.today
162 @date_from = @date_to << 11
162 @date_from = @date_to << 11
163 @date_from = Date.civil(@date_from.year, @date_from.month, 1)
163 @date_from = Date.civil(@date_from.year, @date_from.month, 1)
164 commits_by_day = repository.changesets.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
164 commits_by_day = repository.changesets.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
165 commits_by_month = [0] * 12
165 commits_by_month = [0] * 12
166 commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
166 commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
167
167
168 changes_by_day = repository.changes.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
168 changes_by_day = repository.changes.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
169 changes_by_month = [0] * 12
169 changes_by_month = [0] * 12
170 changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
170 changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
171
171
172 fields = []
172 fields = []
173 month_names = l(:actionview_datehelper_select_month_names_abbr).split(',')
173 month_names = l(:actionview_datehelper_select_month_names_abbr).split(',')
174 12.times {|m| fields << month_names[((Date.today.month - 1 - m) % 12)]}
174 12.times {|m| fields << month_names[((Date.today.month - 1 - m) % 12)]}
175
175
176 graph = SVG::Graph::Bar.new(
176 graph = SVG::Graph::Bar.new(
177 :height => 300,
177 :height => 300,
178 :width => 500,
178 :width => 500,
179 :fields => fields.reverse,
179 :fields => fields.reverse,
180 :stack => :side,
180 :stack => :side,
181 :scale_integers => true,
181 :scale_integers => true,
182 :step_x_labels => 2,
182 :step_x_labels => 2,
183 :show_data_values => false,
183 :show_data_values => false,
184 :graph_title => l(:label_commits_per_month),
184 :graph_title => l(:label_commits_per_month),
185 :show_graph_title => true
185 :show_graph_title => true
186 )
186 )
187
187
188 graph.add_data(
188 graph.add_data(
189 :data => commits_by_month[0..11].reverse,
189 :data => commits_by_month[0..11].reverse,
190 :title => l(:label_revision_plural)
190 :title => l(:label_revision_plural)
191 )
191 )
192
192
193 graph.add_data(
193 graph.add_data(
194 :data => changes_by_month[0..11].reverse,
194 :data => changes_by_month[0..11].reverse,
195 :title => l(:label_change_plural)
195 :title => l(:label_change_plural)
196 )
196 )
197
197
198 graph.burn
198 graph.burn
199 end
199 end
200
200
201 def graph_commits_per_author(repository)
201 def graph_commits_per_author(repository)
202 commits_by_author = repository.changesets.count(:all, :group => :committer)
202 commits_by_author = repository.changesets.count(:all, :group => :committer)
203 commits_by_author.sort! {|x, y| x.last <=> y.last}
203 commits_by_author.sort! {|x, y| x.last <=> y.last}
204
204
205 changes_by_author = repository.changes.count(:all, :group => :committer)
205 changes_by_author = repository.changes.count(:all, :group => :committer)
206 h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
206 h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
207
207
208 fields = commits_by_author.collect {|r| r.first}
208 fields = commits_by_author.collect {|r| r.first}
209 commits_data = commits_by_author.collect {|r| r.last}
209 commits_data = commits_by_author.collect {|r| r.last}
210 changes_data = commits_by_author.collect {|r| h[r.first] || 0}
210 changes_data = commits_by_author.collect {|r| h[r.first] || 0}
211
211
212 fields = fields + [""]*(10 - fields.length) if fields.length<10
212 fields = fields + [""]*(10 - fields.length) if fields.length<10
213 commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
213 commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
214 changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
214 changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
215
215
216 graph = SVG::Graph::BarHorizontal.new(
216 graph = SVG::Graph::BarHorizontal.new(
217 :height => 300,
217 :height => 300,
218 :width => 500,
218 :width => 500,
219 :fields => fields,
219 :fields => fields,
220 :stack => :side,
220 :stack => :side,
221 :scale_integers => true,
221 :scale_integers => true,
222 :show_data_values => false,
222 :show_data_values => false,
223 :rotate_y_labels => false,
223 :rotate_y_labels => false,
224 :graph_title => l(:label_commits_per_author),
224 :graph_title => l(:label_commits_per_author),
225 :show_graph_title => true
225 :show_graph_title => true
226 )
226 )
227
227
228 graph.add_data(
228 graph.add_data(
229 :data => commits_data,
229 :data => commits_data,
230 :title => l(:label_revision_plural)
230 :title => l(:label_revision_plural)
231 )
231 )
232
232
233 graph.add_data(
233 graph.add_data(
234 :data => changes_data,
234 :data => changes_data,
235 :title => l(:label_change_plural)
235 :title => l(:label_change_plural)
236 )
236 )
237
237
238 graph.burn
238 graph.burn
239 end
239 end
240
240
241 end
241 end
242
242
243 class Date
243 class Date
244 def months_ago(date = Date.today)
244 def months_ago(date = Date.today)
245 (date.year - self.year)*12 + (date.month - self.month)
245 (date.year - self.year)*12 + (date.month - self.month)
246 end
246 end
247
247
248 def weeks_ago(date = Date.today)
248 def weeks_ago(date = Date.today)
249 (date.year - self.year)*52 + (date.cweek - self.cweek)
249 (date.year - self.year)*52 + (date.cweek - self.cweek)
250 end
250 end
251 end
251 end
252
252
253 class String
253 class String
254 def with_leading_slash
254 def with_leading_slash
255 starts_with?('/') ? self : "/#{self}"
255 starts_with?('/') ? self : "/#{self}"
256 end
256 end
257 end
257 end
@@ -1,29 +1,28
1 <% form_tag({:controller => 'repositories', :action => 'diff', :id => @project}, :method => :get) do %>
1 <% form_tag({:controller => 'repositories', :action => 'diff', :id => @project, :path => path}, :method => :get) do %>
2 <table class="list">
2 <table class="list">
3 <thead><tr>
3 <thead><tr>
4 <th>#</th>
4 <th>#</th>
5 <th></th>
5 <th></th>
6 <th></th>
6 <th></th>
7 <th><%= l(:label_date) %></th>
7 <th><%= l(:label_date) %></th>
8 <th><%= l(:field_author) %></th>
8 <th><%= l(:field_author) %></th>
9 <th><%= l(:field_comments) %></th>
9 <th><%= l(:field_comments) %></th>
10 </tr></thead>
10 </tr></thead>
11 <tbody>
11 <tbody>
12 <% show_diff = entry && entry.is_file? && revisions.size > 1 %>
12 <% show_diff = entry && entry.is_file? && revisions.size > 1 %>
13 <% line_num = 1 %>
13 <% line_num = 1 %>
14 <% revisions.each do |changeset| %>
14 <% revisions.each do |changeset| %>
15 <tr class="<%= cycle 'odd', 'even' %>">
15 <tr class="<%= cycle 'odd', 'even' %>">
16 <td class="id"><%= link_to changeset.revision, :action => 'revision', :id => project, :rev => changeset.revision %></th>
16 <td class="id"><%= link_to changeset.revision, :action => 'revision', :id => project, :rev => changeset.revision %></th>
17 <td class="checkbox"><%= radio_button_tag('rev', changeset.revision, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < revisions.size) %></td>
17 <td class="checkbox"><%= radio_button_tag('rev', changeset.revision, (line_num==1), :id => "cb-#{line_num}", :onclick => "$('cbto-#{line_num+1}').checked=true;") if show_diff && (line_num < revisions.size) %></td>
18 <td class="checkbox"><%= radio_button_tag('rev_to', changeset.revision, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('cb-#{line_num}').checked==true) {$('cb-#{line_num-1}').checked=true;}") if show_diff && (line_num > 1) %></td>
18 <td class="checkbox"><%= radio_button_tag('rev_to', changeset.revision, (line_num==2), :id => "cbto-#{line_num}", :onclick => "if ($('cb-#{line_num}').checked==true) {$('cb-#{line_num-1}').checked=true;}") if show_diff && (line_num > 1) %></td>
19 <td align="center" style="width:15%"><%= format_time(changeset.committed_on) %></td>
19 <td align="center" style="width:15%"><%= format_time(changeset.committed_on) %></td>
20 <td align="center" style="width:15%"><em><%=h changeset.committer %></em></td>
20 <td align="center" style="width:15%"><em><%=h changeset.committer %></em></td>
21 <td align="left"><%= textilizable(changeset.comments) %></td>
21 <td align="left"><%= textilizable(changeset.comments) %></td>
22 </tr>
22 </tr>
23 <% line_num += 1 %>
23 <% line_num += 1 %>
24 <% end %>
24 <% end %>
25 </tbody>
25 </tbody>
26 </table>
26 </table>
27 <%= hidden_field_tag 'path', path %>
28 <%= submit_tag(l(:label_view_diff), :class => 'small', :name => nil) if show_diff %>
27 <%= submit_tag(l(:label_view_diff), :class => 'small', :name => nil) if show_diff %>
29 <% end %>
28 <% end %>
@@ -1,27 +1,34
1 ActionController::Routing::Routes.draw do |map|
1 ActionController::Routing::Routes.draw do |map|
2 # Add your own custom routes here.
2 # Add your own custom routes here.
3 # The priority is based upon order of creation: first created -> highest priority.
3 # The priority is based upon order of creation: first created -> highest priority.
4
4
5 # Here's a sample route:
5 # Here's a sample route:
6 # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
6 # map.connect 'products/:id', :controller => 'catalog', :action => 'view'
7 # Keep in mind you can assign values other than :controller and :action
7 # Keep in mind you can assign values other than :controller and :action
8
8
9 map.home '', :controller => 'welcome'
9 map.home '', :controller => 'welcome'
10
10
11 map.connect 'wiki/:id/:page/:action', :controller => 'wiki', :page => nil
11 map.connect 'wiki/:id/:page/:action', :controller => 'wiki', :page => nil
12 map.connect 'roles/workflow/:id/:role_id/:tracker_id', :controller => 'roles', :action => 'workflow'
12 map.connect 'roles/workflow/:id/:role_id/:tracker_id', :controller => 'roles', :action => 'workflow'
13 map.connect 'help/:ctrl/:page', :controller => 'help'
13 map.connect 'help/:ctrl/:page', :controller => 'help'
14 #map.connect ':controller/:action/:id/:sort_key/:sort_order'
14 #map.connect ':controller/:action/:id/:sort_key/:sort_order'
15
15
16 map.connect 'issues/:issue_id/relations/:action/:id', :controller => 'issue_relations'
16 map.connect 'issues/:issue_id/relations/:action/:id', :controller => 'issue_relations'
17 map.connect 'projects/:project_id/boards/:action/:id', :controller => 'boards'
17 map.connect 'projects/:project_id/boards/:action/:id', :controller => 'boards'
18 map.connect 'boards/:board_id/topics/:action/:id', :controller => 'messages'
18 map.connect 'boards/:board_id/topics/:action/:id', :controller => 'messages'
19
20 map.with_options :controller => 'repositories' do |omap|
21 omap.repositories_show 'repositories/browse/:id/*path', :action => 'browse'
22 omap.repositories_changes 'repositories/changes/:id/*path', :action => 'changes'
23 omap.repositories_diff 'repositories/diff/:id/*path', :action => 'diff'
24 omap.repositories_entry 'repositories/entry/:id/*path', :action => 'entry'
25 end
19
26
20 # Allow downloading Web Service WSDL as a file with an extension
27 # Allow downloading Web Service WSDL as a file with an extension
21 # instead of a file named 'wsdl'
28 # instead of a file named 'wsdl'
22 map.connect ':controller/service.wsdl', :action => 'wsdl'
29 map.connect ':controller/service.wsdl', :action => 'wsdl'
23
30
24
31
25 # Install the default route as the lowest priority.
32 # Install the default route as the lowest priority.
26 map.connect ':controller/:action/:id'
33 map.connect ':controller/:action/:id'
27 end
34 end
General Comments 0
You need to be logged in to leave comments. Login now