diff --git a/NEWS.mdtext b/NEWS.mdtext index 8820900..ed3cadc 100644 --- a/NEWS.mdtext +++ b/NEWS.mdtext @@ -4,12 +4,15 @@ - Mercurial source views now show parent revisions (if any) and detailed change information - File download URLs now contain the file name rather than the upload id; old links still work though (issue 686) +- Display monotone file and directory attributes in the tree and file view + (needs a monotone with an interface version of 13.1 or newer) -## Bugfixes +## Bugfixes +- The SVN interface acts more robust if an underlying repository has been restructured (issue 364) - monotone zip archive entries now all carry the revision date as mtime (issue 645) - Timeline only displays filter options for items a user has actually access to (issue 655) -- The log, tags and branches parsers for Mercurial are more robust now (issue 663) +- The log, tags and branches parsers for Mercurial are more robust now (issue 663) - Fix SSH public key parsing issues and improve the check for existing, uploaded keys (issue 679) - Let the SVN command line client not store the login credentials we give him as arguments diff --git a/src/IDF/Scm/Monotone.php b/src/IDF/Scm/Monotone.php index d75fa6d..d04c53b 100644 --- a/src/IDF/Scm/Monotone.php +++ b/src/IDF/Scm/Monotone.php @@ -31,10 +31,10 @@ class IDF_Scm_Monotone extends IDF_Scm /** the minimum supported interface version */ public static $MIN_INTERFACE_VERSION = 13.0; - private $stdio; - private static $instances = array(); + private $stdio; + /** * Constructor */ @@ -698,6 +698,29 @@ class IDF_Scm_Monotone extends IDF_Scm return (object) $res; } + /** + * @see IDF_Scm::getProperties() + */ + public function getProperties($rev, $path='') + { + $out = $this->stdio->exec(array('interface_version')); + // support for querying file attributes of committed revisions + // was added for mtn 1.1 (interface version 13.1) + if (floatval($out) < 13.1) + return array(); + + $out = $this->stdio->exec(array('get_attributes', $path), array('r' => $rev)); + $stanzas = IDF_Scm_Monotone_BasicIO::parse($out); + $res = array(); + + foreach ($stanzas as $stanza) { + $line = $stanza[0]; + $res[$line['values'][0]] = $line['values'][1]; + } + + return $res; + } + /** * @see IDF_Scm::getExtraProperties */ diff --git a/src/IDF/Scm/Svn.php b/src/IDF/Scm/Svn.php index 791624f..340fe14 100644 --- a/src/IDF/Scm/Svn.php +++ b/src/IDF/Scm/Svn.php @@ -163,11 +163,11 @@ class IDF_Scm_Svn extends IDF_Scm return IDF_Scm::REVISION_VALID; } - $cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --username=%s --password=%s %s@%s', + $cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --username=%s --password=%s --revision=%s %s', escapeshellarg($this->username), escapeshellarg($this->password), - escapeshellarg($this->repo), - escapeshellarg($rev)); + escapeshellarg($rev), + escapeshellarg($this->repo)); $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; self::exec('IDF_Scm_Svn::validateRevision', $cmd, $out, $ret); @@ -176,7 +176,6 @@ class IDF_Scm_Svn extends IDF_Scm return IDF_Scm::REVISION_INVALID; } - /** * Test a given object hash. * @@ -191,11 +190,11 @@ class IDF_Scm_Svn extends IDF_Scm } // Else, test the path on revision - $cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --xml --username=%s --password=%s %s@%s', + $cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --xml --username=%s --password=%s --revision=%s %s', escapeshellarg($this->username), escapeshellarg($this->password), - escapeshellarg($this->repo.'/'.self::smartEncode($path)), - escapeshellarg($rev)); + escapeshellarg($rev), + escapeshellarg($this->repo.'/'.self::smartEncode($path))); $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; $xmlInfo = self::shell_exec('IDF_Scm_Svn::testHash', $cmd); @@ -218,11 +217,11 @@ class IDF_Scm_Svn extends IDF_Scm public function getTree($commit, $folder='/', $branch=null) { - $cmd = sprintf(Pluf::f('svn_path', 'svn').' ls --no-auth-cache --xml --username=%s --password=%s %s@%s', + $cmd = sprintf(Pluf::f('svn_path', 'svn').' ls --no-auth-cache --xml --username=%s --password=%s --revision=%s %s', escapeshellarg($this->username), escapeshellarg($this->password), - escapeshellarg($this->repo.'/'.self::smartEncode($folder)), - escapeshellarg($commit)); + escapeshellarg($commit), + escapeshellarg($this->repo.'/'.self::smartEncode($folder))); $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; $xml = simplexml_load_string(self::shell_exec('IDF_Scm_Svn::getTree', $cmd)); $res = array(); @@ -248,7 +247,6 @@ class IDF_Scm_Svn extends IDF_Scm return $res; } - /** * Get the commit message of a revision revision. * @@ -260,11 +258,11 @@ class IDF_Scm_Svn extends IDF_Scm if (isset($this->cache['commitmess'][$rev])) { return $this->cache['commitmess'][$rev]; } - $cmd = sprintf(Pluf::f('svn_path', 'svn').' log --no-auth-cache --xml --limit 1 --username=%s --password=%s %s@%s', + $cmd = sprintf(Pluf::f('svn_path', 'svn').' log --no-auth-cache --xml --limit 1 --username=%s --password=%s --revision=%s %s', escapeshellarg($this->username), escapeshellarg($this->password), - escapeshellarg($this->repo), - escapeshellarg($rev)); + escapeshellarg($rev), + escapeshellarg($this->repo)); $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; try { $xml = simplexml_load_string(self::shell_exec('IDF_Scm_Svn::getCommitMessage', $cmd)); @@ -281,11 +279,11 @@ class IDF_Scm_Svn extends IDF_Scm if ($rev == null) { $rev = 'HEAD'; } - $cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --xml --username=%s --password=%s %s@%s', + $cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --xml --username=%s --password=%s --revision=%s %s', escapeshellarg($this->username), escapeshellarg($this->password), - escapeshellarg($this->repo.'/'.self::smartEncode($filename)), - escapeshellarg($rev)); + escapeshellarg($rev), + escapeshellarg($this->repo.'/'.self::smartEncode($filename))); $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; $xml = simplexml_load_string(self::shell_exec('IDF_Scm_Svn::getPathInfo', $cmd)); if (!isset($xml->entry)) { @@ -308,11 +306,11 @@ class IDF_Scm_Svn extends IDF_Scm public function getFile($def, $cmd_only=false) { - $cmd = sprintf(Pluf::f('svn_path', 'svn').' cat --no-auth-cache --username=%s --password=%s %s@%s', + $cmd = sprintf(Pluf::f('svn_path', 'svn').' cat --no-auth-cache --username=%s --password=%s --revision=%s %s', escapeshellarg($this->username), escapeshellarg($this->password), - escapeshellarg($this->repo.'/'.self::smartEncode($def->fullpath)), - escapeshellarg($def->rev)); + escapeshellarg($def->rev), + escapeshellarg($this->repo.'/'.self::smartEncode($def->fullpath))); $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; return ($cmd_only) ? $cmd : self::shell_exec('IDF_Scm_Svn::getFile', $cmd); @@ -329,7 +327,7 @@ class IDF_Scm_Svn extends IDF_Scm return $this->cache['branches']; } $res = array(); - $cmd = sprintf(Pluf::f('svn_path', 'svn').' ls --no-auth-cache --username=%s --password=%s %s@HEAD', + $cmd = sprintf(Pluf::f('svn_path', 'svn').' ls --no-auth-cache --username=%s --password=%s --revision=HEAD %s', escapeshellarg($this->username), escapeshellarg($this->password), escapeshellarg($this->repo.'/branches')); @@ -344,7 +342,7 @@ class IDF_Scm_Svn extends IDF_Scm } } ksort($res); - $cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --username=%s --password=%s %s@HEAD', + $cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --username=%s --password=%s --revision=HEAD %s', escapeshellarg($this->username), escapeshellarg($this->password), escapeshellarg($this->repo.'/trunk')); @@ -368,7 +366,7 @@ class IDF_Scm_Svn extends IDF_Scm return $this->cache['tags']; } $res = array(); - $cmd = sprintf(Pluf::f('svn_path', 'svn').' ls --no-auth-cache --username=%s --password=%s %s@HEAD', + $cmd = sprintf(Pluf::f('svn_path', 'svn').' ls --no-auth-cache --username=%s --password=%s --revision=HEAD %s', escapeshellarg($this->username), escapeshellarg($this->password), escapeshellarg($this->repo.'/tags')); @@ -412,7 +410,6 @@ class IDF_Scm_Svn extends IDF_Scm return array(); } - /** * Get commit details. * @@ -426,11 +423,11 @@ class IDF_Scm_Svn extends IDF_Scm return false; } $res = array(); - $cmd = sprintf(Pluf::f('svn_path', 'svn').' log --no-auth-cache --xml --limit 1 -v --username=%s --password=%s %s@%s', + $cmd = sprintf(Pluf::f('svn_path', 'svn').' log --no-auth-cache --xml --limit 1 -v --username=%s --password=%s --revision=%s %s', escapeshellarg($this->username), escapeshellarg($this->password), - escapeshellarg($this->repo), - escapeshellarg($commit)); + escapeshellarg($commit), + escapeshellarg($this->repo)); $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; $xmlRes = self::shell_exec('IDF_Scm_Svn::getCommit', $cmd); $xml = simplexml_load_string($xmlRes); @@ -498,12 +495,12 @@ class IDF_Scm_Svn extends IDF_Scm $branch = 'HEAD'; } $res = array(); - $cmd = sprintf(Pluf::f('svn_path', 'svn').' log --no-auth-cache --xml -v --limit %s --username=%s --password=%s %s@%s', + $cmd = sprintf(Pluf::f('svn_path', 'svn').' log --no-auth-cache --xml -v --limit %s --username=%s --password=%s --revision=%s %s', escapeshellarg($n), escapeshellarg($this->username), escapeshellarg($this->password), - escapeshellarg($this->repo), - escapeshellarg($branch)); + escapeshellarg($branch), + escapeshellarg($this->repo)); $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; $xmlRes = self::shell_exec('IDF_Scm_Svn::getChangeLog', $cmd); $xml = simplexml_load_string($xmlRes); @@ -520,7 +517,6 @@ class IDF_Scm_Svn extends IDF_Scm return $res; } - /** * Get additionnals properties on path and revision * @@ -531,11 +527,11 @@ class IDF_Scm_Svn extends IDF_Scm public function getProperties($rev, $path='') { $res = array(); - $cmd = sprintf(Pluf::f('svn_path', 'svn').' proplist --no-auth-cache --xml --username=%s --password=%s %s@%s', + $cmd = sprintf(Pluf::f('svn_path', 'svn').' proplist --no-auth-cache --xml --username=%s --password=%s --revision=%s %s', escapeshellarg($this->username), escapeshellarg($this->password), - escapeshellarg($this->repo.'/'.self::smartEncode($path)), - escapeshellarg($rev)); + escapeshellarg($rev), + escapeshellarg($this->repo.'/'.self::smartEncode($path))); $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; $xmlProps = self::shell_exec('IDF_Scm_Svn::getProperties', $cmd); $props = simplexml_load_string($xmlProps); @@ -554,7 +550,6 @@ class IDF_Scm_Svn extends IDF_Scm return $res; } - /** * Get a specific additionnal property on path and revision * @@ -566,12 +561,12 @@ class IDF_Scm_Svn extends IDF_Scm private function getProperty($property, $rev, $path='') { $res = array(); - $cmd = sprintf(Pluf::f('svn_path', 'svn').' propget --no-auth-cache --xml %s --username=%s --password=%s %s@%s', + $cmd = sprintf(Pluf::f('svn_path', 'svn').' propget --no-auth-cache --xml %s --username=%s --password=%s --revision=%s %s', escapeshellarg($property), escapeshellarg($this->username), escapeshellarg($this->password), - escapeshellarg($this->repo.'/'.self::smartEncode($path)), - escapeshellarg($rev)); + escapeshellarg($rev), + escapeshellarg($this->repo.'/'.self::smartEncode($path))); $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; $xmlProp = self::shell_exec('IDF_Scm_Svn::getProperty', $cmd); $prop = simplexml_load_string($xmlProp); @@ -579,7 +574,6 @@ class IDF_Scm_Svn extends IDF_Scm return (string) $prop->target->property; } - /** * Get the number of the last commit in the repository. * @@ -590,11 +584,11 @@ class IDF_Scm_Svn extends IDF_Scm public function getLastCommit($rev='HEAD') { $xmlInfo = ''; - $cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --xml --username=%s --password=%s %s@%s', + $cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --xml --username=%s --password=%s --revision=%s %s', escapeshellarg($this->username), escapeshellarg($this->password), - escapeshellarg($this->repo), - escapeshellarg($rev)); + escapeshellarg($rev), + escapeshellarg($this->repo)); $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd; $xmlInfo = self::shell_exec('IDF_Scm_Svn::getLastCommit', $cmd); diff --git a/src/IDF/locale/de/idf.po b/src/IDF/locale/de/idf.po index f533215..ec7530d 100644 --- a/src/IDF/locale/de/idf.po +++ b/src/IDF/locale/de/idf.po @@ -3231,7 +3231,7 @@ msgid "" "%%cobject.date%%." msgstr "" "Quellcode bei Revision %%commit%% " -"erzeugt am %%cobject.date%%." +"erzeugt %%cobject.date%%." #: IDF/gettexttemplates/idf/source/git/file.html.php:4 #: IDF/gettexttemplates/idf/source/git/tree.html.php:4 @@ -3409,8 +3409,8 @@ msgstr "Revision:" #: IDF/gettexttemplates/idf/source/svn/commit.html.php:4 #: IDF/gettexttemplates/idf/source/svn/file.html.php:11 #: IDF/gettexttemplates/idf/source/svn/tree.html.php:16 -msgid "Go to revision" -msgstr "Zu Revision gehen:" +msgid "Switch" +msgstr "Wechseln" #: IDF/gettexttemplates/idf/source/svn/file.html.php:6 #: IDF/gettexttemplates/idf/source/svn/tree.html.php:11 diff --git a/src/IDF/templates/idf/source/mtn/file.html b/src/IDF/templates/idf/source/mtn/file.html index 2901c60..0c1bd19 100644 --- a/src/IDF/templates/idf/source/mtn/file.html +++ b/src/IDF/templates/idf/source/mtn/file.html @@ -5,12 +5,25 @@

