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
This commit is contained in:
parent
1e6b8a482a
commit
12fbd06c02
@ -66,6 +66,7 @@ class RepositoriesController < ApplicationController
|
|||||||
@entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
|
@entries ? render(:partial => 'dir_list_content') : render(:nothing => true)
|
||||||
else
|
else
|
||||||
show_error_not_found and return unless @entries
|
show_error_not_found and return unless @entries
|
||||||
|
@properties = @repository.properties(@path, @rev)
|
||||||
render :action => 'browse'
|
render :action => 'browse'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -74,6 +75,7 @@ class RepositoriesController < ApplicationController
|
|||||||
@entry = @repository.entry(@path, @rev)
|
@entry = @repository.entry(@path, @rev)
|
||||||
show_error_not_found and return unless @entry
|
show_error_not_found and return unless @entry
|
||||||
@changesets = @repository.changesets_for_path(@path)
|
@changesets = @repository.changesets_for_path(@path)
|
||||||
|
@properties = @repository.properties(@path, @rev)
|
||||||
end
|
end
|
||||||
|
|
||||||
def revisions
|
def revisions
|
||||||
@ -106,7 +108,7 @@ class RepositoriesController < ApplicationController
|
|||||||
else
|
else
|
||||||
# Prevent empty lines when displaying a file with Windows style eol
|
# Prevent empty lines when displaying a file with Windows style eol
|
||||||
@content.gsub!("\r\n", "\n")
|
@content.gsub!("\r\n", "\n")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def annotate
|
def annotate
|
||||||
|
@ -22,6 +22,16 @@ module RepositoriesHelper
|
|||||||
txt.to_s[0,8]
|
txt.to_s[0,8]
|
||||||
end
|
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)
|
def to_path_param(path)
|
||||||
path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
|
path.to_s.split(%r{[/\\]}).select {|p| !p.blank?}
|
||||||
end
|
end
|
||||||
|
@ -59,6 +59,10 @@ class Repository < ActiveRecord::Base
|
|||||||
scm.entries(path, identifier)
|
scm.entries(path, identifier)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def properties(path, identifier=nil)
|
||||||
|
scm.properties(path, identifier)
|
||||||
|
end
|
||||||
|
|
||||||
def cat(path, identifier=nil)
|
def cat(path, identifier=nil)
|
||||||
scm.cat(path, identifier)
|
scm.cat(path, identifier)
|
||||||
end
|
end
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => 'dir', :revision => @rev } %></h2>
|
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => 'dir', :revision => @rev } %></h2>
|
||||||
|
|
||||||
<%= render :partial => 'dir_list' %>
|
<%= render :partial => 'dir_list' %>
|
||||||
|
<%= render_properties(@properties) %>
|
||||||
|
|
||||||
<% content_for :header_tags do %>
|
<% content_for :header_tags do %>
|
||||||
<%= stylesheet_link_tag "scm" %>
|
<%= stylesheet_link_tag "scm" %>
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => (@entry ? @entry.kind : nil), :revision => @rev } %></h2>
|
<h2><%= render :partial => 'navigation', :locals => { :path => @path, :kind => (@entry ? @entry.kind : nil), :revision => @rev } %></h2>
|
||||||
|
|
||||||
<h3><%=h @entry.name %></h3>
|
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<% if @repository.supports_cat? %>
|
<% if @repository.supports_cat? %>
|
||||||
<%= link_to l(:button_view), {:action => 'entry', :id => @project, :path => to_path_param(@path), :rev => @rev } %> |
|
<%= 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 %>
|
<%= "(#{number_to_human_size(@entry.size)})" if @entry.size %>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<%= render_properties(@properties) %>
|
||||||
|
|
||||||
<%= render(:partial => 'revisions',
|
<%= render(:partial => 'revisions',
|
||||||
:locals => {:project => @project, :path => @path, :revisions => @changesets, :entry => @entry }) unless @changesets.empty? %>
|
:locals => {:project => @project, :path => @path, :revisions => @changesets, :entry => @entry }) unless @changesets.empty? %>
|
||||||
|
|
||||||
|
@ -24,6 +24,20 @@ module Redmine
|
|||||||
end
|
end
|
||||||
|
|
||||||
class AbstractAdapter #:nodoc:
|
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)
|
def initialize(url, root_url=nil, login=nil, password=nil)
|
||||||
@url = url
|
@url = url
|
||||||
@login = login if login && !login.empty?
|
@login = login if login && !login.empty?
|
||||||
@ -78,6 +92,10 @@ module Redmine
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def properties(path, identifier=nil)
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
|
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
@ -131,10 +149,18 @@ module Redmine
|
|||||||
end
|
end
|
||||||
|
|
||||||
def logger
|
def logger
|
||||||
RAILS_DEFAULT_LOGGER
|
self.class.logger
|
||||||
end
|
end
|
||||||
|
|
||||||
def shellout(cmd, &block)
|
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?
|
logger.debug "Shelling out: #{cmd}" if logger && logger.debug?
|
||||||
begin
|
begin
|
||||||
IO.popen(cmd, "r+") do |io|
|
IO.popen(cmd, "r+") do |io|
|
||||||
|
@ -26,6 +26,25 @@ module Redmine
|
|||||||
# SVN executable name
|
# SVN executable name
|
||||||
SVN_BIN = "svn"
|
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
|
# Get info about the svn repository
|
||||||
def info
|
def info
|
||||||
cmd = "#{SVN_BIN} info --xml #{target('')}"
|
cmd = "#{SVN_BIN} info --xml #{target('')}"
|
||||||
@ -88,6 +107,28 @@ module Redmine
|
|||||||
entries.sort_by_name
|
entries.sort_by_name
|
||||||
end
|
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={})
|
def revisions(path=nil, identifier_from=nil, identifier_to=nil, options={})
|
||||||
path ||= ''
|
path ||= ''
|
||||||
identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : "HEAD"
|
identifier_from = (identifier_from and identifier_from.to_i > 0) ? identifier_from.to_i : "HEAD"
|
||||||
|
@ -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 tbody tr.total { font-style: normal; font-weight: bold; color: #555; background-color:#EEEEEE; }
|
||||||
table#time-report .hours-dec { font-size: 0.9em; }
|
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 { font-size: 110%; font-weight: bold; }
|
||||||
.total-hours span.hours-int { font-size: 120%; }
|
.total-hours span.hours-int { font-size: 120%; }
|
||||||
|
|
||||||
|
@ -72,6 +72,19 @@ class RepositoriesSubversionControllerTest < Test::Unit::TestCase
|
|||||||
assert_equal ['folder', '.project', 'helloworld.c', 'helloworld.rb', 'textfile.txt'], assigns(:entries).collect(&:name)
|
assert_equal ['folder', '.project', 'helloworld.c', 'helloworld.rb', 'textfile.txt'], assigns(:entries).collect(&:name)
|
||||||
end
|
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
|
def test_entry
|
||||||
get :entry, :id => 1, :path => ['subversion_test', 'helloworld.c']
|
get :entry, :id => 1, :path => ['subversion_test', 'helloworld.c']
|
||||||
assert_response :success
|
assert_response :success
|
||||||
|
33
test/unit/subversion_adapter_test.rb
Normal file
33
test/unit/subversion_adapter_test.rb
Normal file
@ -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
|
Loading…
x
Reference in New Issue
Block a user