Project

General

Profile

Defect #11834 » repositories_helper.rb

Alexander Usenko, 2012-09-14 17:15

 
1
# encoding: utf-8
2
#
3
# Redmine - project management software
4
# Copyright (C) 2006-2012  Jean-Philippe Lang
5
#
6
# This program is free software; you can redistribute it and/or
7
# modify it under the terms of the GNU General Public License
8
# as published by the Free Software Foundation; either version 2
9
# of the License, or (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
# GNU General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19

    
20
require 'iconv'
21
require 'redmine/codeset_util'
22

    
23
module RepositoriesHelper
24
  def format_revision(revision)
25
    if revision.respond_to? :format_identifier
26
      revision.format_identifier
27
    else
28
      revision.to_s
29
    end
30
  end
31

    
32
  def truncate_at_line_break(text, length = 255)
33
    if text
34
      text.gsub(%r{^(.{#{length}}[^\n]*)\n.+$}m, '\\1...')
35
    end
36
  end
37

    
38
  def render_properties(properties)
39
    unless properties.nil? || properties.empty?
40
      content = ''
41
      properties.keys.sort.each do |property|
42
        content << content_tag('li', "<b>#{h property}</b>: <span>#{h properties[property]}</span>".html_safe)
43
      end
44
      content_tag('ul', content.html_safe, :class => 'properties')
45
    end
46
  end
47

    
48
  def render_changeset_changes
49
    changes = @changeset.filechanges.find(:all, :limit => 1000, :order => 'path').collect do |change|
50
      case change.action
51
      when 'A'
52
        # Detects moved/copied files
53
        if !change.from_path.blank?
54
          change.action =
55
             @changeset.filechanges.detect {|c| c.action == 'D' && c.path == change.from_path} ? 'R' : 'C'
56
        end
57
        change
58
      when 'D'
59
        @changeset.filechanges.detect {|c| c.from_path == change.path} ? nil : change
60
      else
61
        change
62
      end
63
    end.compact
64

    
65
    tree = { }
66
    changes.each do |change|
67
      p = tree
68
      dirs = change.path.to_s.split('/').select {|d| !d.blank?}
69
      path = ''
70
      dirs.each do |dir|
71
        path += '/' + dir
72
        p[:s] ||= {}
73
        p = p[:s]
74
        p[path] ||= {}
75
        p = p[path]
76
      end
77
      p[:c] = change
78
    end
79
    render_changes_tree(tree[:s])
80
  end
81

    
82
  def render_changes_tree(tree)
83
    return '' if tree.nil?
84
    output = ''
85
    output << '<ul>'
86
    tree.keys.sort.each do |file|
87
      style = 'change'
88
      text = File.basename(h(file))
89
      if s = tree[file][:s]
90
        style << ' folder'
91
        path_param = to_path_param(@repository.relative_path(file))
92
        text = link_to(h(text), :controller => 'repositories',
93
                             :action => 'show',
94
                             :id => @project,
95
                             :repository_id => @repository.identifier_param,
96
                             :path => path_param,
97
                             :rev => @changeset.identifier)
98
        output << "<li class='#{style}'>#{text}"
99
        output << render_changes_tree(s)
100
        output << "</li>"
101
      elsif c = tree[file][:c]
102
        style << " change-#{c.action}"
103
        path_param = to_path_param(@repository.relative_path(c.path))
104
        text = link_to(h(text), :controller => 'repositories',
105
                             :action => 'entry',
106
                             :id => @project,
107
                             :repository_id => @repository.identifier_param,
108
                             :path => path_param,
109
                             :rev => @changeset.identifier) unless c.action == 'D'
110
        text << " - #{h(c.revision)}" unless c.revision.blank?
111
        text << ' ('.html_safe + link_to(l(:label_diff), :controller => 'repositories',
112
                                       :action => 'diff',
113
                                       :id => @project,
114
                                       :repository_id => @repository.identifier_param,
115
                                       :path => path_param,
116
                                       :rev => @changeset.identifier) + ') '.html_safe if c.action == 'M'
117
        text << ' '.html_safe + content_tag('span', h(c.from_path), :class => 'copied-from') unless c.from_path.blank?
118
        output << "<li class='#{style}'>#{text}</li>"
119
      end
120
    end
121
    output << '</ul>'
122
    output.html_safe
123
  end
124

    
125
  def repository_field_tags(form, repository)
126
    method = repository.class.name.demodulize.underscore + "_field_tags"
127
    if repository.is_a?(Repository) &&
128
        respond_to?(method) && method != 'repository_field_tags'
129
      send(method, form, repository)
130
    end
131
  end
132

    
133
  def scm_select_tag(repository)
134
    scm_options = [["--- #{l(:actionview_instancetag_blank_option)} ---", '']]
135
    Redmine::Scm::Base.all.each do |scm|
136
    if Setting.enabled_scm.include?(scm) ||
137
          (repository && repository.class.name.demodulize == scm)
138
        scm_options << ["Repository::#{scm}".constantize.scm_name, scm]
139
      end
140
    end
141
    select_tag('repository_scm',
142
               options_for_select(scm_options, repository.class.name.demodulize),
143
               :disabled => (repository && !repository.new_record?),
144
               :onchange => remote_function(
145
                 :url => new_project_repository_path(@project),
146
                 :method => :get,
147
                 :update => 'content',
148
                 :with   => "Form.serialize(this.form)")
149
             )
150
  end
151

    
152
  def with_leading_slash(path)
153
    path.to_s.starts_with?('/') ? path : "/#{path}"
154
  end
155

    
156
  def without_leading_slash(path)
157
    path.gsub(%r{^/+}, '')
158
  end
159

    
160
  def subversion_field_tags(form, repository)
161
      content_tag('p', form.text_field(:url, :size => 60, :required => true,
162
                       :disabled => (repository && !repository.root_url.blank?)) +
163
                       '<br />'.html_safe +
164
                       '(file:///, http://, https://, svn://, svn+[tunnelscheme]://)') +
165
      content_tag('p', form.text_field(:login, :size => 30)) +
166
      content_tag('p', form.password_field(
167
                            :password, :size => 30, :name => 'ignore',
168
                            :value => ((repository.new_record? || repository.password.blank?) ? '' : ('x'*15)),
169
                            :onfocus => "this.value=''; this.name='repository[password]';",
170
                            :onchange => "this.name='repository[password]';"))
171
  end
172

    
173
  def darcs_field_tags(form, repository)
174
    content_tag('p', form.text_field(
175
                     :url, :label => l(:field_path_to_repository),
176
                     :size => 60, :required => true,
177
                     :disabled => (repository && !repository.new_record?))) +
178
    content_tag('p', form.select(
179
                     :log_encoding, [nil] + Setting::ENCODINGS,
180
                     :label => l(:field_commit_logs_encoding), :required => true))
181
  end
182

    
183
  def mercurial_field_tags(form, repository)
184
    content_tag('p', form.text_field(
185
                       :url, :label => l(:field_path_to_repository),
186
                       :size => 60, :required => true,
187
                       :disabled => (repository && !repository.root_url.blank?)
188
                         ) +
189
                     '<br />'.html_safe + l(:text_mercurial_repository_note)) +
190
    content_tag('p', form.select(
191
                        :path_encoding, [nil] + Setting::ENCODINGS,
192
                        :label => l(:field_scm_path_encoding)
193
                        ) +
194
                     '<br />'.html_safe + l(:text_scm_path_encoding_note))
195
  end
196

    
197
  def git_field_tags(form, repository)
198
    content_tag('p', form.text_field(
199
                       :url, :label => l(:field_path_to_repository),
200
                       :size => 60, :required => true,
201
                       :disabled => (repository && !repository.root_url.blank?)
202
                         ) +
203
                      '<br />'.html_safe +
204
                      l(:text_git_repository_note)) +
205
    content_tag('p', form.select(
206
                        :path_encoding, [nil] + Setting::ENCODINGS,
207
                        :label => l(:field_scm_path_encoding)
208
                        ) +
209
                     '<br />'.html_safe + l(:text_scm_path_encoding_note)) +
210
    content_tag('p', form.check_box(
211
                        :extra_report_last_commit,
212
                        :label => l(:label_git_report_last_commit)
213
                         ))
214
  end
215

    
216
  def cvs_field_tags(form, repository)
217
    content_tag('p', form.text_field(
218
                     :root_url,
219
                     :label => l(:field_cvsroot),
220
                     :size => 60, :required => true,
221
                     :disabled => !repository.new_record?)) +
222
    content_tag('p', form.text_field(
223
                     :url,
224
                     :label => l(:field_cvs_module),
225
                     :size => 30, :required => true,
226
                     :disabled => !repository.new_record?)) +
227
    content_tag('p', form.select(
228
                     :log_encoding, [nil] + Setting::ENCODINGS,
229
                     :label => l(:field_commit_logs_encoding), :required => true)) +
230
    content_tag('p', form.select(
231
                        :path_encoding, [nil] + Setting::ENCODINGS,
232
                        :label => l(:field_scm_path_encoding)
233
                        ) +
234
                     '<br />'.html_safe + l(:text_scm_path_encoding_note))
235
  end
236

    
237
  def bazaar_field_tags(form, repository)
238
    content_tag('p', form.text_field(
239
                     :url, :label => l(:field_path_to_repository),
240
                     :size => 60, :required => true,
241
                     :disabled => (repository && !repository.new_record?))) +
242
#--- Begin: removed by Alexander Usenko ---
243
# Bazaar uses UTF-8 as internal encoding and shell encoding as external encoding (see next add of shell encoding)
244
# So, use convert messages from shell encoding to UTF-8 when extracting them from shell output
245
#    content_tag('p', form.select(
246
#                     :log_encoding, [nil] + Setting::ENCODINGS,
247
#                     :label => l(:field_commit_logs_encoding), :required => true)) +
248
#--- End: removed by Alexander Usenko ---
249
#--- Begin: added by Alexander Usenko ---
250
# Need to know shell encoding for russian paths
251
    content_tag('p', form.select(
252
                        :path_encoding, [nil] + Setting::ENCODINGS,
253
                        :label => l(:field_scm_path_encoding)
254
                        ) +
255
                     '<br />'.html_safe + l(:text_scm_path_encoding_note))
256
#--- End: added by Alexander Usenko ---
257
  end
258

    
259
  def filesystem_field_tags(form, repository)
260
    content_tag('p', form.text_field(
261
                     :url, :label => l(:field_root_directory),
262
                     :size => 60, :required => true,
263
                     :disabled => (repository && !repository.root_url.blank?))) +
264
    content_tag('p', form.select(
265
                        :path_encoding, [nil] + Setting::ENCODINGS,
266
                        :label => l(:field_scm_path_encoding)
267
                        ) +
268
                     '<br />'.html_safe + l(:text_scm_path_encoding_note))
269
  end
270

    
271
  def index_commits(commits, heads)
272
    return nil if commits.nil? or commits.first.parents.nil?
273

    
274
    refs_map = {}
275
    heads.each do |head|
276
      refs_map[head.scmid] ||= []
277
      refs_map[head.scmid] << head
278
    end
279

    
280
    commits_by_scmid = {}
281
    commits.reverse.each_with_index do |commit, commit_index|
282

    
283
      commits_by_scmid[commit.scmid] = {
284
        :parent_scmids => commit.parents.collect { |parent| parent.scmid },
285
        :rdmid => commit_index,
286
        :refs  => refs_map.include?(commit.scmid) ? refs_map[commit.scmid].join(" ") : nil,
287
        :scmid => commit.scmid,
288
        :href  => block_given? ? yield(commit.scmid) : commit.scmid
289
      }
290
    end
291

    
292
    heads.sort! { |head1, head2| head1.to_s <=> head2.to_s }
293

    
294
    space = nil  
295
    heads.each do |head|
296
      if commits_by_scmid.include? head.scmid
297
        space = index_head((space || -1) + 1, head, commits_by_scmid)
298
      end
299
    end
300

    
301
    # when no head matched anything use first commit
302
    space ||= index_head(0, commits.first, commits_by_scmid)
303

    
304
    return commits_by_scmid, space
305
  end
306

    
307
  def index_head(space, commit, commits_by_scmid)
308

    
309
    stack = [[space, commits_by_scmid[commit.scmid]]]
310
    max_space = space
311

    
312
    until stack.empty?
313
      space, commit = stack.pop
314
      commit[:space] = space if commit[:space].nil?
315

    
316
      space -= 1
317
      commit[:parent_scmids].each_with_index do |parent_scmid, parent_index|
318

    
319
        parent_commit = commits_by_scmid[parent_scmid]
320

    
321
        if parent_commit and parent_commit[:space].nil?
322

    
323
          stack.unshift [space += 1, parent_commit]
324
        end
325
      end
326
      max_space = space if max_space < space
327
    end
328
    max_space
329
  end
330
end
(3-3/3)