##// END OF EJS Templates
added pagination on revisions list...
Jean-Philippe Lang -
r378:e951209d6824
parent child
Show More
@@ -1,188 +1,197
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
20
21 class RepositoriesController < ApplicationController
21 class RepositoriesController < ApplicationController
22 layout 'base'
22 layout 'base'
23 before_filter :find_project
23 before_filter :find_project
24 before_filter :authorize, :except => [:stats, :graph]
24 before_filter :authorize, :except => [:stats, :graph]
25 before_filter :check_project_privacy, :only => [:stats, :graph]
25 before_filter :check_project_privacy, :only => [:stats, :graph]
26
26
27 def show
27 def show
28 # get entries for the browse frame
28 # get entries for the browse frame
29 @entries = @repository.scm.entries('')
29 @entries = @repository.scm.entries('')
30 show_error and return unless @entries
30 show_error and return unless @entries
31 # check if new revisions have been committed in the repository
31 # check if new revisions have been committed in the repository
32 scm_latestrev = @entries.revisions.latest
32 scm_latestrev = @entries.revisions.latest
33 if Setting.autofetch_changesets? && scm_latestrev && ((@repository.latest_changeset.nil?) || (@repository.latest_changeset.revision < scm_latestrev.identifier.to_i))
33 if Setting.autofetch_changesets? && scm_latestrev && ((@repository.latest_changeset.nil?) || (@repository.latest_changeset.revision < scm_latestrev.identifier.to_i))
34 @repository.fetch_changesets
34 @repository.fetch_changesets
35 @repository.reload
35 @repository.reload
36 end
36 end
37 @changesets = @repository.changesets.find(:all, :limit => 5, :order => "committed_on DESC")
37 @changesets = @repository.changesets.find(:all, :limit => 5, :order => "committed_on DESC")
38 end
38 end
39
39
40 def browse
40 def browse
41 @entries = @repository.scm.entries(@path, @rev)
41 @entries = @repository.scm.entries(@path, @rev)
42 show_error and return unless @entries
42 show_error and return unless @entries
43 end
43 end
44
44
45 def revisions
45 def revisions
46 unless @path == ''
46 unless @path == ''
47 @entry = @repository.scm.entry(@path, @rev)
47 @entry = @repository.scm.entry(@path, @rev)
48 show_error and return unless @entry
48 show_error and return unless @entry
49 end
49 end
50 @changesets = @repository.changesets_for_path(@path)
50 @repository.changesets_with_path @path do
51 @changeset_count = @repository.changesets.count
52 @changeset_pages = Paginator.new self, @changeset_count,
53 25,
54 params['page']
55 @changesets = @repository.changesets.find(:all,
56 :limit => @changeset_pages.items_per_page,
57 :offset => @changeset_pages.current.offset)
58 end
59 render :action => "revisions", :layout => false if request.xhr?
51 end
60 end
52
61
53 def entry
62 def entry
54 if 'raw' == params[:format]
63 if 'raw' == params[:format]
55 content = @repository.scm.cat(@path, @rev)
64 content = @repository.scm.cat(@path, @rev)
56 show_error and return unless content
65 show_error and return unless content
57 send_data content, :filename => @path.split('/').last
66 send_data content, :filename => @path.split('/').last
58 end
67 end
59 end
68 end
60
69
61 def revision
70 def revision
62 @changeset = @repository.changesets.find_by_revision(@rev)
71 @changeset = @repository.changesets.find_by_revision(@rev)
63 show_error and return unless @changeset
72 show_error and return unless @changeset
64 end
73 end
65
74
66 def diff
75 def diff
67 @rev_to = params[:rev_to] || (@rev-1)
76 @rev_to = params[:rev_to] || (@rev-1)
68 @diff = @repository.scm.diff(params[:path], @rev, @rev_to)
77 @diff = @repository.scm.diff(params[:path], @rev, @rev_to)
69 show_error and return unless @diff
78 show_error and return unless @diff
70 end
79 end
71
80
72 def stats
81 def stats
73 end
82 end
74
83
75 def graph
84 def graph
76 data = nil
85 data = nil
77 case params[:graph]
86 case params[:graph]
78 when "commits_per_month"
87 when "commits_per_month"
79 data = graph_commits_per_month(@repository)
88 data = graph_commits_per_month(@repository)
80 when "commits_per_author"
89 when "commits_per_author"
81 data = graph_commits_per_author(@repository)
90 data = graph_commits_per_author(@repository)
82 end
91 end
83 if data
92 if data
84 headers["Content-Type"] = "image/svg+xml"
93 headers["Content-Type"] = "image/svg+xml"
85 send_data(data, :type => "image/svg+xml", :disposition => "inline")
94 send_data(data, :type => "image/svg+xml", :disposition => "inline")
86 else
95 else
87 render_404
96 render_404
88 end
97 end
89 end
98 end
90
99
91 private
100 private
92 def find_project
101 def find_project
93 @project = Project.find(params[:id])
102 @project = Project.find(params[:id])
94 @repository = @project.repository
103 @repository = @project.repository
95 render_404 and return false unless @repository
104 render_404 and return false unless @repository
96 @path = params[:path].squeeze('/').gsub(/^\//, '') if params[:path]
105 @path = params[:path].squeeze('/').gsub(/^\//, '') if params[:path]
97 @path ||= ''
106 @path ||= ''
98 @rev = params[:rev].to_i if params[:rev] and params[:rev].to_i > 0
107 @rev = params[:rev].to_i if params[:rev] and params[:rev].to_i > 0
99 rescue ActiveRecord::RecordNotFound
108 rescue ActiveRecord::RecordNotFound
100 render_404
109 render_404
101 end
110 end
102
111
103 def show_error
112 def show_error
104 flash.now[:notice] = l(:notice_scm_error)
113 flash.now[:notice] = l(:notice_scm_error)
105 render :nothing => true, :layout => true
114 render :nothing => true, :layout => true
106 end
115 end
107
116
108 def graph_commits_per_month(repository)
117 def graph_commits_per_month(repository)
109 @date_to = Date.today
118 @date_to = Date.today
110 @date_from = @date_to << 12
119 @date_from = @date_to << 12
111 commits_by_day = repository.changesets.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
120 commits_by_day = repository.changesets.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
112 commits_by_month = [0] * 12
121 commits_by_month = [0] * 12
113 commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
122 commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
114
123
115 changes_by_day = repository.changes.count(:all, :group => :commit_date)
124 changes_by_day = repository.changes.count(:all, :group => :commit_date)
116 changes_by_month = [0] * 12
125 changes_by_month = [0] * 12
117 changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
126 changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
118
127
119 fields = []
128 fields = []
120 month_names = l(:actionview_datehelper_select_month_names_abbr).split(',')
129 month_names = l(:actionview_datehelper_select_month_names_abbr).split(',')
121 12.times {|m| fields << month_names[((Date.today.month - 1 - m) % 12)]}
130 12.times {|m| fields << month_names[((Date.today.month - 1 - m) % 12)]}
122
131
123 graph = SVG::Graph::Bar.new(
132 graph = SVG::Graph::Bar.new(
124 :height => 300,
133 :height => 300,
125 :width => 500,
134 :width => 500,
126 :fields => fields.reverse,
135 :fields => fields.reverse,
127 :stack => :side,
136 :stack => :side,
128 :scale_integers => true,
137 :scale_integers => true,
129 :step_x_labels => 2,
138 :step_x_labels => 2,
130 :show_data_values => false,
139 :show_data_values => false,
131 :graph_title => l(:label_commits_per_month),
140 :graph_title => l(:label_commits_per_month),
132 :show_graph_title => true
141 :show_graph_title => true
133 )
142 )
134
143
135 graph.add_data(
144 graph.add_data(
136 :data => commits_by_month[0..11].reverse,
145 :data => commits_by_month[0..11].reverse,
137 :title => l(:label_revision_plural)
146 :title => l(:label_revision_plural)
138 )
147 )
139
148
140 graph.add_data(
149 graph.add_data(
141 :data => changes_by_month[0..11].reverse,
150 :data => changes_by_month[0..11].reverse,
142 :title => l(:label_change_plural)
151 :title => l(:label_change_plural)
143 )
152 )
144
153
145 graph.burn
154 graph.burn
146 end
155 end
147
156
148 def graph_commits_per_author(repository)
157 def graph_commits_per_author(repository)
149 commits_by_author = repository.changesets.count(:all, :group => :committer)
158 commits_by_author = repository.changesets.count(:all, :group => :committer)
150 commits_by_author.sort! {|x, y| x.last <=> y.last}
159 commits_by_author.sort! {|x, y| x.last <=> y.last}
151
160
152 fields = commits_by_author.collect {|r| r.first}
161 fields = commits_by_author.collect {|r| r.first}
153 data = commits_by_author.collect {|r| r.last}
162 data = commits_by_author.collect {|r| r.last}
154
163
155 fields = fields + [""]*(10 - fields.length) if fields.length<10
164 fields = fields + [""]*(10 - fields.length) if fields.length<10
156 data = data + [0]*(10 - data.length) if data.length<10
165 data = data + [0]*(10 - data.length) if data.length<10
157
166
158 graph = SVG::Graph::BarHorizontal.new(
167 graph = SVG::Graph::BarHorizontal.new(
159 :height => 300,
168 :height => 300,
160 :width => 500,
169 :width => 500,
161 :fields => fields,
170 :fields => fields,
162 :stack => :side,
171 :stack => :side,
163 :scale_integers => true,
172 :scale_integers => true,
164 :show_data_values => false,
173 :show_data_values => false,
165 :rotate_y_labels => false,
174 :rotate_y_labels => false,
166 :graph_title => l(:label_commits_per_author),
175 :graph_title => l(:label_commits_per_author),
167 :show_graph_title => true
176 :show_graph_title => true
168 )
177 )
169
178
170 graph.add_data(
179 graph.add_data(
171 :data => data,
180 :data => data,
172 :title => l(:label_revision_plural)
181 :title => l(:label_revision_plural)
173 )
182 )
174
183
175 graph.burn
184 graph.burn
176 end
185 end
177
186
178 end
187 end
179
188
180 class Date
189 class Date
181 def months_ago(date = Date.today)
190 def months_ago(date = Date.today)
182 (date.year - self.year)*12 + (date.month - self.month)
191 (date.year - self.year)*12 + (date.month - self.month)
183 end
192 end
184
193
185 def weeks_ago(date = Date.today)
194 def weeks_ago(date = Date.today)
186 (date.year - self.year)*52 + (date.cweek - self.cweek)
195 (date.year - self.year)*52 + (date.cweek - self.cweek)
187 end
196 end
188 end No newline at end of file
197 end
@@ -1,87 +1,96
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 class Repository < ActiveRecord::Base
18 class Repository < ActiveRecord::Base
19 belongs_to :project
19 belongs_to :project
20 has_many :changesets, :dependent => :destroy, :order => 'revision DESC'
20 has_many :changesets, :dependent => :destroy, :order => 'revision DESC'
21 has_many :changes, :through => :changesets
21 has_many :changes, :through => :changesets
22 has_one :latest_changeset, :class_name => 'Changeset', :foreign_key => :repository_id, :order => 'revision DESC'
22 has_one :latest_changeset, :class_name => 'Changeset', :foreign_key => :repository_id, :order => 'revision DESC'
23
23
24 attr_protected :root_url
24 attr_protected :root_url
25
25
26 validates_presence_of :url
26 validates_presence_of :url
27 validates_format_of :url, :with => /^(http|https|svn|file):\/\/.+/i
27 validates_format_of :url, :with => /^(http|https|svn|file):\/\/.+/i
28
28
29 def scm
29 def scm
30 @scm ||= SvnRepos::Base.new url, root_url, login, password
30 @scm ||= SvnRepos::Base.new url, root_url, login, password
31 update_attribute(:root_url, @scm.root_url) if root_url.blank?
31 update_attribute(:root_url, @scm.root_url) if root_url.blank?
32 @scm
32 @scm
33 end
33 end
34
34
35 def url=(str)
35 def url=(str)
36 super if root_url.blank?
36 super if root_url.blank?
37 end
37 end
38
38
39 def changesets_with_path(path="")
40 path = "/#{path}%"
41 path = url.gsub(/^#{root_url}/, '') + path if root_url && root_url != url
42 path.squeeze!("/")
43 Changeset.with_scope(:find => { :include => :changes, :conditions => ["#{Change.table_name}.path LIKE ?", path] }) do
44 yield
45 end
46 end
47
39 def changesets_for_path(path="")
48 def changesets_for_path(path="")
40 path = "/#{path}%"
49 path = "/#{path}%"
41 path = url.gsub(/^#{root_url}/, '') + path if root_url && root_url != url
50 path = url.gsub(/^#{root_url}/, '') + path if root_url && root_url != url
42 path.squeeze!("/")
51 path.squeeze!("/")
43 changesets.find(:all, :include => :changes,
52 changesets.find(:all, :include => :changes,
44 :conditions => ["#{Change.table_name}.path LIKE ?", path])
53 :conditions => ["#{Change.table_name}.path LIKE ?", path])
45 end
54 end
46
55
47 def fetch_changesets
56 def fetch_changesets
48 scm_info = scm.info
57 scm_info = scm.info
49 if scm_info
58 if scm_info
50 lastrev_identifier = scm_info.lastrev.identifier.to_i
59 lastrev_identifier = scm_info.lastrev.identifier.to_i
51 if latest_changeset.nil? || latest_changeset.revision < lastrev_identifier
60 if latest_changeset.nil? || latest_changeset.revision < lastrev_identifier
52 logger.debug "Fetching changesets for repository #{url}" if logger && logger.debug?
61 logger.debug "Fetching changesets for repository #{url}" if logger && logger.debug?
53 identifier_from = latest_changeset ? latest_changeset.revision + 1 : 1
62 identifier_from = latest_changeset ? latest_changeset.revision + 1 : 1
54 while (identifier_from <= lastrev_identifier)
63 while (identifier_from <= lastrev_identifier)
55 # loads changesets by batches of 200
64 # loads changesets by batches of 200
56 identifier_to = [identifier_from + 199, lastrev_identifier].min
65 identifier_to = [identifier_from + 199, lastrev_identifier].min
57 revisions = scm.revisions('', identifier_to, identifier_from, :with_paths => true)
66 revisions = scm.revisions('', identifier_to, identifier_from, :with_paths => true)
58 transaction do
67 transaction do
59 revisions.reverse_each do |revision|
68 revisions.reverse_each do |revision|
60 changeset = Changeset.create(:repository => self,
69 changeset = Changeset.create(:repository => self,
61 :revision => revision.identifier,
70 :revision => revision.identifier,
62 :committer => revision.author,
71 :committer => revision.author,
63 :committed_on => revision.time,
72 :committed_on => revision.time,
64 :comment => revision.message)
73 :comment => revision.message)
65
74
66 revision.paths.each do |change|
75 revision.paths.each do |change|
67 Change.create(:changeset => changeset,
76 Change.create(:changeset => changeset,
68 :action => change[:action],
77 :action => change[:action],
69 :path => change[:path],
78 :path => change[:path],
70 :from_path => change[:from_path],
79 :from_path => change[:from_path],
71 :from_revision => change[:from_revision])
80 :from_revision => change[:from_revision])
72 end
81 end
73 end
82 end
74 end
83 end
75 identifier_from = identifier_to + 1
84 identifier_from = identifier_to + 1
76 end
85 end
77 end
86 end
78 end
87 end
79 end
88 end
80
89
81 # fetch new changesets for all repositories
90 # fetch new changesets for all repositories
82 # can be called periodically by an external script
91 # can be called periodically by an external script
83 # eg. ruby script/runner "Repository.fetch_changesets"
92 # eg. ruby script/runner "Repository.fetch_changesets"
84 def self.fetch_changesets
93 def self.fetch_changesets
85 find(:all).each(&:fetch_changesets)
94 find(:all).each(&:fetch_changesets)
86 end
95 end
87 end
96 end
@@ -1,22 +1,24
1 <div class="contextual">
1 <div class="contextual">
2 <% form_tag do %>
2 <% form_tag do %>
3 <p><%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 5 %>
3 <p><%= l(:label_revision) %>: <%= text_field_tag 'rev', @rev, :size => 5 %>
4 <%= submit_tag 'OK' %></p>
4 <%= submit_tag 'OK' %></p>
5 <% end %>
5 <% end %>
6 </div>
6 </div>
7
7
8 <h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => (@entry ? @entry.kind : nil), :revision => @rev } %></h2>
8 <h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => (@entry ? @entry.kind : nil), :revision => @rev } %></h2>
9
9
10 <% if @entry && @entry.is_file? %>
10 <% if @entry && @entry.is_file? %>
11 <h3><%=h @entry.name %></h3>
11 <h3><%=h @entry.name %></h3>
12 <p><%= link_to 'Download', {:action => 'entry', :id => @project, :path => @path, :rev => @rev, :format => 'raw' }, :class => "icon file" %> (<%= number_to_human_size @entry.size %>)</p>
12 <p><%= link_to 'Download', {:action => 'entry', :id => @project, :path => @path, :rev => @rev, :format => 'raw' }, :class => "icon file" %> (<%= number_to_human_size @entry.size %>)</p>
13 <% end %>
13 <% end %>
14
14
15 <h3>Revisions</h3>
15 <h3>Revisions</h3>
16
16
17 <%= render :partial => 'revisions', :locals => {:project => @project, :path => @path, :changesets => @changesets, :entry => @entry }%>
17 <%= render :partial => 'revisions', :locals => {:project => @project, :path => @path, :changesets => @changesets, :entry => @entry }%>
18 <p><%= lwr(:label_modification, @changesets.length) %></p>
18
19 <p><%= pagination_links_full @changeset_pages %>
20 [ <%= @changeset_pages.current.first_item %> - <%= @changeset_pages.current.last_item %> / <%= @changeset_count %> ]</p>
19
21
20 <% content_for :header_tags do %>
22 <% content_for :header_tags do %>
21 <%= stylesheet_link_tag "scm" %>
23 <%= stylesheet_link_tag "scm" %>
22 <% end %> No newline at end of file
24 <% end %>
General Comments 0
You need to be logged in to leave comments. Login now