{trans 'Root'}/{if $breadcrumb}{$breadcrumb|safe}{/if}

-{if !$tree_in and !$tags_in} +{if (!$tree_in and !$tags_in) or $props} {aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)} - + {if $props} + + {/if} + {if !$tree_in and !$tags_in} + + + + {/if} {/if} diff --git a/src/IDF/templates/idf/source/mtn/tree.html b/src/IDF/templates/idf/source/mtn/tree.html index 5ca9db5..2a77ad3 100644 --- a/src/IDF/templates/idf/source/mtn/tree.html +++ b/src/IDF/templates/idf/source/mtn/tree.html @@ -11,14 +11,27 @@ -{if !$tree_in and !$tags_in} + +{if (!$tree_in and !$tags_in) or $props} {aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)} - + {if $props} + + {/if} + {if !$tree_in and !$tags_in} + + {/if} +{/if} -{/if} + {if $base} diff --git a/src/IDF/templates/idf/source/svn/changelog.html b/src/IDF/templates/idf/source/svn/changelog.html index b535e5c..12287a8 100644 --- a/src/IDF/templates/idf/source/svn/changelog.html +++ b/src/IDF/templates/idf/source/svn/changelog.html @@ -4,7 +4,7 @@

{trans 'Revision:'} {$commit}

