##// END OF EJS Templates
Diff style (inline or side by side) automatically saved as a user preference....
Jean-Philippe Lang -
r880:1454a711caf6
parent child
Show More
@@ -1,257 +1,264
1 1 # redMine - project management software
2 2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require 'SVG/Graph/Bar'
19 19 require 'SVG/Graph/BarHorizontal'
20 20 require 'digest/sha1'
21 21
22 22 class RepositoriesController < ApplicationController
23 23 layout 'base'
24 24 before_filter :find_repository, :except => :edit
25 25 before_filter :find_project, :only => :edit
26 26 before_filter :authorize
27 27 accept_key_auth :revisions
28 28
29 29 def edit
30 30 @repository = @project.repository
31 31 if !@repository
32 32 @repository = Repository.factory(params[:repository_scm])
33 33 @repository.project = @project
34 34 end
35 35 if request.post?
36 36 @repository.attributes = params[:repository]
37 37 @repository.save
38 38 end
39 39 render(:update) {|page| page.replace_html "tab-content-repository", :partial => 'projects/settings/repository'}
40 40 end
41 41
42 42 def destroy
43 43 @repository.destroy
44 44 redirect_to :controller => 'projects', :action => 'settings', :id => @project, :tab => 'repository'
45 45 end
46 46
47 47 def show
48 48 # check if new revisions have been committed in the repository
49 49 @repository.fetch_changesets if Setting.autofetch_changesets?
50 50 # get entries for the browse frame
51 51 @entries = @repository.entries('')
52 52 # latest changesets
53 53 @changesets = @repository.changesets.find(:all, :limit => 10, :order => "committed_on DESC")
54 54 show_error and return unless @entries || @changesets.any?
55 55 end
56 56
57 57 def browse
58 58 @entries = @repository.entries(@path, @rev)
59 59 if request.xhr?
60 60 @entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
61 61 else
62 62 show_error unless @entries
63 63 end
64 64 end
65 65
66 66 def changes
67 67 @entry = @repository.scm.entry(@path, @rev)
68 68 show_error and return unless @entry
69 69 @changesets = @repository.changesets_for_path(@path)
70 70 end
71 71
72 72 def revisions
73 73 @changeset_count = @repository.changesets.count
74 74 @changeset_pages = Paginator.new self, @changeset_count,
75 75 25,
76 76 params['page']
77 77 @changesets = @repository.changesets.find(:all,
78 78 :limit => @changeset_pages.items_per_page,
79 79 :offset => @changeset_pages.current.offset)
80 80
81 81 respond_to do |format|
82 82 format.html { render :layout => false if request.xhr? }
83 83 format.atom { render_feed(@changesets, :title => "#{@project.name}: #{l(:label_revision_plural)}") }
84 84 end
85 85 end
86 86
87 87 def entry
88 88 @content = @repository.scm.cat(@path, @rev)
89 89 show_error and return unless @content
90 90 if 'raw' == params[:format]
91 91 send_data @content, :filename => @path.split('/').last
92 92 end
93 93 end
94 94
95 95 def revision
96 96 @changeset = @repository.changesets.find_by_revision(@rev)
97 97 show_error and return unless @changeset
98 98 @changes_count = @changeset.changes.size
99 99 @changes_pages = Paginator.new self, @changes_count, 150, params['page']
100 100 @changes = @changeset.changes.find(:all,
101 101 :limit => @changes_pages.items_per_page,
102 102 :offset => @changes_pages.current.offset)
103 103
104 104 render :action => "revision", :layout => false if request.xhr?
105 105 end
106 106
107 107 def diff
108 108 @rev_to = params[:rev_to] ? params[:rev_to].to_i : (@rev - 1)
109 @diff_type = ('sbs' == params[:type]) ? 'sbs' : 'inline'
109 @diff_type = params[:type] || User.current.pref[:diff_type] || 'inline'
110 @diff_type = 'inline' unless %w(inline sbs).include?(@diff_type)
111
112 # Save diff type as user preference
113 if User.current.logged? && @diff_type != User.current.pref[:diff_type]
114 User.current.pref[:diff_type] = @diff_type
115 User.current.preference.save
116 end
110 117
111 118 @cache_key = "repositories/diff/#{@repository.id}/" + Digest::MD5.hexdigest("#{@path}-#{@rev}-#{@rev_to}-#{@diff_type}")
112 119 unless read_fragment(@cache_key)
113 120 @diff = @repository.diff(@path, @rev, @rev_to, @diff_type)
114 121 show_error and return unless @diff
115 122 end
116 123 end
117 124
118 125 def stats
119 126 end
120 127
121 128 def graph
122 129 data = nil
123 130 case params[:graph]
124 131 when "commits_per_month"
125 132 data = graph_commits_per_month(@repository)
126 133 when "commits_per_author"
127 134 data = graph_commits_per_author(@repository)
128 135 end
129 136 if data
130 137 headers["Content-Type"] = "image/svg+xml"
131 138 send_data(data, :type => "image/svg+xml", :disposition => "inline")
132 139 else
133 140 render_404
134 141 end
135 142 end
136 143
137 144 private
138 145 def find_project
139 146 @project = Project.find(params[:id])
140 147 rescue ActiveRecord::RecordNotFound
141 148 render_404
142 149 end
143 150
144 151 def find_repository
145 152 @project = Project.find(params[:id])
146 153 @repository = @project.repository
147 154 render_404 and return false unless @repository
148 155 @path = params[:path].join('/') unless params[:path].nil?
149 156 @path ||= ''
150 157 @rev = params[:rev].to_i if params[:rev]
151 158 rescue ActiveRecord::RecordNotFound
152 159 render_404
153 160 end
154 161
155 162 def show_error
156 163 flash.now[:error] = l(:notice_scm_error)
157 164 render :nothing => true, :layout => true
158 165 end
159 166
160 167 def graph_commits_per_month(repository)
161 168 @date_to = Date.today
162 169 @date_from = @date_to << 11
163 170 @date_from = Date.civil(@date_from.year, @date_from.month, 1)
164 171 commits_by_day = repository.changesets.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
165 172 commits_by_month = [0] * 12
166 173 commits_by_day.each {|c| commits_by_month[c.first.to_date.months_ago] += c.last }
167 174
168 175 changes_by_day = repository.changes.count(:all, :group => :commit_date, :conditions => ["commit_date BETWEEN ? AND ?", @date_from, @date_to])
169 176 changes_by_month = [0] * 12
170 177 changes_by_day.each {|c| changes_by_month[c.first.to_date.months_ago] += c.last }
171 178
172 179 fields = []
173 180 month_names = l(:actionview_datehelper_select_month_names_abbr).split(',')
174 181 12.times {|m| fields << month_names[((Date.today.month - 1 - m) % 12)]}
175 182
176 183 graph = SVG::Graph::Bar.new(
177 184 :height => 300,
178 185 :width => 500,
179 186 :fields => fields.reverse,
180 187 :stack => :side,
181 188 :scale_integers => true,
182 189 :step_x_labels => 2,
183 190 :show_data_values => false,
184 191 :graph_title => l(:label_commits_per_month),
185 192 :show_graph_title => true
186 193 )
187 194
188 195 graph.add_data(
189 196 :data => commits_by_month[0..11].reverse,
190 197 :title => l(:label_revision_plural)
191 198 )
192 199
193 200 graph.add_data(
194 201 :data => changes_by_month[0..11].reverse,
195 202 :title => l(:label_change_plural)
196 203 )
197 204
198 205 graph.burn
199 206 end
200 207
201 208 def graph_commits_per_author(repository)
202 209 commits_by_author = repository.changesets.count(:all, :group => :committer)
203 210 commits_by_author.sort! {|x, y| x.last <=> y.last}
204 211
205 212 changes_by_author = repository.changes.count(:all, :group => :committer)
206 213 h = changes_by_author.inject({}) {|o, i| o[i.first] = i.last; o}
207 214
208 215 fields = commits_by_author.collect {|r| r.first}
209 216 commits_data = commits_by_author.collect {|r| r.last}
210 217 changes_data = commits_by_author.collect {|r| h[r.first] || 0}
211 218
212 219 fields = fields + [""]*(10 - fields.length) if fields.length<10
213 220 commits_data = commits_data + [0]*(10 - commits_data.length) if commits_data.length<10
214 221 changes_data = changes_data + [0]*(10 - changes_data.length) if changes_data.length<10
215 222
216 223 graph = SVG::Graph::BarHorizontal.new(
217 224 :height => 300,
218 225 :width => 500,
219 226 :fields => fields,
220 227 :stack => :side,
221 228 :scale_integers => true,
222 229 :show_data_values => false,
223 230 :rotate_y_labels => false,
224 231 :graph_title => l(:label_commits_per_author),
225 232 :show_graph_title => true
226 233 )
227 234
228 235 graph.add_data(
229 236 :data => commits_data,
230 237 :title => l(:label_revision_plural)
231 238 )
232 239
233 240 graph.add_data(
234 241 :data => changes_data,
235 242 :title => l(:label_change_plural)
236 243 )
237 244
238 245 graph.burn
239 246 end
240 247
241 248 end
242 249
243 250 class Date
244 251 def months_ago(date = Date.today)
245 252 (date.year - self.year)*12 + (date.month - self.month)
246 253 end
247 254
248 255 def weeks_ago(date = Date.today)
249 256 (date.year - self.year)*52 + (date.cweek - self.cweek)
250 257 end
251 258 end
252 259
253 260 class String
254 261 def with_leading_slash
255 262 starts_with?('/') ? self : "/#{self}"
256 263 end
257 264 end
@@ -1,93 +1,93
1 1 <h2><%= l(:label_revision) %> <%= @rev %>: <%= @path.gsub(/^.*\//, '') %></h2>
2 2
3 3 <!-- Choose view type -->
4 4 <% form_tag({ :controller => 'repositories', :action => 'diff'}, :method => 'get') do %>
5 5 <% params.each do |k, p| %>
6 6 <% if k != "type" %>
7 7 <%= hidden_field_tag(k,p) %>
8 8 <% end %>
9 9 <% end %>
10 10 <p><label><%= l(:label_view_diff) %></label>
11 <%= select_tag 'type', options_for_select([[l(:label_diff_inline), "inline"], [l(:label_diff_side_by_side), "sbs"]], params[:type]), :onchange => "if (this.value != '') {this.form.submit()}" %></p>
11 <%= select_tag 'type', options_for_select([[l(:label_diff_inline), "inline"], [l(:label_diff_side_by_side), "sbs"]], @diff_type), :onchange => "if (this.value != '') {this.form.submit()}" %></p>
12 12 <% end %>
13 13
14 14 <div class="autoscroll">
15 15 <% cache(@cache_key) do %>
16 16 <% @diff.each do |table_file| %>
17 17 <% if @diff_type == 'sbs' %>
18 18 <table class="filecontent CodeRay">
19 19 <thead>
20 20 <tr>
21 21 <th colspan="4" class="filename">
22 22 <%= table_file.file_name %>
23 23 </th>
24 24 </tr>
25 25 <tr>
26 26 <th colspan="2">@<%= @rev %></th>
27 27 <th colspan="2">@<%= @rev_to %></th>
28 28 </tr>
29 29 </thead>
30 30 <tbody>
31 31 <% table_file.keys.sort.each do |key| %>
32 32 <tr>
33 33 <th class="line-num">
34 34 <%= table_file[key].nb_line_left %>
35 35 </th>
36 36 <td class="line-code <%= table_file[key].type_diff_left %>">
37 37 <pre><%=to_utf8 table_file[key].line_left %></pre>
38 38 </td>
39 39 <th class="line-num">
40 40 <%= table_file[key].nb_line_right %>
41 41 </th>
42 42 <td class="line-code <%= table_file[key].type_diff_right %>">
43 43 <pre><%=to_utf8 table_file[key].line_right %></pre>
44 44 </td>
45 45 </tr>
46 46 <% end %>
47 47 </tbody>
48 48 </table>
49 49
50 50 <% else %>
51 51 <table class="filecontent CodeRay">
52 52 <thead>
53 53 <tr>
54 54 <th colspan="3" class="filename">
55 55 <%= table_file.file_name %>
56 56 </th>
57 57 </tr>
58 58 <tr>
59 59 <th>@<%= @rev %></th>
60 60 <th>@<%= @rev_to %></th>
61 61 <th></th>
62 62 </tr>
63 63 </thead>
64 64 <tbody>
65 65 <% table_file.keys.sort.each do |key, line| %>
66 66 <tr>
67 67 <th class="line-num">
68 68 <%= table_file[key].nb_line_left %>
69 69 </th>
70 70 <th class="line-num">
71 71 <%= table_file[key].nb_line_right %>
72 72 </th>
73 73 <% if table_file[key].line_left.empty? %>
74 74 <td class="line-code <%= table_file[key].type_diff_right %>">
75 75 <pre><%=to_utf8 table_file[key].line_right %></pre>
76 76 </td>
77 77 <% else %>
78 78 <td class="line-code <%= table_file[key].type_diff_left %>">
79 79 <pre><%=to_utf8 table_file[key].line_left %></pre>
80 80 </td>
81 81 <% end %>
82 82 </tr>
83 83 <% end %>
84 84 </tbody>
85 85 </table>
86 86 <% end %>
87 87 <% end %>
88 88 <% end %>
89 89 </div>
90 90
91 91 <% content_for :header_tags do %>
92 92 <%= stylesheet_link_tag "scm" %>
93 93 <% end %>
@@ -1,132 +1,132
1 1 # redMine - project management software
2 2 # Copyright (C) 2006 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.dirname(__FILE__) + '/../test_helper'
19 19
20 20 class UserTest < Test::Unit::TestCase
21 21 fixtures :users, :members, :projects
22 22
23 23 def setup
24 24 @admin = User.find(1)
25 25 @jsmith = User.find(2)
26 26 @dlopper = User.find(3)
27 27 end
28 28
29 29 def test_truth
30 30 assert_kind_of User, @jsmith
31 31 end
32 32
33 33 def test_create
34 34 user = User.new(:firstname => "new", :lastname => "user", :mail => "newuser@somenet.foo")
35 35
36 36 user.login = "jsmith"
37 37 user.password, user.password_confirmation = "password", "password"
38 38 # login uniqueness
39 39 assert !user.save
40 40 assert_equal 1, user.errors.count
41 41
42 42 user.login = "newuser"
43 43 user.password, user.password_confirmation = "passwd", "password"
44 44 # password confirmation
45 45 assert !user.save
46 46 assert_equal 1, user.errors.count
47 47
48 48 user.password, user.password_confirmation = "password", "password"
49 49 assert user.save
50 50 end
51 51
52 52 def test_update
53 53 assert_equal "admin", @admin.login
54 54 @admin.login = "john"
55 55 assert @admin.save, @admin.errors.full_messages.join("; ")
56 56 @admin.reload
57 57 assert_equal "john", @admin.login
58 58 end
59 59
60 60 def test_validate
61 61 @admin.login = ""
62 62 assert !@admin.save
63 63 assert_equal 2, @admin.errors.count
64 64 end
65 65
66 66 def test_password
67 67 user = User.try_to_login("admin", "admin")
68 68 assert_kind_of User, user
69 69 assert_equal "admin", user.login
70 70 user.password = "hello"
71 71 assert user.save
72 72
73 73 user = User.try_to_login("admin", "hello")
74 74 assert_kind_of User, user
75 75 assert_equal "admin", user.login
76 76 assert_equal User.hash_password("hello"), user.hashed_password
77 77 end
78 78
79 79 def test_lock
80 80 user = User.try_to_login("jsmith", "jsmith")
81 81 assert_equal @jsmith, user
82 82
83 83 @jsmith.status = User::STATUS_LOCKED
84 84 assert @jsmith.save
85 85
86 86 user = User.try_to_login("jsmith", "jsmith")
87 87 assert_equal nil, user
88 88 end
89 89
90 90 def test_rss_key
91 91 assert_nil @jsmith.rss_token
92 92 key = @jsmith.rss_key
93 93 assert_equal 40, key.length
94 94
95 95 @jsmith.reload
96 96 assert_equal key, @jsmith.rss_key
97 97 end
98 98
99 99 def test_role_for_project
100 100 # user with a role
101 101 role = @jsmith.role_for_project(Project.find(1))
102 102 assert_kind_of Role, role
103 103 assert_equal "Manager", role.name
104 104
105 105 # user with no role
106 106 assert !@dlopper.role_for_project(Project.find(2)).member?
107 107 end
108 108
109 109 def test_mail_notification_all
110 110 @jsmith.mail_notification = true
111 111 @jsmith.notified_project_ids = []
112 112 @jsmith.save
113 113 @jsmith.reload
114 114 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
115 115 end
116 116
117 117 def test_mail_notification_selected
118 118 @jsmith.mail_notification = false
119 @jsmith.notified_project_ids = [@jsmith.projects.first.id]
119 @jsmith.notified_project_ids = [1]
120 120 @jsmith.save
121 121 @jsmith.reload
122 assert @jsmith.projects.first.recipients.include?(@jsmith.mail)
122 assert Project.find(1).recipients.include?(@jsmith.mail)
123 123 end
124 124
125 125 def test_mail_notification_none
126 126 @jsmith.mail_notification = false
127 127 @jsmith.notified_project_ids = []
128 128 @jsmith.save
129 129 @jsmith.reload
130 130 assert !@jsmith.projects.first.recipients.include?(@jsmith.mail)
131 131 end
132 132 end
General Comments 0
You need to be logged in to leave comments. Login now