Compare commits
29 Commits
feature.is
...
feature.we
Author | SHA1 | Date | |
---|---|---|---|
|
cdb8dbafe2 | ||
|
33b22f95ab | ||
|
39f77886db | ||
|
f7470e4a7a | ||
|
c10c002ee3 | ||
|
a47ec0df0a | ||
|
be95050a4b | ||
|
5043c4e845 | ||
|
dacbf0707b | ||
|
34c9d04a35 | ||
|
aa2868eb17 | ||
|
a2c832a130 | ||
|
b17de014ec | ||
|
b1b72190e1 | ||
|
2ff2f888bc | ||
|
57c2389aae | ||
|
d54c86f813 | ||
|
05a9697007 | ||
|
945429abf0 | ||
|
a016bcb51b | ||
|
f2b1ce795c | ||
|
3a8c56acc4 | ||
|
7b2552f940 | ||
|
324b202215 | ||
|
2c2da6082a | ||
|
dd3fbbd7e4 | ||
|
9bbcd571ec | ||
|
d1bcdcda20 | ||
|
4879d64989 |
@@ -13,10 +13,11 @@ or newer to properly run this version of Indefero!
|
|||||||
- Display monotone file and directory attributes in the tree and file view
|
- Display monotone file and directory attributes in the tree and file view
|
||||||
(needs a monotone with an interface version of 13.1 or newer)
|
(needs a monotone with an interface version of 13.1 or newer)
|
||||||
- The context area is now kept in view when a page scrolls down several pages
|
- The context area is now kept in view when a page scrolls down several pages
|
||||||
|
- git repositories are now available under /r/projectname/
|
||||||
|
|
||||||
## 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 +30,16 @@ 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)
|
||||||
- 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
|
||||||
|
|
||||||
|
57
doc/httprepos-git.mdtext
Normal file
57
doc/httprepos-git.mdtext
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# Accessing git repositories over HTTP
|
||||||
|
|
||||||
|
Starting with indefero 1.2, git repositories are provided via http,
|
||||||
|
featuring read-only and read-write access with full integration with
|
||||||
|
indefero's access control mechanisms.
|
||||||
|
|
||||||
|
## Access git repositories
|
||||||
|
|
||||||
|
The repositories are available under http://YOURHOST/BASEPATH/r/PROJECT.
|
||||||
|
|
||||||
|
For authentication, use the "extra password" which you can find on your
|
||||||
|
profile page.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
The main thing to setup is git_repositories and git_remote_url in
|
||||||
|
src/IDF/conf/idf.php
|
||||||
|
|
||||||
|
git_remote_url should match http://YOURHOST/BASEPATH/r/%s, while
|
||||||
|
git_repositories points to the local directory supposed to contain the
|
||||||
|
repositories like /PATH/TO/REPOSITORIES/%s.git.
|
||||||
|
|
||||||
|
## Setup requirements when using PHP over FastCGI
|
||||||
|
|
||||||
|
There are a couple of things to setup when using PHP over FastCGI, as compared
|
||||||
|
to mod_php or similar integrated mechanisms:
|
||||||
|
|
||||||
|
- You will need to setup a RewriteRule for mod_rewrite (in case of Apache,
|
||||||
|
analogous mechanisms might need to be setup for other http daemons), which
|
||||||
|
passes through the Authorization HTTP Header of a request.
|
||||||
|
|
||||||
|
In case of mod_rewrite, the necessary line is
|
||||||
|
one of (depending on server configuration):
|
||||||
|
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L]
|
||||||
|
RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization},L]
|
||||||
|
|
||||||
|
- The FastCGI adaptor must allow large requests to be handled by PHP,
|
||||||
|
otherwise push might fail.
|
||||||
|
|
||||||
|
For mod_fcgid, this is the FcgidMaxRequestLen option, or MaxRequestLen in
|
||||||
|
older versions. Set this option to some large enough value - there is no
|
||||||
|
issue with RAM use, as another option defines the limit after which the
|
||||||
|
request is backed on disk, which can remain small.
|
||||||
|
|
||||||
|
## When migrating from syncgit
|
||||||
|
|
||||||
|
HTTP access can be used in parallel to syncgit.
|
||||||
|
|
||||||
|
If you want to disable syncgit, just undo the changes detailled
|
||||||
|
in doc/syncgit.mdtext:
|
||||||
|
- In src/IDF/conf/idf.php keep git_repositories
|
||||||
|
- In src/IDF/conf/idf.php adapt git_remote_url to http://host/basepath/r/%s
|
||||||
|
- In src/IDF/conf/idf.php remove idf_plugin_syncgit*
|
||||||
|
- Remove the cronjob that called gitcron.php
|
||||||
|
- Disable the git daemon (eg. /etc/event.d/local-git-daemon)
|
||||||
|
- You can remove the git user and group, if you also adapt the git repositories
|
||||||
|
to be owned by the www user.
|
@@ -44,7 +44,7 @@ class IDF_Middleware
|
|||||||
function process_request(&$request)
|
function process_request(&$request)
|
||||||
{
|
{
|
||||||
$match = array();
|
$match = array();
|
||||||
if (preg_match('#^/(?:api/p|p)/([\-\w]+)/?#', $request->query, $match)) {
|
if (preg_match('#^/(?:api/p|p|r)/([\-\w]+)/?#', $request->query, $match)) {
|
||||||
try {
|
try {
|
||||||
$request->project = IDF_Project::getOr404($match[1]);
|
$request->project = IDF_Project::getOr404($match[1]);
|
||||||
} catch (Pluf_HTTP_Error404 $e) {
|
} catch (Pluf_HTTP_Error404 $e) {
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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();
|
||||||
|
@@ -497,5 +497,10 @@ class IDF_Scm
|
|||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function repository($request, $match)
|
||||||
|
{
|
||||||
|
throw new Exception('This repository does not support web based repository access');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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;
|
||||||
@@ -912,4 +924,113 @@ class IDF_Scm_Git extends IDF_Scm
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function repository($request, $match)
|
||||||
|
{
|
||||||
|
// handle a couple of workarounds for authenticating with FastCGI/PHP
|
||||||
|
if (!empty($_SERVER['HTTP_AUTHORIZATION']))
|
||||||
|
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':' , base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
|
||||||
|
elseif (!empty($_SERVER['REDIRECT_HTTP_AUTHORIZATION']))
|
||||||
|
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':' , base64_decode(substr($_SERVER['REDIRECT_HTTP_AUTHORIZATION'], 6)));
|
||||||
|
elseif (!empty($_SERVER['REDIRECT_REMOTE_USER']))
|
||||||
|
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) = explode(':' , base64_decode(substr($_SERVER['REDIRECT_REMOTE_USER'], 6)));
|
||||||
|
|
||||||
|
// authenticate: authenticate connection through "extra" password
|
||||||
|
if (!empty($_SERVER['PHP_AUTH_USER'])) {
|
||||||
|
$sql = new Pluf_SQL('login=%s', array($_SERVER['PHP_AUTH_USER']));
|
||||||
|
$users = Pluf::factory('Pluf_User')->getList(array('filter'=>$sql->gen()));
|
||||||
|
if (count($users) == 1 && $users[0]->active) {
|
||||||
|
$user = $users[0];
|
||||||
|
$realkey = substr(sha1($user->password.Pluf::f('secret_key')), 0, 8);
|
||||||
|
if ($_SERVER['PHP_AUTH_PW'] == $realkey) {
|
||||||
|
$request->user = $user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IDF_Precondition::accessSource($request) !== true) {
|
||||||
|
$response = new Pluf_HTTP_Response("");
|
||||||
|
$response->status_code = 401;
|
||||||
|
$response->headers['WWW-Authenticate']='Basic realm="git for '.$this->project.'"';
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = $match[2];
|
||||||
|
|
||||||
|
if (!file_exists($this->repo)) {
|
||||||
|
mkdir($this->repo, 0750, true);
|
||||||
|
$out = array();
|
||||||
|
$res = 0;
|
||||||
|
exec(sprintf(Pluf::f('idf_exec_cmd_prefix', '').
|
||||||
|
Pluf::f('git_path', 'git').' --git-dir=%s init', escapeshellarg($this->repo)),
|
||||||
|
$out, $res);
|
||||||
|
if ($res != 0) {
|
||||||
|
Pluf_Log::error(array('IDF_Scm_Git::repository', $res, $this->repo));
|
||||||
|
throw new Exception(sprintf('Init repository error, exit status %d.', $res));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update files before delivering them
|
||||||
|
if (($path == 'objects/info/pack') || ($path == 'info/refs')) {
|
||||||
|
$cmd = sprintf(Pluf::f('idf_exec_cmd_prefix', '').
|
||||||
|
'GIT_DIR=%s '.Pluf::f('git_path', 'git').' update-server-info -f',
|
||||||
|
escapeshellarg($this->repo));
|
||||||
|
self::shell_exec('IDF_Scm_Git::repository', $cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// smart HTTP discovery
|
||||||
|
if ($path == 'info/refs' && !empty($request->GET['service'])){
|
||||||
|
$service = $request->GET['service'];
|
||||||
|
switch ($service) {
|
||||||
|
case 'git-receive-pack':
|
||||||
|
if (IDF_Precondition::projectMemberOrOwner($request) !== true) {
|
||||||
|
$response = new Pluf_HTTP_Response("");
|
||||||
|
$response->status_code = 401;
|
||||||
|
$response->headers['WWW-Authenticate']='Basic realm="git for '.$this->project.'"';
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
case 'git-upload-pack':
|
||||||
|
$content = sprintf('%04x',strlen($service)+15).
|
||||||
|
'# service='.$service."\n0000";
|
||||||
|
$content .= self::shell_exec('IDF_Scm_Git::repository',
|
||||||
|
Pluf::f('idf_exec_cmd_prefix', '').
|
||||||
|
$service.' --stateless-rpc --advertise-refs '.
|
||||||
|
$this->repo);
|
||||||
|
$response = new Pluf_HTTP_Response($content,
|
||||||
|
'application/x-'.$service.'-advertisement');
|
||||||
|
return $response;
|
||||||
|
default:
|
||||||
|
throw new Exception('unknown service: '.$service);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch($path) {
|
||||||
|
// smart HTTP RPC
|
||||||
|
case 'git-receive-pack':
|
||||||
|
if (IDF_Precondition::projectMemberOrOwner($request) !== true) {
|
||||||
|
$response = new Pluf_HTTP_Response("");
|
||||||
|
$response->status_code = 401;
|
||||||
|
$response->headers['WWW-Authenticate']='Basic realm="git for '.$this->project.'"';
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
case 'git-upload-pack':
|
||||||
|
$response = new Pluf_HTTP_Response_CommandPassThru(
|
||||||
|
Pluf::f('idf_exec_cmd_prefix', '').$path.
|
||||||
|
' --stateless-rpc '.$this->repo,
|
||||||
|
'application/x-'.$path.'-result');
|
||||||
|
$response->setStdin(fopen('php://input', 'rb'));
|
||||||
|
return $response;
|
||||||
|
|
||||||
|
// regular file
|
||||||
|
default:
|
||||||
|
// make sure we're inside the repo hierarchy (ie. no break-out)
|
||||||
|
if (is_file($this->repo.'/'.$path) &&
|
||||||
|
strpos(realpath($this->repo.'/'.$path), $this->repo.'/') == 0) {
|
||||||
|
return new Pluf_HTTP_Response_File($this->repo.'/'.$path,
|
||||||
|
'application/octet-stream');
|
||||||
|
} else {
|
||||||
|
return new Pluf_HTTP_Response_NotFound($request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -747,7 +747,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'
|
||||||
|
@@ -132,6 +132,12 @@ class IDF_Views_Source
|
|||||||
$request);
|
$request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function repository($request, $match)
|
||||||
|
{
|
||||||
|
$scm = IDF_Scm::get($request->project);
|
||||||
|
return $scm->repository($request, $match);
|
||||||
|
}
|
||||||
|
|
||||||
public $treeBase_precond = array('IDF_Precondition::accessSource',
|
public $treeBase_precond = array('IDF_Precondition::accessSource',
|
||||||
'IDF_Views_Source_Precondition::scmAvailable',
|
'IDF_Views_Source_Precondition::scmAvailable',
|
||||||
'IDF_Views_Source_Precondition::revisionValid');
|
'IDF_Views_Source_Precondition::revisionValid');
|
||||||
|
@@ -245,6 +245,11 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/source/changesrev/$#',
|
|||||||
'model' => 'IDF_Views_Source_Svn',
|
'model' => 'IDF_Views_Source_Svn',
|
||||||
'method' => 'changelogRev');
|
'method' => 'changelogRev');
|
||||||
|
|
||||||
|
$ctl[] = array('regex' => '#^/r/([\-\w]+)/(.*)$#',
|
||||||
|
'base' => $base,
|
||||||
|
'model' => 'IDF_Views_Source',
|
||||||
|
'method' => 'repository');
|
||||||
|
|
||||||
// ---------- WIKI -----------------------------------------
|
// ---------- WIKI -----------------------------------------
|
||||||
|
|
||||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/$#',
|
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/$#',
|
||||||
|
@@ -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>
|
||||||
|
|
||||||
|
@@ -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}
|
||||||
|
@@ -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
55
test/IDF/Scm/SvnTest.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@@ -0,0 +1 @@
|
|||||||
|
3
|
@@ -0,0 +1,2 @@
|
|||||||
|
4
|
||||||
|
layout sharded 1000
|
@@ -0,0 +1 @@
|
|||||||
|
fsfs
|
@@ -0,0 +1 @@
|
|||||||
|
0
|
Binary file not shown.
@@ -0,0 +1,5 @@
|
|||||||
|
K 8
|
||||||
|
svn:date
|
||||||
|
V 27
|
||||||
|
2011-08-12T17:06:15.429110Z
|
||||||
|
END
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -0,0 +1 @@
|
|||||||
|
3
|
@@ -0,0 +1 @@
|
|||||||
|
5d5eaf70-0fe3-4d07-ada0-45c336a5bfad
|
@@ -0,0 +1 @@
|
|||||||
|
5
|
Reference in New Issue
Block a user