- +

{/block} diff --git a/src/IDF/templates/idf/source/svn/commit.html b/src/IDF/templates/idf/source/svn/commit.html index 830cf60..024f539 100644 --- a/src/IDF/templates/idf/source/svn/commit.html +++ b/src/IDF/templates/idf/source/svn/commit.html @@ -4,7 +4,7 @@

{trans 'Revision:'} {$commit}

- +

{/block} diff --git a/src/IDF/templates/idf/source/svn/file.html b/src/IDF/templates/idf/source/svn/file.html index 5ed8114..4e08bdc 100644 --- a/src/IDF/templates/idf/source/svn/file.html +++ b/src/IDF/templates/idf/source/svn/file.html @@ -6,13 +6,13 @@
{blocktrans}Source at commit {$commit} created {$cobject.date|dateago}.{/blocktrans}
-{blocktrans}By {$cobject.author|strip_tags|trim}, {$cobject.title}{/blocktrans} -
+
    + {foreach $props as $prop => $val} +
  • {blocktrans}Property {$prop} set to {$val}{/blocktrans}
  • + {/foreach} +
+
{blocktrans}Source at commit {$commit} created {$cobject.date|dateago}.{/blocktrans}
+ {blocktrans}By {$cobject.author|strip_tags|trim}, {$cobject.title}{/blocktrans} +
{trans 'Message'} {trans 'Size'}
{blocktrans}Source at commit {$commit} created {$cobject.date|dateago}.{/blocktrans}
-{blocktrans}By {$cobject.author|strip_tags|trim}, {$cobject.title}{/blocktrans} -
+
    + {foreach $props as $prop => $val} +
  • {blocktrans}Property {$prop} set to {$val}{/blocktrans}
  • + {/foreach} +
