summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean-Philippe Lang <jp_lang@yahoo.fr>2008-07-05 08:59:04 +0000
committerJean-Philippe Lang <jp_lang@yahoo.fr>2008-07-05 08:59:04 +0000
commit12fbd06c02d44fdb96922154476302f002783e23 (patch)
tree308599edd76edf56e18308a0559ddf59f731e222
parent1e6b8a482ae12d48fa72305d73891a2611db47c5 (diff)
downloadredmine-12fbd06c02d44fdb96922154476302f002783e23.tar.gz
redmine-12fbd06c02d44fdb96922154476302f002783e23.zip
Display svn properties in the browser, svn >= 1.5.0 only (#1581).
git-svn-id: http://redmine.rubyforge.org/svn/trunk@1627 e93f8b46-1217-0410-a6f0-8f06a7374b81
-rw-r--r--app/controllers/repositories_controller.rb4
-rw-r--r--app/helpers/repositories_helper.rb10
-rw-r--r--app/models/repository.rb4
-rw-r--r--app/views/repositories/browse.rhtml1
-rw-r--r--app/views/repositories/changes.rhtml4
-rw-r--r--lib/redmine/scm/adapters/abstract_adapter.rb28
-rw-r--r--lib/redmine/scm/adapters/subversion_adapter.rb43
-rw-r--r--public/stylesheets/application.css4
-rw-r--r--test/functional/repositories_subversion_controller_test.rb13
-rw-r--r--test/unit/subversion_adapter_test.rb33
10 files changed, 139 insertions, 5 deletions
diff --git a/app/controllers/repositories_controller.rb b/app/controllers/repositories_controller.rb
index 7b3387347..13bba6200 100644
--- a/app/controllers/repositories_controller.rb
+++ b/app/controllers/repositories_controller.rb
@@ -66,6 +66,7 @@ class RepositoriesController < ApplicationController
@entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
else
show_error_not_found and return unless @entries
+ @properties = @repository.properties(@path, @rev)
render :action => 'browse'
end
end
@@ -74,6 +75,7 @@ class RepositoriesController < ApplicationController
@entry = @repository.entry(@path, @rev)
show_error_not_found and return unless @entry
@changesets = @repository.changesets_for_path(@path)
+ @properties = @repository.properties(@path, @rev)
end
def revisions
@@ -106,7 +108,7 @@ class RepositoriesController < ApplicationController
else
# Prevent empty lines when displaying a file with Windows style eol
@content.gsub!("\r\n", "\n")
- end
+ end
end
def annotate
diff --git a/app/helpers/repositories_helper.rb b/app/helpers/repositories_helper.rb
index 4ef337c26..59e1e0fb9 100644
--- a/app/helpers/repositories_helper.rb
+++ b/app/helpers/repositories_helper.rb
@@ -22,6 +22,16 @@ module RepositoriesHelper
txt.to_s[0,8]
end
+ def render_properties(properties)
+ unless properties.nil? || properties.empty?
+ content = ''
+ properties.keys.sort.each do |property|
+ content << content_tag('li', "<b>#{h property}</b>: <span>#{h properties[property]}</span>")
+ end
+ content_tag('ul', content, :class => 'properties')
+ end
+ end
+
def to_path_param(path)
path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index c7bf0dbf4..2a8728f92 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -59,6 +59,10 @@ class Repository < ActiveRecord::Base
scm.entries(path, identifier)
end
+ def properties(path, identifier=nil)
+ scm.properties(path, identifier)
+ end
+
def cat(path, identifier=nil)
scm.cat(path, identifier)
end
diff --git a/app/views/repositories/browse.rhtml b/app/views/repositories/browse.rhtml
index 868388f11..4029a77d2 100644
--- a/app/views/repositories/browse.rhtml
+++ b/app/views/repositories/browse.rhtml
@@ -7,6 +7,7 @@
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => 'dir', :revision => @rev } %></h2>
<%= render :partial => 'dir_list' %>
+<%= render_properties(@properties) %>
<% content_for :header_tags do %>
<%= stylesheet_link_tag "scm" %>
diff --git a/app/views/repositories/changes.rhtml b/app/views/repositories/changes.rhtml
index 85695ec45..ca5c58328 100644
--- a/app/views/repositories/changes.rhtml
+++ b/app/views/repositories/changes.rhtml
@@ -1,7 +1,5 @@
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => (@entry ? @entry.kind : nil), :revision => @rev } %></h2>
-<h3><%=h @entry.name %></h3>
-
<p>
<% if @repository.supports_cat? %>
<%= link_to l(:button_view), {:action => 'entry', :id => @project, :path => to_path_param(@path), :rev => @rev } %> |
@@ -13,6 +11,8 @@
<%= "(#{number_to_human_size(@entry.size)})" if @entry.size %>
</p>
+<%= render_properties(@properties) %>
+
<%= render(:partial => 'revisions',
:locals => {:project => @project, :path => @path, :revisions => @changesets, :entry => @entry }) unless @changesets.empty? %>
diff --git a/lib/redmine/scm/adapters/abstract_adapter.rb b/lib/redmine/scm/adapters/abstract_adapter.rb
index 0bacda770..a876de937 100644
--- a/lib/redmine/scm/adapters/abstract_adapter.rb
+++ b/lib/redmine/scm/adapters/abstract_adapter.rb
@@ -24,6 +24,20 @@ module Redmine
end
class AbstractAdapter #:nodoc:
+ class << self
+ # Returns the version of the scm client
+ # Eg: [1, 5, 0]
+ def client_version
+ 'Unknown version'
+ end
+
+ # Returns the version string of the scm client
+ # Eg: '1.5.0'
+ def client_version_string
+ client_version.is_a?(Array) ? client_version.join('.') : client_version.to_s
+ end
+ end
+
def initialize(url, root_url=nil, login=nil, password=nil)
@url = url
@login = login if login && !login.empty?
@@ -77,6 +91,10 @@ module Redmine
def entries(path=nil, identifier=nil)
return nil
end
+
+ def properties(path, identifier=nil)
+ return nil
+ end
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
return nil
@@ -131,10 +149,18 @@ module Redmine
end
def logger
- RAILS_DEFAULT_LOGGER
+ self.class.logger
end
def shellout(cmd, &block)
+ self.class.shellout(cmd, &block)
+ end
+
+ def self.logger
+ RAILS_DEFAULT_LOGGER
+ end
+
+ def self.shellout(cmd, &block)
logger.debug "Shelling out: #{cmd}" if logger && logger.debug?
begin
IO.popen(cmd, "r+") do |io|
diff --git a/lib/redmine/scm/adapters/subversion_adapter.rb b/lib/redmine/scm/adapters/subversion_adapter.rb
index 7c98eee8b..90940715d 100644
--- a/lib/redmine/scm/adapters/subversion_adapter.rb
+++ b/lib/redmine/scm/adapters/subversion_adapter.rb
@@ -26,6 +26,25 @@ module Redmine
# SVN executable name
SVN_BIN = "svn"
+ class << self
+ def client_version
+ @@client_version ||= (svn_binary_version || 'Unknown version')
+ end
+
+ def svn_binary_version
+ cmd = "#{SVN_BIN} --version"
+ version = nil
+ shellout(cmd) do |io|
+ # Read svn version in first returned line
+ if m = io.gets.match(%r{((\d+\.)+\d+)})
+ version = m[0].scan(%r{\d+}).collect(&:to_i)
+ end
+ end
+ return nil if $? && $?.exitstatus != 0
+ version
+ end
+ end
+
# Get info about the svn repository
def info
cmd = "#{SVN_BIN} info --xml #{target('')}"
@@ -87,7 +106,29 @@ module Redmine
logger.debug("Found #{entries.size} entries in the repository for #{target(path)}") if logger && logger.debug?
entries.sort_by_name
end
-
+
+ def properties(path, identifier=nil)
+ # proplist xml output supported in svn 1.5.0 and higher
+ return nil if (self.class.client_version <=> [1, 5, 0]) < 0
+
+ identifier = (identifier and identifier.to_i > 0) ? identifier.to_i : "HEAD"
+ cmd = "#{SVN_BIN} proplist --verbose --xml #{target(path)}@#{identifier}"
+ cmd << credentials_string
+ properties = {}
+ shellout(cmd) do |io|
+ output = io.read
+ begin
+ doc = REXML::Document.new(output)
+ doc.elements.each("properties/target/property") do |property|
+ properties[ property.attributes['name'] ] = property.text
+ end
+ rescue
+ end
+ end
+ return nil if $? && $?.exitstatus != 0
+ properties
+ end
+
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
path ||= ''
identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : "HEAD"
diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css
index c10dfbef1..8f092eaee 100644
--- a/public/stylesheets/application.css
+++ b/public/stylesheets/application.css
@@ -217,6 +217,10 @@ table#time-report tbody tr.last-level { font-style: normal; color: #555; }
table#time-report tbody tr.total { font-style: normal; font-weight: bold; color: #555; background-color:#EEEEEE; }
table#time-report .hours-dec { font-size: 0.9em; }
+ul.properties {padding:0; font-size: 0.9em; color: #777;}
+ul.properties li {list-style-type:none;}
+ul.properties li span {font-style:italic;}
+
.total-hours { font-size: 110%; font-weight: bold; }
.total-hours span.hours-int { font-size: 120%; }
diff --git a/test/functional/repositories_subversion_controller_test.rb b/test/functional/repositories_subversion_controller_test.rb
index 6af5cd5dd..35dfbb1a1 100644
--- a/test/functional/repositories_subversion_controller_test.rb
+++ b/test/functional/repositories_subversion_controller_test.rb
@@ -71,6 +71,19 @@ class RepositoriesSubversionControllerTest < Test::Unit::TestCase
assert_not_nil assigns(:entries)
assert_equal ['folder', '.project', 'helloworld.c', 'helloworld.rb', 'textfile.txt'], assigns(:entries).collect(&:name)
end
+
+ def test_changes
+ get :changes, :id => 1, :path => ['subversion_test', 'folder', 'helloworld.rb' ]
+ assert_response :success
+ assert_template 'changes'
+ # svn properties
+ assert_not_nil assigns(:properties)
+ assert_equal 'native', assigns(:properties)['svn:eol-style']
+ assert_tag :ul,
+ :child => { :tag => 'li',
+ :child => { :tag => 'b', :content => 'svn:eol-style' },
+ :child => { :tag => 'span', :content => 'native' } }
+ end
def test_entry
get :entry, :id => 1, :path => ['subversion_test', 'helloworld.c']
diff --git a/test/unit/subversion_adapter_test.rb b/test/unit/subversion_adapter_test.rb
new file mode 100644
index 000000000..9f208839a
--- /dev/null
+++ b/test/unit/subversion_adapter_test.rb
@@ -0,0 +1,33 @@
+# redMine - project management software
+# Copyright (C) 2006-2008 Jean-Philippe Lang
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+require 'mkmf'
+
+require File.dirname(__FILE__) + '/../test_helper'
+
+class SubversionAdapterTest < Test::Unit::TestCase
+
+ if find_executable0('svn')
+ def test_client_version
+ v = Redmine::Scm::Adapters::SubversionAdapter.client_version
+ assert v.is_a?(Array)
+ end
+ else
+ puts "Subversion binary NOT FOUND. Skipping unit tests !!!"
+ def test_fake; assert true end
+ end
+end