23 Commits

Author SHA1 Message Date
William MARTIN
813184f06c Fix an issue with "unasigned issues"
Rename the view to userIssues
2011-10-04 10:05:42 +02:00
William MARTIN
d860f299fd The last part of the previous patch. 2011-10-03 10:17:10 +02:00
William MARTIN
33882d4fa7 Update how the myIssue view works.
It's allow to display this view for other members.
In the issue summary, we can now follow make links for each user display in the part "Unresolved: By Assignee".
2011-10-03 10:00:35 +02:00
William MARTIN
13fad756ab Fix issue 732
Commit based on Stéphane Baron patch
2011-10-01 22:43:00 +02:00
Thomas Keller
1f0791df0e Make the '@rev' part in the regex optional (fixes issue 730). 2011-09-12 17:54:40 +02:00
Thomas Keller
a2c832a130 Improve the 'parents' parsing for git and ignore any empty parts; also
react gracefully if we could not parse the parents for some weird reason.
2011-08-17 20:17:14 +02:00
Thomas Keller
b17de014ec Reworked the option / argument handling in the SVN interface to
have less code duplication.
2011-08-13 02:28:15 +02:00
Thomas Keller
b1b72190e1 Dropped a few more not needed files. 2011-08-13 01:19:58 +02:00
Thomas Keller
2ff2f888bc - Make the SVN test case work without specific test configuration.
- Rename the test repo to match the test function so we can create
  more test repositories for other tests at a later stage.
- Remove useless hooks and configs from the repo (they are not used
  for our specific test and just need memory).
- Note the fix for issue 721 in NEWS.mdtext.
2011-08-13 01:11:00 +02:00
Patrick Georgi
57c2389aae Make SVN backend more robust
The SVN backend failed when trying to access historical information on deleted files.

There's also an initial test case for the SVN backend, testing this issue
and issue 364, which is about a similar problem for renamed files.
Reverting any of these fixes breaks the test.
2011-08-12 20:53:26 +02:00
Thomas Keller
d54c86f813 Note the change from issue 716. 2011-07-27 20:06:56 +02:00
Thomas Keller
05a9697007 Merge branch 'feature.content-md5' into develop 2011-07-27 19:59:18 +02:00
Patrick Georgi
945429abf0 Provide MD5 value of downloads to HTTP client
Content-MD5 is a HTTP header to provide end-to-end integrity checks
(see RFC2616, 14.15). This doesn't protect against malicious
modifications, but against transmissions errors and storage errors
on the server.

The change also removes one redirect when downloading files.
2011-07-24 22:12:36 +02:00
William MARTIN
a016bcb51b Merge branch 'develop' of projects.ceondo.com:indefero into develop 2011-07-05 11:31:05 +02:00
William MARTIN
f2b1ce795c Fix issue 247 : cron overwrites authorized_keys during cron run 2011-07-05 11:30:23 +02:00
Thomas Keller
3a8c56acc4 Postgres needs a VARCHAR cast, which MySQL doesn't understand, of
course. *sigh*
2011-07-01 13:35:43 +02:00
Thomas Keller
7b2552f940 Postgres (and probably others as well) needs an explicit char cast. 2011-06-30 00:25:29 +02:00
Thomas Keller
324b202215 Fix the rendering of issue changes in a mail template and the issue feed fragment. 2011-06-29 17:41:18 +02:00
Loïc d'Anterroches
2c2da6082a Fixed stupid missing semicolon. 2011-06-29 14:41:57 +02:00
Loïc d'Anterroches
dd3fbbd7e4 Fixes to support older PHP versions. 2011-06-29 14:30:17 +02:00
William MARTIN
9bbcd571ec Merge branch 'feature.issue-summary' into develop 2011-06-20 11:37:26 +02:00
Thomas Keller
d1bcdcda20 Fix the mtn getChanges() test. 2011-06-17 23:50:35 +02:00
Thomas Keller
4879d64989 If git's author name does not contain valid utf-8 bytes, skip the author
lookup in the database, which would otherwise only bring up errors.
2011-06-15 13:50:02 +02:00
42 changed files with 408 additions and 152 deletions

View File

@@ -16,7 +16,7 @@ or newer to properly run this version of Indefero!
## Bugfixes ## Bugfixes
- The SVN interface acts more robust if an underlying repository has been restructured (issue 364) - The SVN interface acts more robust if an underlying repository has been restructured (issues 364 and 721)
- monotone zip archive entries now all carry the revision date as mtime (issue 645) - 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) - 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)
@@ -29,10 +29,17 @@ or newer to properly run this version of Indefero!
- Prevent a timeout from popping up when Usher is restarted (issue 695) - Prevent a timeout from popping up when Usher is restarted (issue 695)
- The SyncMonotone plugin now cleans up partial artifacts it created during the addition of - The SyncMonotone plugin now cleans up partial artifacts it created during the addition of
a new project or monotone key, in case an error popped up in the middle (issue 697) a new project or monotone key, in case an error popped up in the middle (issue 697)
- Indefero now sends the MD5 checksum as HTTP header when downloading a file from the
download area. Additionally, a unneeded redirect has been removed. (issue 716)
- Source links without a specific revision did not work due to a wrong regex (issue 730)
- Better error detection and reporting in the SyncMonotone plugin - Better error detection and reporting in the SyncMonotone plugin
ATTENTION: This needs Pluf 46b7f251 or newer! ATTENTION: This needs Pluf 46b7f251 or newer!
- Fix the branch links users of the Subversion frontend get when they enter a wrong revision - Fix the branch links users of the Subversion frontend get when they enter a wrong revision
and only display this list if there are any branches available for all SCMs and only display this list if there are any branches available for all SCMs
- If git's author name is not encoded in an UTF-8 compatible encoding, skip the author lookup,
as we have no information what the author string is actually encoded in
- Indefero no longer displays an empty parents paragraph in the commit view for root revisions of
a git repository
## Documentation ## Documentation

View File

@@ -88,6 +88,7 @@ class IDF_Middleware
'showuser' => 'IDF_Template_ShowUser', 'showuser' => 'IDF_Template_ShowUser',
'ashowuser' => 'IDF_Template_AssignShowUser', 'ashowuser' => 'IDF_Template_AssignShowUser',
'appversion' => 'IDF_Template_AppVersion', 'appversion' => 'IDF_Template_AppVersion',
'upload' => 'IDF_Template_Tag_UploadUrl',
)); ));
$params['modifiers'] = array_merge($params['modifiers'], $params['modifiers'] = array_merge($params['modifiers'],
array( array(

View File

@@ -59,6 +59,16 @@ class IDF_Plugin_SyncGit_Cron
$out .= sprintf($template, $cmd, $key->login, $content)."\n"; $out .= sprintf($template, $cmd, $key->login, $content)."\n";
} }
} }
$out = "# indefero start" . PHP_EOL . $out . "# indefero end" . PHP_EOL;
// We update only the part of the file between IDF_START / IDF_END comment
$original_keys = file_get_contents($authorized_keys);
if (strstr($original_keys, "# indefero start") && strstr($original_keys, "# indefero end")) {
$out = preg_replace('/(#\sindefero\sstart).+(#\sindefero\send\s\s?)/isU',
$out, $original_keys);
} else {
$out .= $original_keys;
}
file_put_contents($authorized_keys, $out, LOCK_EX); file_put_contents($authorized_keys, $out, LOCK_EX);
} }