+
{blocktrans}Source at commit {$commit} created {$cobject.date|dateago}.{/blocktrans}
+ {blocktrans}By {$cobject.author|strip_tags|trim}, {$cobject.title}{/blocktrans} +
 
{if !$tree_in || $props} - {aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)} + {aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)} {if $props} @@ -38,7 +38,7 @@

-

+

{/block} diff --git a/src/IDF/templates/idf/source/svn/tree.html b/src/IDF/templates/idf/source/svn/tree.html index dbb1713..fd5d5aa 100644 --- a/src/IDF/templates/idf/source/svn/tree.html +++ b/src/IDF/templates/idf/source/svn/tree.html @@ -13,13 +13,13 @@ {if (!$tree_in and !$tags_in and $commit != 'HEAD') || $props} - {aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)} + {aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $commit)} {if $props} @@ -45,7 +45,7 @@ - + {$file.author|strip_tags|trim}{trans ':'} {issuetext $file.log, $request, true, false} {if $file.type == 'blob'} @@ -66,7 +66,7 @@

-

+

{trans 'Branches:'}
{foreach $branches as $branch => $path} diff --git a/test/IDF/Scm/MonotoneTest.php b/test/IDF/Scm/MonotoneTest.php index a278fba..c50c1b0 100644 --- a/test/IDF/Scm/MonotoneTest.php +++ b/test/IDF/Scm/MonotoneTest.php @@ -714,6 +714,31 @@ END; $this->assertEquals('', $commit->diff); } + public function testGetProperties() + { + $rev = "2345678901234567890123456789012345678901"; + + $instance = $this->createMock(); + $instance->getStdio()->setExpectedOutput(array('interface_version'), array(), '13.1'); + + $stdio =<<- +nesses" +END; + $instance->getStdio()->setExpectedOutput(array('get_attributes', 'foo'), array('r' => $rev), $stdio); + $res = $instance->getProperties($rev, 'foo'); + + $this->assertEquals(2, count($res)); + $this->assertEquals(array( + 'foo' => 'bar', + "some new\nline" => "and more -\nnesses" + ), $res); + } + public function testGetExtraProperties() { $instance = $this->createMock();

    {foreach $props as $prop => $val} -
  • {trans 'Property'} {$prop} {trans 'set to:'} {$val}
  • +
  • {blocktrans}Property {$prop} set to {$val}{/blocktrans}
  • {/foreach}
{trans 'Size'}
    {foreach $props as $prop => $val} -
  • {trans 'Property'} {$prop} {trans 'set to:'} {$val}
  • +
  • {blocktrans}Property {$prop} set to {$val}{/blocktrans}
  • {/foreach}
{$file.type} {$file.file}{$file.date|dateago:"without"}{$file.date|dateago:"without"} {$file.rev}