View File

@@ -152,15 +152,14 @@ class IDF_Project extends Pluf_Model
break; break;
} }
$sqlIssueTable = Pluf::factory('IDF_Issue')->getSqlTable(); $sqlIssueTable = Pluf::factory('IDF_Issue')->getSqlTable();
$query = <<<"QUERY" $query = "SELECT uid AS id,COUNT(uid) AS nb
SELECT uid AS id,COUNT(uid) AS nb
FROM ( FROM (
SELECT COALESCE(owner, -1) AS uid SELECT COALESCE(owner, -1) AS uid
FROM $sqlIssueTable FROM $sqlIssueTable
WHERE status IN ($tags) WHERE status IN ($tags)
) AS ff ) AS ff
GROUP BY uid GROUP BY uid";
QUERY;
$db = Pluf::db(); $db = Pluf::db();
$dbData = $db->select($query); $dbData = $db->select($query);
$ownerStatistics = array(); $ownerStatistics = array();

View File

@@ -346,6 +346,14 @@ class IDF_Scm_Git extends IDF_Scm
if (!preg_match('/<(.*)>/', $author, $match)) { if (!preg_match('/<(.*)>/', $author, $match)) {
return null; return null;
} }
// FIXME: newer git versions know a i18n.commitencoding setting which
// leads to another header, "encoding", with which we _could_ try to
// decode the string into utf8. Unfortunately this does not always
// work, especially not in older repos, so we would then still have
// to supply some fallback.
if (!mb_check_encoding($match[1], 'UTF-8')) {
return null;
}
$sql = new Pluf_SQL('login=%s', array($match[1])); $sql = new Pluf_SQL('login=%s', array($match[1]));
$users = Pluf::factory('Pluf_User')->getList(array('filter'=>$sql->gen())); $users = Pluf::factory('Pluf_User')->getList(array('filter'=>$sql->gen()));
if ($users->count() > 0) { if ($users->count() > 0) {
@@ -638,7 +646,11 @@ class IDF_Scm_Git extends IDF_Scm
$c['full_message'] = IDF_Commit::toUTF8($c['full_message']); $c['full_message'] = IDF_Commit::toUTF8($c['full_message']);
$c['title'] = IDF_Commit::toUTF8($c['title']); $c['title'] = IDF_Commit::toUTF8($c['title']);
if (isset($c['parents'])) { if (isset($c['parents'])) {
$c['parents'] = explode(' ', trim($c['parents'])); $c['parents'] = preg_split('/ /', trim($c['parents']), -1, PREG_SPLIT_NO_EMPTY);
} else {
// this is actually an error state because we should _always_
// be able to parse the parents line with every git version
$c['parents'] = null;
} }
$res[] = (object) $c; $res[] = (object) $c;
return $res; return $res;

View File

@@ -33,7 +33,6 @@
*/ */
class IDF_Scm_Svn extends IDF_Scm class IDF_Scm_Svn extends IDF_Scm
{ {
public $username = ''; public $username = '';
public $password = ''; public $password = '';
private $assoc = array('dir' => 'tree', private $assoc = array('dir' => 'tree',
@@ -48,11 +47,7 @@ class IDF_Scm_Svn extends IDF_Scm
public function isAvailable() public function isAvailable()
{ {
$cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --xml --username=%s --password=%s %s', $cmd = $this->svnCmd(array('info', '--xml'), $this->repo);
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xmlInfo = self::shell_exec('IDF_Scm_Svn::isAvailable', $cmd); $xmlInfo = self::shell_exec('IDF_Scm_Svn::isAvailable', $cmd);
try { try {
@@ -163,12 +158,7 @@ class IDF_Scm_Svn extends IDF_Scm
return IDF_Scm::REVISION_VALID; return IDF_Scm::REVISION_VALID;
} }
$cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --username=%s --password=%s --revision=%s %s', $cmd = $this->svnCmd(array('info'), $this->repo, $rev);
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($rev),
escapeshellarg($this->repo));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
self::exec('IDF_Scm_Svn::validateRevision', $cmd, $out, $ret); self::exec('IDF_Scm_Svn::validateRevision', $cmd, $out, $ret);
if ($ret == 0) if ($ret == 0)
@@ -190,12 +180,9 @@ class IDF_Scm_Svn extends IDF_Scm
} }
// Else, test the path on revision // Else, test the path on revision
$cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --xml --username=%s --password=%s --revision=%s %s', $cmd = $this->svnCmd(array('info', '--xml'),
escapeshellarg($this->username), $this->repo.'/'.self::smartEncode($path),
escapeshellarg($this->password), $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); $xmlInfo = self::shell_exec('IDF_Scm_Svn::testHash', $cmd);
// If exception is thrown, return false // If exception is thrown, return false
@@ -217,12 +204,9 @@ class IDF_Scm_Svn extends IDF_Scm
public function getTree($commit, $folder='/', $branch=null) public function getTree($commit, $folder='/', $branch=null)
{ {
$cmd = sprintf(Pluf::f('svn_path', 'svn').' ls --no-auth-cache --xml --username=%s --password=%s --revision=%s %s', $cmd = $this->svnCmd(array('ls', '--xml'),
escapeshellarg($this->username), $this->repo.'/'.self::smartEncode($folder),
escapeshellarg($this->password), $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)); $xml = simplexml_load_string(self::shell_exec('IDF_Scm_Svn::getTree', $cmd));
$res = array(); $res = array();
$folder = (strlen($folder) and ($folder != '/')) ? $folder.'/' : ''; $folder = (strlen($folder) and ($folder != '/')) ? $folder.'/' : '';
@@ -258,12 +242,7 @@ class IDF_Scm_Svn extends IDF_Scm
if (isset($this->cache['commitmess'][$rev])) { if (isset($this->cache['commitmess'][$rev])) {
return $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 --revision=%s %s', $cmd = $this->svnCmd(array('log', '--xml', '--limit', '1'), $this->repo, $rev);
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($rev),
escapeshellarg($this->repo));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
try { try {
$xml = simplexml_load_string(self::shell_exec('IDF_Scm_Svn::getCommitMessage', $cmd)); $xml = simplexml_load_string(self::shell_exec('IDF_Scm_Svn::getCommitMessage', $cmd));
$this->cache['commitmess'][$rev] = (string) $xml->logentry->msg; $this->cache['commitmess'][$rev] = (string) $xml->logentry->msg;
@@ -279,12 +258,8 @@ class IDF_Scm_Svn extends IDF_Scm
if ($rev == null) { if ($rev == null) {
$rev = 'HEAD'; $rev = 'HEAD';
} }
$cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --xml --username=%s --password=%s --revision=%s %s', $cmd = $this->svnCmd(array('info', '--xml'),
escapeshellarg($this->username), $this->repo.'/'.self::smartEncode($filename), $rev);
escapeshellarg($this->password),
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)); $xml = simplexml_load_string(self::shell_exec('IDF_Scm_Svn::getPathInfo', $cmd));
if (!isset($xml->entry)) { if (!isset($xml->entry)) {
return false; return false;
@@ -306,12 +281,9 @@ class IDF_Scm_Svn extends IDF_Scm
public function getFile($def, $cmd_only=false) public function getFile($def, $cmd_only=false)
{ {
$cmd = sprintf(Pluf::f('svn_path', 'svn').' cat --no-auth-cache --username=%s --password=%s --revision=%s %s', $cmd = $this->svnCmd(array('cat'),
escapeshellarg($this->username), $this->repo.'/'.self::smartEncode($def->fullpath),
escapeshellarg($this->password), $def->rev);
escapeshellarg($def->rev),
escapeshellarg($this->repo.'/'.self::smartEncode($def->fullpath)));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
return ($cmd_only) ? return ($cmd_only) ?
$cmd : self::shell_exec('IDF_Scm_Svn::getFile', $cmd); $cmd : self::shell_exec('IDF_Scm_Svn::getFile', $cmd);
} }
@@ -327,11 +299,7 @@ class IDF_Scm_Svn extends IDF_Scm
return $this->cache['branches']; return $this->cache['branches'];
} }
$res = array(); $res = array();
$cmd = sprintf(Pluf::f('svn_path', 'svn').' ls --no-auth-cache --username=%s --password=%s --revision=HEAD %s', $cmd = $this->svnCmd(array('ls'), $this->repo.'/branches', 'HEAD');
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo.'/branches'));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
self::exec('IDF_Scm_Svn::getBranches', $cmd, $out, $ret); self::exec('IDF_Scm_Svn::getBranches', $cmd, $out, $ret);
if ($ret == 0) { if ($ret == 0) {
foreach ($out as $entry) { foreach ($out as $entry) {
@@ -342,11 +310,8 @@ class IDF_Scm_Svn extends IDF_Scm
} }
} }
ksort($res); ksort($res);
$cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --username=%s --password=%s --revision=HEAD %s',
escapeshellarg($this->username), $cmd = $this->svnCmd(array('info'), $this->repo.'/trunk', 'HEAD');
escapeshellarg($this->password),
escapeshellarg($this->repo.'/trunk'));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
self::exec('IDF_Scm_Svn::getBranches', $cmd, $out, $ret); self::exec('IDF_Scm_Svn::getBranches', $cmd, $out, $ret);
if ($ret == 0) { if ($ret == 0) {
$res = array('trunk' => 'trunk') + $res; $res = array('trunk' => 'trunk') + $res;
@@ -366,11 +331,7 @@ class IDF_Scm_Svn extends IDF_Scm
return $this->cache['tags']; return $this->cache['tags'];
} }
$res = array(); $res = array();
$cmd = sprintf(Pluf::f('svn_path', 'svn').' ls --no-auth-cache --username=%s --password=%s --revision=HEAD %s', $cmd = $this->svnCmd(array('ls'), $this->repo.'/tags', 'HEAD');
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo.'/tags'));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
self::exec('IDF_Scm_Svn::getTags', $cmd, $out, $ret); self::exec('IDF_Scm_Svn::getTags', $cmd, $out, $ret);
if ($ret == 0) { if ($ret == 0) {
foreach ($out as $entry) { foreach ($out as $entry) {
@@ -423,12 +384,8 @@ class IDF_Scm_Svn extends IDF_Scm
return false; return false;
} }
$res = array(); $res = array();
$cmd = sprintf(Pluf::f('svn_path', 'svn').' log --no-auth-cache --xml --limit 1 -v --username=%s --password=%s --revision=%s %s', $cmd = $this->svnCmd(array('log', '--xml', '--limit', '1', '-v'),
escapeshellarg($this->username), $this->repo, $commit);
escapeshellarg($this->password),
escapeshellarg($commit),
escapeshellarg($this->repo));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xmlRes = self::shell_exec('IDF_Scm_Svn::getCommit', $cmd); $xmlRes = self::shell_exec('IDF_Scm_Svn::getCommit', $cmd);
$xml = simplexml_load_string($xmlRes); $xml = simplexml_load_string($xmlRes);
$res['author'] = (string) $xml->logentry->author; $res['author'] = (string) $xml->logentry->author;
@@ -470,12 +427,7 @@ class IDF_Scm_Svn extends IDF_Scm
private function getDiff($rev='HEAD') private function getDiff($rev='HEAD')
{ {
$res = array(); $res = array();
$cmd = sprintf(Pluf::f('svn_path', 'svn').' diff --no-auth-cache -c %s --username=%s --password=%s %s', $cmd = $this->svnCmd(array('diff', '-c', $rev), $this->repo);
escapeshellarg($rev),
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
return self::shell_exec('IDF_Scm_Svn::getDiff', $cmd); return self::shell_exec('IDF_Scm_Svn::getDiff', $cmd);
} }
@@ -487,13 +439,8 @@ class IDF_Scm_Svn extends IDF_Scm
if ($this->validateRevision($commit) != IDF_Scm::REVISION_VALID) { if ($this->validateRevision($commit) != IDF_Scm::REVISION_VALID) {
return null; return null;
} }
$cmd = sprintf(Pluf::f('svn_path', 'svn').' log --xml -v --no-auth-cache -r %s --username=%s --password=%s %s', $cmd = $this->svnCmd(array('log', '--xml', '-v'), $this->repo, $commit);
escapeshellarg($commit),
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo));
$out = array(); $out = array();
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$out = self::shell_exec('IDF_Scm_Svn::getChanges', $cmd); $out = self::shell_exec('IDF_Scm_Svn::getChanges', $cmd);
$xml = simplexml_load_string($out); $xml = simplexml_load_string($out);
if (count($xml) == 0) { if (count($xml) == 0) {
@@ -570,20 +517,15 @@ class IDF_Scm_Svn extends IDF_Scm
* *
* @return array Changes. * @return array Changes.
*/ */
public function getChangeLog($branch=null, $n=10) public function getChangeLog($rev=null, $n=10)
{ {
if ($branch != 'HEAD' and !preg_match('/^\d+$/', $branch)) { if ($rev != 'HEAD' and !preg_match('/^\d+$/', $rev)) {
// we accept only revisions or HEAD // we accept only revisions or HEAD
$branch = 'HEAD'; $rev = 'HEAD';
} }
$res = array(); $res = array();
$cmd = sprintf(Pluf::f('svn_path', 'svn').' log --no-auth-cache --xml -v --limit %s --username=%s --password=%s --revision=%s %s', $cmd = $this->svnCmd(array('log', '--xml', '-v', '--limit', $n),
escapeshellarg($n), $this->repo.'@'.$rev);
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($branch),
escapeshellarg($this->repo));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xmlRes = self::shell_exec('IDF_Scm_Svn::getChangeLog', $cmd); $xmlRes = self::shell_exec('IDF_Scm_Svn::getChangeLog', $cmd);
$xml = simplexml_load_string($xmlRes); $xml = simplexml_load_string($xmlRes);
foreach ($xml->logentry as $entry) { foreach ($xml->logentry as $entry) {
@@ -609,12 +551,8 @@ class IDF_Scm_Svn extends IDF_Scm
public function getProperties($rev, $path='') public function getProperties($rev, $path='')
{ {
$res = array(); $res = array();
$cmd = sprintf(Pluf::f('svn_path', 'svn').' proplist --no-auth-cache --xml --username=%s --password=%s --revision=%s %s', $cmd = $this->svnCmd(array('proplist', '--xml'),
escapeshellarg($this->username), $this->repo.'/'.self::smartEncode($path), $rev);
escapeshellarg($this->password),
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); $xmlProps = self::shell_exec('IDF_Scm_Svn::getProperties', $cmd);
$props = simplexml_load_string($xmlProps); $props = simplexml_load_string($xmlProps);
@@ -643,13 +581,8 @@ class IDF_Scm_Svn extends IDF_Scm
private function getProperty($property, $rev, $path='') private function getProperty($property, $rev, $path='')
{ {
$res = array(); $res = array();
$cmd = sprintf(Pluf::f('svn_path', 'svn').' propget --no-auth-cache --xml %s --username=%s --password=%s --revision=%s %s', $cmd = $this->svnCmd(array('propget', $property, '--xml'),
escapeshellarg($property), $this->repo.'/'.self::smartEncode($path), $rev);
escapeshellarg($this->username),
escapeshellarg($this->password),
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); $xmlProp = self::shell_exec('IDF_Scm_Svn::getProperty', $cmd);
$prop = simplexml_load_string($xmlProp); $prop = simplexml_load_string($xmlProp);
@@ -666,16 +599,38 @@ class IDF_Scm_Svn extends IDF_Scm
public function getLastCommit($rev='HEAD') public function getLastCommit($rev='HEAD')
{ {
$xmlInfo = ''; $xmlInfo = '';
$cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --xml --username=%s --password=%s --revision=%s %s', $cmd = $this->svnCmd(array('info', '--xml'), $this->repo, $rev);
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($rev),
escapeshellarg($this->repo));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xmlInfo = self::shell_exec('IDF_Scm_Svn::getLastCommit', $cmd); $xmlInfo = self::shell_exec('IDF_Scm_Svn::getLastCommit', $cmd);
$xml = simplexml_load_string($xmlInfo); $xml = simplexml_load_string($xmlInfo);
return (string) $xml->entry->commit['revision']; return (string) $xml->entry->commit['revision'];
} }
private function svnCmd($args = array(), $repoarg = null, $revarg = null)
{
$cmdline = array();
$cmdline[] = Pluf::f('idf_exec_cmd_prefix', '');
$cmdline[] = Pluf::f('svn_path', 'svn');
$cmdline[] = '--no-auth-cache';
$cmdline[] = '--username='.escapeshellarg($this->username);
$cmdline[] = '--password='.escapeshellarg($this->password);
foreach ($args as $arg) {
$cmdline[] = escapeshellarg($arg);
}
if ($repoarg != null) {
if ($revarg != null) {
$repoarg .= '@'.$revarg;
}
$cmdline[] = escapeshellarg($repoarg);
}
if ($revarg != null) {
$cmdline[] = '--revision='.escapeshellarg($revarg);
}
return implode(' ', $cmdline);
}
} }

View File

@@ -58,7 +58,7 @@ class IDF_Template_IssueComment extends Pluf_Template_Tag
implode('|', $nouns); implode('|', $nouns);
$text = IDF_Template_safePregReplace('#((?:'.$prefix.')(?:\s+r?))([0-9a-f]{1,40}((?:\s+and|\s+or|,)\s+r?[0-9a-f]{1,40})*)\b#i', $text = IDF_Template_safePregReplace('#((?:'.$prefix.')(?:\s+r?))([0-9a-f]{1,40}((?:\s+and|\s+or|,)\s+r?[0-9a-f]{1,40})*)\b#i',
array($this, 'callbackCommits'), $text); array($this, 'callbackCommits'), $text);
$text = IDF_Template_safePregReplace('=(src:)([^\s@#,\(\)\\\\]+(?:(\\\\)[\s@#][^\s@#,\(\)\\\\]+){0,})+(?:\@([^\s#,]+))(?:#(\d+))?=im', $text = IDF_Template_safePregReplace('=(src:)([^\s@#,\(\)\\\\]+(?:(\\\\)[\s@#][^\s@#,\(\)\\\\]+){0,})+(?:\@([^\s#,]+))?(?:#(\d+))?=im',
array($this, 'callbackSource'), $text); array($this, 'callbackSource'), $text);
} }
if ($wordwrap) $text = Pluf_Text::wrapHtml($text, 69, "\n"); if ($wordwrap) $text = Pluf_Text::wrapHtml($text, 69, "\n");

View File

@@ -0,0 +1,35 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2011 Céondo Ltd and contributors.
#
# InDefero 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.
#
# InDefero 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 St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
class IDF_Template_Tag_UploadUrl extends Pluf_Template_Tag
{
function start($file='')
{
echo IDF_Template_Tag_UploadUrl::url($file);
}
public static function url($file='')
{
return Pluf::f('url_upload', Pluf_Template_Tag_MediaUrl::url() . '/upload') . $file;
}
}

View File

@@ -150,7 +150,7 @@ class IDF_Upload extends Pluf_Model
if ($this->id == '') { if ($this->id == '') {
$this->creation_dtime = gmdate('Y-m-d H:i:s'); $this->creation_dtime = gmdate('Y-m-d H:i:s');
$this->modif_dtime = gmdate('Y-m-d H:i:s'); $this->modif_dtime = gmdate('Y-m-d H:i:s');
$this->md5 = md5_file (Pluf::f('upload_path') . '/' . $this->get_project()->shortname . '/files/' . $this->file); $this->md5 = md5_file ($this->getFullPath());
} }
} }
@@ -167,6 +167,11 @@ class IDF_Upload extends Pluf_Model
return Pluf::f('url_upload').'/'.$project->shortname.'/files/'.$this->file; return Pluf::f('url_upload').'/'.$project->shortname.'/files/'.$this->file;
} }
function getFullPath()
{
return(Pluf::f('upload_path').'/'.$this->get_project()->shortname.'/files/'.$this->file);
}
/** /**
* We drop the information from the timeline. * We drop the information from the timeline.
*/ */
@@ -256,4 +261,4 @@ class IDF_Upload extends Pluf_Model
} }
Pluf_Translation::loadSetLocale($current_locale); Pluf_Translation::loadSetLocale($current_locale);
} }
} }

View File

@@ -202,7 +202,11 @@ class IDF_Views_Download
$prj->inOr404($upload); $prj->inOr404($upload);
$upload->downloads += 1; $upload->downloads += 1;
$upload->update(); $upload->update();
return new Pluf_HTTP_Response_Redirect($upload->getAbsoluteUrl($prj)); $path = $upload->getFullPath();
$mime = IDF_FileUtil::getMimeType($path);
$render = new Pluf_HTTP_Response_File($path, $mime[0]);
$render->headers["Content-MD5"] = $upload->md5;
return $render;
} }
/** /**

View File

@@ -109,11 +109,13 @@ class IDF_Views_Issue
foreach ($owners as $user => $nb) { foreach ($owners as $user => $nb) {
if ($user === '') { if ($user === '') {
$key = __('Not assigned'); $key = __('Not assigned');
$login = null;
} else { } else {
$obj = Pluf::factory('Pluf_User')->getOne(array('filter'=>'id='.$user)); $obj = Pluf::factory('Pluf_User')->getOne(array('filter'=>'id='.$user));
$key = $obj->first_name . ' ' . $obj->last_name; $key = $obj->first_name . ' ' . $obj->last_name;
$login = $obj->login;
} }
$ownerStatistics[$key] = array($nb, (int)(100 * $nb / $opened)); $ownerStatistics[$key] = array($nb, (int)(100 * $nb / $opened), $login);
} }
// Issue class tag statistics // Issue class tag statistics
@@ -313,42 +315,55 @@ class IDF_Views_Issue
* *
* Only open issues are shown. * Only open issues are shown.
*/ */
public $myIssues_precond = array('IDF_Precondition::accessIssues', public $userIssues_precond = array('IDF_Precondition::accessIssues');
'Pluf_Precondition::loginRequired'); public function userIssues($request, $match)
public function myIssues($request, $match)
{ {
$prj = $request->project; $prj = $request->project;
$sql = new Pluf_SQL('login=%s', array($match[2]));
$user = Pluf::factory('Pluf_User')->getOne(array('filter' => $sql->gen()));
if ($user === null) {
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::index',
array($prj->shortname));
return new Pluf_HTTP_Response_Redirect($url);
}
$otags = $prj->getTagIdsByStatus('open'); $otags = $prj->getTagIdsByStatus('open');
$ctags = $prj->getTagIdsByStatus('closed'); $ctags = $prj->getTagIdsByStatus('closed');
if (count($otags) == 0) $otags[] = 0; if (count($otags) == 0) $otags[] = 0;
if (count($ctags) == 0) $ctags[] = 0; if (count($ctags) == 0) $ctags[] = 0;
switch ($match[2]) { switch ($match[3]) {
case 'submit': case 'submit':
$title = sprintf(__('My Submitted %s Issues'), (string) $prj); $titleFormat = __('%s %s Submitted %s Issues');
$f_sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $request->user->id)); $f_sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $user->id));
break; break;
case 'submitclosed': case 'submitclosed':
$title = sprintf(__('My Closed Submitted %s Issues'), (string) $prj); $titleFormat = __('%s %s Closed Submitted %s Issues');
$f_sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $request->user->id)); $f_sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $user->id));
break; break;
case 'ownerclosed': case 'ownerclosed':
$title = sprintf(__('My Closed Working %s Issues'), (string) $prj); $titleFormat = __('%s %s Closed Working %s Issues');
$f_sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $request->user->id)); $f_sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $user->id));
break; break;
default: default:
$title = sprintf(__('My Working %s Issues'), (string) $prj); $titleFormat = __('%s %s Working %s Issues');
$f_sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $request->user->id)); $f_sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $user->id));
break; break;
} }
$title = sprintf($titleFormat,
$user->first_name,
$user->last_name,
(string) $prj);
// Get stats about the issues // Get stats about the issues
$sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $request->user->id)); $sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $user->id));
$nb_submit = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen())); $nb_submit = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen()));
$sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $request->user->id)); $sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $user->id));
$nb_owner = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen())); $nb_owner = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen()));
// Closed issues // Closed issues
$sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $request->user->id)); $sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $user->id));
$nb_submit_closed = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen())); $nb_submit_closed = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen()));
$sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $request->user->id)); $sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $user->id));
$nb_owner_closed = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen())); $nb_owner_closed = Pluf::factory('IDF_Issue')->getCount(array('filter'=>$sql->gen()));
// Paginator to paginate the issues // Paginator to paginate the issues
@@ -359,7 +374,7 @@ class IDF_Views_Issue
'current_user' => $request->user); 'current_user' => $request->user);
$pag->summary = __('This table shows the open issues.'); $pag->summary = __('This table shows the open issues.');
$pag->forced_where = $f_sql; $pag->forced_where = $f_sql;
$pag->action = array('IDF_Views_Issue::myIssues', array($prj->shortname, $match[2])); $pag->action = array('IDF_Views_Issue::userIssues', array($prj->shortname, $match[2]));
$pag->sort_order = array('modif_dtime', 'ASC'); // will be reverted $pag->sort_order = array('modif_dtime', 'ASC'); // will be reverted
$pag->sort_reverse_order = array('modif_dtime'); $pag->sort_reverse_order = array('modif_dtime');
$pag->sort_link_title = true; $pag->sort_link_title = true;
@@ -374,9 +389,10 @@ class IDF_Views_Issue
$pag->items_per_page = 10; $pag->items_per_page = 10;
$pag->no_results_text = __('No issues were found.'); $pag->no_results_text = __('No issues were found.');
$pag->setFromRequest($request); $pag->setFromRequest($request);
return Pluf_Shortcuts_RenderToResponse('idf/issues/my-issues.html', return Pluf_Shortcuts_RenderToResponse('idf/issues/userIssues.html',
array('project' => $prj, array('project' => $prj,
'page_title' => $title, 'page_title' => $title,
'login' => $user->login,
'nb_submit' => $nb_submit, 'nb_submit' => $nb_submit,
'nb_owner' => $nb_owner, 'nb_owner' => $nb_owner,
'nb_submit_closed' => $nb_submit_closed, 'nb_submit_closed' => $nb_submit_closed,
@@ -747,7 +763,13 @@ class IDF_Views_Issue
else { else {
// ID-based search // ID-based search
if (is_numeric($query)) { if (is_numeric($query)) {
$sql = new Pluf_SQL('project=%s AND id LIKE %s', array($prj->id, $query.'%')); $sql = 'project=%s AND CAST(id AS VARCHAR) LIKE %s';
// MySQL can't cast to VARCHAR and a CAST to CHAR converts
// the whole number, not just the first digit
if (strtolower(Pluf::f('db_engine')) == 'mysql') {
$sql = 'project=%s AND CAST(id AS CHAR) LIKE %s';
}
$sql = new Pluf_SQL($sql, array($prj->id, $query.'%'));
$tmp = Pluf::factory('IDF_Issue')->getList(array( $tmp = Pluf::factory('IDF_Issue')->getList(array(
'filter' => $sql->gen(), 'filter' => $sql->gen(),
'order' => 'id ASC' 'order' => 'id ASC'

View File

@@ -153,10 +153,10 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/create/$#',
'model' => 'IDF_Views_Issue', 'model' => 'IDF_Views_Issue',
'method' => 'create'); 'method' => 'create');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/my/(\w+)/$#', $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/(.*)/(\w+)/$#',
'base' => $base, 'base' => $base,
'model' => 'IDF_Views_Issue', 'model' => 'IDF_Views_Issue',
'method' => 'myIssues'); 'method' => 'userIssues');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/attachment/(\d+)/(.*)$#', $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/attachment/(\d+)/(.*)$#',
'base' => $base, 'base' => $base,

View File

@@ -4,7 +4,7 @@
<div id="sub-tabs"> <div id="sub-tabs">
<a {if $inSummaryIssues}class="active" {/if}href="{url 'IDF_Views_Issue::summary', array($project.shortname)}">{trans 'Summary'}</a> <a {if $inSummaryIssues}class="active" {/if}href="{url 'IDF_Views_Issue::summary', array($project.shortname)}">{trans 'Summary'}</a>
| <a {if $inOpenIssues}class="active" {/if}href="{url 'IDF_Views_Issue::index', array($project.shortname)}">{trans 'Open Issues'}</a> | <a {if $inOpenIssues}class="active" {/if}href="{url 'IDF_Views_Issue::index', array($project.shortname)}">{trans 'Open Issues'}</a>
{if !$user.isAnonymous()} | <a {if $inCreate}class="active" {/if}href="{url 'IDF_Views_Issue::create', array($project.shortname)}">{trans 'New Issue'}</a> | <a {if $inMyIssues}class="active" {/if}href="{url 'IDF_Views_Issue::myIssues', array($project.shortname, 'submit')}">{trans 'My Issues'}</a> {if !$user.isAnonymous()} | <a {if $inCreate}class="active" {/if}href="{url 'IDF_Views_Issue::create', array($project.shortname)}">{trans 'New Issue'}</a> | <a {if $inMyIssues}class="active" {/if}href="{url 'IDF_Views_Issue::userIssues', array($project.shortname, $user.login, 'submit')}">{trans 'My Issues'}</a>
| <a {if $inWatchList}class="active" {/if}href="{url 'IDF_Views_Issue::watchList', array($project.shortname, 'open')}">{trans 'My watch list'}</a>{/if} | | <a {if $inWatchList}class="active" {/if}href="{url 'IDF_Views_Issue::watchList', array($project.shortname, 'open')}">{trans 'My watch list'}</a>{/if} |
<form class="star" action="{url 'IDF_Views_Issue::search', array($project.shortname)}" method="get"> <form class="star" action="{url 'IDF_Views_Issue::search', array($project.shortname)}" method="get">
<input accesskey="4" type="text" value="{$q}" name="q" size="20" /> <input accesskey="4" type="text" value="{$q}" name="q" size="20" />

View File

@@ -10,12 +10,22 @@
{if $attachments.count() > 0} {if $attachments.count() > 0}
<hr align="left" class="attach" /> <hr align="left" class="attach" />
<ul> <ul>
{foreach $attachments as $a}<li><a href="{url 'IDF_Views_Issue::viewAttachment', array($project.shortname, $a.id, $a.filename)}">{$a.filename}</a> - {$a.filesize|ssize}</li>{/foreach} {foreach $attachments as $a}<li><a href="{url 'IDF_Views_Issue::viewAttachment', array($project.shortname, $a.id, $a.filename)}">{$a.filename}</a> - {$a.filesize|ssize}</li>{/foreach}
</ul>{/if} </ul>{/if}
{if $c.changes} {if $c.changes}
{foreach $c.changes as $w => $v} {foreach $c.changes as $w => $v}
<strong>{if $w == 'su'}{trans 'Summary:'}{/if}{if $w == 'st'}{trans 'Status:'}{/if}{if $w == 'ow'}{trans 'Owner:'}{/if}{if $w == 'lb'}{trans 'Labels:'}{/if}</strong> {if $w == 'lb'}{assign $l = implode(', ', $v)}{$l}{else}{$v}{/if}<br /> <strong>{if $w == 'su'}{trans 'Summary:'}{/if}{if $w == 'st'}{trans 'Status:'}{/if}{if $w == 'ow'}{trans 'Owner:'}{/if}{if $w == 'lb'}{trans 'Labels:'}{/if}{if $w == 'rel'}{trans 'Relations:'}{/if}</strong>
{if $w == 'lb' or $w == 'rel'}
{foreach $v as $t => $ls}
{foreach $ls as $l}
{if $t == 'rem'}<s>{/if}{$l}{if $t == 'rem'}</s>{/if}
{/foreach}
{/foreach}
{else}
{$v}
{/if}<br />
{/foreach} {/foreach}
{/if} {/if}
</div></content> </div></content>
</entry> </entry>

View File

@@ -16,7 +16,7 @@
{if strlen($c.content) > 0}{$c.content|safe}{/if}{if $c.changedIssue()} {if strlen($c.content) > 0}{$c.content|safe}{/if}{if $c.changedIssue()}
{foreach $c.changes as $w => $v} {foreach $c.changes as $w => $v}
{if $w == 'su'}{trans 'Summary:'}{/if}{if $w == 'st'}{trans 'Status:'}{/if}{if $w == 'ow'}{trans 'Owner:'}{/if}{if $w == 'lb'}{trans 'Labels:'}{/if} {if $w == 'lb'}{assign $l = implode(', ', $v)}{$l}{else}{$v}{/if}{/foreach}{/if}{assign $attachments = $c.get_attachment_list()}{if $attachments.count() > 0} {if $w == 'su'}{trans 'Summary:'}{/if}{if $w == 'st'}{trans 'Status:'}{/if}{if $w == 'ow'}{trans 'Owner:'}{/if}{if $w == 'lb'}{trans 'Labels:'}{/if}{if $w == 'rel'}{trans 'Relations:'}{/if} {if $w == 'lb' or $w == 'rel'}{foreach $v as $t => $ls}{foreach $ls as $l}{if $t == 'rem'}-{/if}{$l} {/foreach}{/foreach}{else}{$v}{/if}{/foreach}{/if}{assign $attachments = $c.get_attachment_list()}{if $attachments.count() > 0}
{trans 'Attachments:'}{foreach $attachments as $a} {trans 'Attachments:'}{foreach $attachments as $a}
- {$a.filename|safe} - {$a.filesize|ssize} - {$a.filename|safe} - {$a.filesize|ssize}

View File

@@ -71,7 +71,12 @@
<tbody> <tbody>
{foreach $ownerStatistics as $key => $value} {foreach $ownerStatistics as $key => $value}
<tr> <tr>
<td class="name">{$key}</td> <td class="name">
{if !empty($value[2])}
{aurl 'url', 'IDF_Views_Issue::userIssues', array($project.shortname, $value[2], 'owner')}
<a href="{$url}">{$key}</a>
{else}{$key}{/if}
</td>
<td class="count">{$value[0]}</td> <td class="count">{$value[0]}</td>
<td class="graph"> <td class="graph">
<table class='graph'> <table class='graph'>

View File

@@ -1,5 +1,5 @@
{extends "idf/issues/base.html"} {extends "idf/issues/base.html"}
{block docclass}yui-t2{assign $inMyIssues = true}{/block} {block docclass}yui-t2{if $user.login == $login}{assign $inMyIssues = true}{/if}{/block}
{block body} {block body}
{$issues.render} {$issues.render}
{if !$user.isAnonymous()} {if !$user.isAnonymous()}
@@ -8,10 +8,10 @@
{/block} {/block}
{block context} {block context}
{aurl 'owner_url', 'IDF_Views_Issue::myIssues', array($project.shortname, 'owner')} {aurl 'owner_url', 'IDF_Views_Issue::userIssues', array($project.shortname, $login, 'owner')}
{aurl 'submit_url', 'IDF_Views_Issue::myIssues', array($project.shortname, 'submit')} {aurl 'submit_url', 'IDF_Views_Issue::userIssues', array($project.shortname, $login, 'submit')}
{aurl 'owner_closed_url', 'IDF_Views_Issue::myIssues', array($project.shortname, 'ownerclosed')} {aurl 'owner_closed_url', 'IDF_Views_Issue::userIssues', array($project.shortname, $login, 'ownerclosed')}
{aurl 'submit_closed_url', 'IDF_Views_Issue::myIssues', array($project.shortname, 'submitclosed')} {aurl 'submit_closed_url', 'IDF_Views_Issue::userIssues', array($project.shortname, $login, 'submitclosed')}
<p><strong>{trans 'Submitted issues:'}</strong> <a href="{$submit_url}">{$nb_submit}</a> <p><strong>{trans 'Submitted issues:'}</strong> <a href="{$submit_url}">{$nb_submit}</a>
{if $nb_submit_closed}<br /><span class="helptext">{blocktrans $nb_submit_closed}See the <a href="{$submit_closed_url}">{$nb_submit_closed} closed</a>.{plural}See the <a href="{$submit_closed_url}">{$nb_submit_closed} closed</a>.{/blocktrans}</span>{/if}</p> {if $nb_submit_closed}<br /><span class="helptext">{blocktrans $nb_submit_closed}See the <a href="{$submit_closed_url}">{$nb_submit_closed} closed</a>.{plural}See the <a href="{$submit_closed_url}">{$nb_submit_closed} closed</a>.{/blocktrans}</span>{/if}</p>
{if $nb_owner > 0} {if $nb_owner > 0}

View File

@@ -17,7 +17,7 @@
{assign $submitter_data = $c.get_submitter_data()} {assign $submitter_data = $c.get_submitter_data()}
<div class="issue-comment{if $i == 0} issue-comment-first{/if}{if $i == ($nc-1)} issue-comment-last{/if}" id="ic{$c.id}"> <div class="issue-comment{if $i == 0} issue-comment-first{/if}{if $i == ($nc-1)} issue-comment-last{/if}" id="ic{$c.id}">
{if $submitter_data.avatar != ''} {if $submitter_data.avatar != ''}
<img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="{media}/upload/avatars/{$submitter_data.avatar}" alt=" " /> <img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="{upload}/avatars/{$submitter_data.avatar}" alt=" " />
{else} {else}
<img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="http://www.gravatar.com/avatar/{$submitter.email|md5}.jpg?s=60&amp;d={media}/idf/img/spacer.gif" alt=" " /> <img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="http://www.gravatar.com/avatar/{$submitter.email|md5}.jpg?s=60&amp;d={media}/idf/img/spacer.gif" alt=" " />
{/if} {/if}

View File

@@ -10,8 +10,8 @@
{if $hasWikiAccess}{hotkey 'Shift+o', 'IDF_Views_Wiki::index', array($project.shortname)}{/if} {if $hasWikiAccess}{hotkey 'Shift+o', 'IDF_Views_Wiki::index', array($project.shortname)}{/if}
{if $hasSourceAccess}{hotkey 'Shift+s', 'IDF_Views_Source::treeBase', array($project.shortname, $project.getScmRoot())}{/if} {if $hasSourceAccess}{hotkey 'Shift+s', 'IDF_Views_Source::treeBase', array($project.shortname, $project.getScmRoot())}{/if}
{if $hasIssuesAccess and !$user.isAnonymous()} {if $hasIssuesAccess and !$user.isAnonymous()}
{hotkey 'Shift+m', 'IDF_Views_Issue::myIssues', array($project.shortname, 'submit')} {hotkey 'Shift+m', 'IDF_Views_Issue::userIssues', array($project.shortname, $user.login, 'submit')}
{hotkey 'Shift+w', 'IDF_Views_Issue::myIssues', array($project.shortname, 'owner')} {hotkey 'Shift+w', 'IDF_Views_Issue::userIssues', array($project.shortname, $user.login, 'owner')}
{/if}{/if} //--> {/if}{/if} //-->
</script> </script>

View File

@@ -4,7 +4,7 @@
<div id="sub-tabs"> <div id="sub-tabs">
<a {if $inOpenReviews}class="active" {/if}href="{url 'IDF_Views_Review::index', array($project.shortname)}">{trans 'Open Reviews'}</a> {* <a {if $inOpenReviews}class="active" {/if}href="{url 'IDF_Views_Review::index', array($project.shortname)}">{trans 'Open Reviews'}</a> {*
{if !$user.isAnonymous()} | <a {if $inCreate}class="active" {/if}href="{url 'IDF_Views_Issue::create', array($project.shortname)}">{trans 'New Issue'}</a> | <a {if $inMyIssues}class="active" {/if}href="{url 'IDF_Views_Issue::myIssues', array($project.shortname, 'submit')}">{trans 'My Issues'}</a>{/if} | {if !$user.isAnonymous()} | <a {if $inCreate}class="active" {/if}href="{url 'IDF_Views_Issue::create', array($project.shortname)}">{trans 'New Issue'}</a> | <a {if $inMyIssues}class="active" {/if}href="{url 'IDF_Views_Issue::userIssues', array($project.shortname, $user.login, 'submit')}">{trans 'My Issues'}</a>{/if} |
<form class="star" action="{url 'IDF_Views_Issue::search', array($project.shortname)}" method="get"> <form class="star" action="{url 'IDF_Views_Issue::search', array($project.shortname)}" method="get">
<input accesskey="4" type="text" value="{$q}" name="q" size="20" /> <input accesskey="4" type="text" value="{$q}" name="q" size="20" />
<input type="submit" name="s" value="{trans 'Search'}" /> <input type="submit" name="s" value="{trans 'Search'}" />

View File

@@ -109,7 +109,7 @@ to propose more contributions</strong>.
{foreach $comments as $c}{ashowuser 'submitter', $c.get_submitter(), $request}{assign $submitter = $c.get_submitter()}{assign $submitter_data = $c.get_submitter_data()} {foreach $comments as $c}{ashowuser 'submitter', $c.get_submitter(), $request}{assign $submitter = $c.get_submitter()}{assign $submitter_data = $c.get_submitter_data()}
<div class="issue-comment{if $i == 1} issue-comment-first{/if}{if $i == ($nc)} issue-comment-last{/if}" id="ic{$c.id}"> <div class="issue-comment{if $i == 1} issue-comment-first{/if}{if $i == ($nc)} issue-comment-last{/if}" id="ic{$c.id}">
{if $submitter_data.avatar != ''} {if $submitter_data.avatar != ''}
<img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="{media}/upload/avatars/{$submitter_data.avatar}" alt=" " /> <img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="{upload}/avatars/{$submitter_data.avatar}" alt=" " />
{else} {else}
<img style="float:right; position: relative;" src="http://www.gravatar.com/avatar/{$submitter.email|md5}.jpg?s=60&amp;d={media}/idf/img/spacer.gif" alt=" " /> <img style="float:right; position: relative;" src="http://www.gravatar.com/avatar/{$submitter.email|md5}.jpg?s=60&amp;d={media}/idf/img/spacer.gif" alt=" " />
{/if} {/if}

View File

@@ -3,7 +3,7 @@
<table class="form" summary=""> <table class="form" summary="">
<tr> <tr>
<th style="text-align: right">{if $user_data.avatar != ''} <th style="text-align: right">{if $user_data.avatar != ''}
<img style="max-height: 60px; max-width: 60px;" src="{media}/upload/avatars/{$user_data.avatar}" alt=" " /> <img style="max-height: 60px; max-width: 60px;" src="{upload}/avatars/{$user_data.avatar}" alt=" " />
{else} {else}
<img src="http://www.gravatar.com/avatar/{$member.email|md5}.jpg?s=60&amp;d={media}/idf/img/spacer.gif" alt=" " /> <img src="http://www.gravatar.com/avatar/{$member.email|md5}.jpg?s=60&amp;d={media}/idf/img/spacer.gif" alt=" " />
{/if} {/if}

View File

@@ -625,6 +625,7 @@ END;
'additions' => array('new_dir', 'new_dir/new_file'), 'additions' => array('new_dir', 'new_dir/new_file'),
'deletions' => array('old_dir', 'old_dir/old_file'), 'deletions' => array('old_dir', 'old_dir/old_file'),
'renames' => array('dir_with_old_name' => 'new_dir/dir_with_new_name'), 'renames' => array('dir_with_old_name' => 'new_dir/dir_with_new_name'),
'copies' => array(), // this is always empty
'patches' => array('existing_file'), 'patches' => array('existing_file'),
'properties' => array( 'properties' => array(
'new_dir/dir_with_new_name' => array( 'new_dir/dir_with_new_name' => array(

55
test/IDF/Scm/SvnTest.php Normal file
View File

@@ -0,0 +1,55 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2011 Céondo Ltd and contributors.
#
# InDefero 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.
#
# InDefero 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 St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
class IDF_Scm_SvnTest extends PHPUnit_Framework_TestCase
{
private $proj = null;
public function setUp()
{
$this->proj = new IDF_Project();
$this->proj->id = 1;
$this->proj->name = $this->proj->shortname = 'test';
$this->proj->create();
}
public function tearDown()
{
$this->proj->delete();
}
public function createMock($reponame)
{
$repourl = 'file://'.DATADIR.'/'.__CLASS__.'/'.$reponame;
$instance = new IDF_Scm_Svn($repourl, $this->proj);
return $instance;
}
public function testAccessHistoryOfRenamedAndDeletedFiles()
{
$instance = $this->createMock(__FUNCTION__);
$this->assertEquals('new-file', $instance->getPathInfo('new-file', 1)->fullpath);
$this->assertEquals('alternate-name', $instance->getPathInfo('alternate-name', 2)->fullpath);
}
}

View File

@@ -0,0 +1,2 @@
4
layout sharded 1000

View File

@@ -0,0 +1,5 @@
K 8
svn:date
V 27
2011-08-12T17:06:15.429110Z
END

View File

@@ -0,0 +1,13 @@
K 10
svn:author
V 7
patrick
K 8
svn:date
V 27
2011-08-12T17:07:33.111035Z
K 7
svn:log
V 12
added a file
END

View File

@@ -0,0 +1,13 @@
K 10
svn:author
V 7
patrick
K 8
svn:date
V 27
2011-08-12T17:07:55.894416Z
K 7
svn:log
V 14
renamed a file
END

View File

@@ -0,0 +1,13 @@
K 10
svn:author
V 7
patrick
K 8
svn:date
V 27
2011-08-12T17:08:12.646051Z
K 7
svn:log
V 14
deleted a file
END

View File

@@ -0,0 +1,11 @@
PLAIN
END
ENDREP
id: 0.0.r0/17
type: dir
count: 0
text: 0 0 4 4 2d2977d1c96f487abe4a1e202dd03b4e
cpath: /
17 107

View File

@@ -0,0 +1,28 @@
DELTA
SVNENDREP
id: 0-1.0.r1/17
type: file
count: 0
text: 1 0 4 0 d41d8cd98f00b204e9800998ecf8427e da39a3ee5e6b4b0d3255bfef95601890afd80709 0-0/_2
cpath: /new-file
copyroot: 0 /
PLAIN
K 8
new-file
V 16
file 0-1.0.r1/17
END
ENDREP
id: 0.0.r1/232
type: dir
pred: 0.0.r0/17
count: 1
text: 1 180 39 39 4a0c4e617d69f8a0bf11161d1e1b09a6
cpath: /
copyroot: 0 /
_0.0.t0-0 add-file true false /new-file
232 357

View File

@@ -0,0 +1,29 @@
id: 0-1.0-2.r2/0
type: file
pred: 0-1.0.r1/17
count: 1
text: 1 0 4 0 d41d8cd98f00b204e9800998ecf8427e da39a3ee5e6b4b0d3255bfef95601890afd80709 0-0/_2
cpath: /alternate-name
copyfrom: 1 /new-file
PLAIN
K 14
alternate-name
V 17
file 0-1.0-2.r2/0
END
ENDREP
id: 0.0.r2/256
type: dir
pred: 0.0.r1/232
count: 2
text: 2 196 47 47 518568bcb7e6fe3165235a9f9993f5e1
cpath: /
copyroot: 0 /
0-1.0.r1/17 delete-file false false /new-file
0-1._0.t1-1 add-file false false /alternate-name
1 /new-file
256 382

View File

@@ -0,0 +1,15 @@
PLAIN
END
ENDREP
id: 0.0.r3/17
type: dir
pred: 0.0.r2/256
count: 3
text: 3 0 4 4 2d2977d1c96f487abe4a1e202dd03b4e
cpath: /
copyroot: 0 /
0-1.0-2.r2/0 delete-file false false /alternate-name
17 138

View File

@@ -0,0 +1 @@
5d5eaf70-0fe3-4d07-ada0-45c336a5bfad