Compare commits
	
		
			23 Commits
		
	
	
		
			feature.is
			...
			feature.is
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					813184f06c | ||
| 
						 | 
					d860f299fd | ||
| 
						 | 
					33882d4fa7 | ||
| 
						 | 
					13fad756ab | ||
| 
						 | 
					1f0791df0e | ||
| 
						 | 
					a2c832a130 | ||
| 
						 | 
					b17de014ec | ||
| 
						 | 
					b1b72190e1 | ||
| 
						 | 
					2ff2f888bc | ||
| 
						 | 
					57c2389aae | ||
| 
						 | 
					d54c86f813 | ||
| 
						 | 
					05a9697007 | ||
| 
						 | 
					945429abf0 | ||
| 
						 | 
					a016bcb51b | ||
| 
						 | 
					f2b1ce795c | ||
| 
						 | 
					3a8c56acc4 | ||
| 
						 | 
					7b2552f940 | ||
| 
						 | 
					324b202215 | ||
| 
						 | 
					2c2da6082a | ||
| 
						 | 
					dd3fbbd7e4 | ||
| 
						 | 
					9bbcd571ec | ||
| 
						 | 
					d1bcdcda20 | ||
| 
						 | 
					4879d64989 | 
@@ -16,7 +16,7 @@ or newer to properly run this version of Indefero!
 | 
			
		||||
 | 
			
		||||
## 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)
 | 
			
		||||
- 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)
 | 
			
		||||
@@ -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)
 | 
			
		||||
- 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)
 | 
			
		||||
- 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
 | 
			
		||||
  ATTENTION: This needs Pluf 46b7f251 or newer!
 | 
			
		||||
- 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
 | 
			
		||||
- 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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -88,6 +88,7 @@ class IDF_Middleware
 | 
			
		||||
                              'showuser' => 'IDF_Template_ShowUser',
 | 
			
		||||
                              'ashowuser' => 'IDF_Template_AssignShowUser',
 | 
			
		||||
                              'appversion' => 'IDF_Template_AppVersion',
 | 
			
		||||
                              'upload' => 'IDF_Template_Tag_UploadUrl',
 | 
			
		||||
                                            ));
 | 
			
		||||
        $params['modifiers'] = array_merge($params['modifiers'],
 | 
			
		||||
                                           array(
 | 
			
		||||
 
 | 
			
		||||
@@ -59,6 +59,16 @@ class IDF_Plugin_SyncGit_Cron
 | 
			
		||||
                $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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -152,15 +152,14 @@ class IDF_Project extends Pluf_Model
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        $sqlIssueTable = Pluf::factory('IDF_Issue')->getSqlTable();
 | 
			
		||||
        $query = <<<"QUERY"
 | 
			
		||||
SELECT uid AS id,COUNT(uid) AS nb
 | 
			
		||||
        $query = "SELECT uid AS id,COUNT(uid) AS nb
 | 
			
		||||
FROM (
 | 
			
		||||
    SELECT COALESCE(owner, -1) AS uid
 | 
			
		||||
    FROM $sqlIssueTable
 | 
			
		||||
    WHERE status IN ($tags)
 | 
			
		||||
    ) AS ff
 | 
			
		||||
GROUP BY uid
 | 
			
		||||
QUERY;
 | 
			
		||||
GROUP BY uid";
 | 
			
		||||
 | 
			
		||||
        $db = Pluf::db();
 | 
			
		||||
        $dbData = $db->select($query);
 | 
			
		||||
        $ownerStatistics = array();
 | 
			
		||||
 
 | 
			
		||||
@@ -346,6 +346,14 @@ class IDF_Scm_Git extends IDF_Scm
 | 
			
		||||
        if (!preg_match('/<(.*)>/', $author, $match)) {
 | 
			
		||||
            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]));
 | 
			
		||||
        $users = Pluf::factory('Pluf_User')->getList(array('filter'=>$sql->gen()));
 | 
			
		||||
        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['title'] = IDF_Commit::toUTF8($c['title']);
 | 
			
		||||
        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;
 | 
			
		||||
        return $res;
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,6 @@
 | 
			
		||||
 */
 | 
			
		||||
class IDF_Scm_Svn extends IDF_Scm
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    public $username = '';
 | 
			
		||||
    public $password = '';
 | 
			
		||||
    private $assoc = array('dir' => 'tree',
 | 
			
		||||
@@ -48,11 +47,7 @@ class IDF_Scm_Svn extends IDF_Scm
 | 
			
		||||
 | 
			
		||||
    public function isAvailable()
 | 
			
		||||
    {
 | 
			
		||||
        $cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --xml --username=%s --password=%s %s',
 | 
			
		||||
                       escapeshellarg($this->username),
 | 
			
		||||
                       escapeshellarg($this->password),
 | 
			
		||||
                       escapeshellarg($this->repo));
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        $cmd = $this->svnCmd(array('info', '--xml'), $this->repo);
 | 
			
		||||
        $xmlInfo = self::shell_exec('IDF_Scm_Svn::isAvailable', $cmd);
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
@@ -163,12 +158,7 @@ class IDF_Scm_Svn extends IDF_Scm
 | 
			
		||||
            return IDF_Scm::REVISION_VALID;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --username=%s --password=%s --revision=%s %s',
 | 
			
		||||
                       escapeshellarg($this->username),
 | 
			
		||||
                       escapeshellarg($this->password),
 | 
			
		||||
                       escapeshellarg($rev),
 | 
			
		||||
                       escapeshellarg($this->repo));
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        $cmd = $this->svnCmd(array('info'), $this->repo, $rev);
 | 
			
		||||
        self::exec('IDF_Scm_Svn::validateRevision', $cmd, $out, $ret);
 | 
			
		||||
 | 
			
		||||
        if ($ret == 0)
 | 
			
		||||
@@ -190,12 +180,9 @@ class IDF_Scm_Svn extends IDF_Scm
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Else, test the path on revision
 | 
			
		||||
        $cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --xml --username=%s --password=%s --revision=%s %s',
 | 
			
		||||
                       escapeshellarg($this->username),
 | 
			
		||||
                       escapeshellarg($this->password),
 | 
			
		||||
                       escapeshellarg($rev),
 | 
			
		||||
                       escapeshellarg($this->repo.'/'.self::smartEncode($path)));
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        $cmd = $this->svnCmd(array('info', '--xml'),
 | 
			
		||||
                             $this->repo.'/'.self::smartEncode($path),
 | 
			
		||||
                             $rev);
 | 
			
		||||
        $xmlInfo = self::shell_exec('IDF_Scm_Svn::testHash', $cmd);
 | 
			
		||||
 | 
			
		||||
        // If exception is thrown, return false
 | 
			
		||||
@@ -217,12 +204,9 @@ class IDF_Scm_Svn extends IDF_Scm
 | 
			
		||||
 | 
			
		||||
    public function getTree($commit, $folder='/', $branch=null)
 | 
			
		||||
    {
 | 
			
		||||
        $cmd = sprintf(Pluf::f('svn_path', 'svn').' ls --no-auth-cache --xml --username=%s --password=%s --revision=%s %s',
 | 
			
		||||
                       escapeshellarg($this->username),
 | 
			
		||||
                       escapeshellarg($this->password),
 | 
			
		||||
                       escapeshellarg($commit),
 | 
			
		||||
                       escapeshellarg($this->repo.'/'.self::smartEncode($folder)));
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        $cmd = $this->svnCmd(array('ls', '--xml'),
 | 
			
		||||
                             $this->repo.'/'.self::smartEncode($folder),
 | 
			
		||||
                             $commit);
 | 
			
		||||
        $xml = simplexml_load_string(self::shell_exec('IDF_Scm_Svn::getTree', $cmd));
 | 
			
		||||
        $res = array();
 | 
			
		||||
        $folder = (strlen($folder) and ($folder != '/')) ? $folder.'/' : '';
 | 
			
		||||
@@ -258,12 +242,7 @@ class IDF_Scm_Svn extends IDF_Scm
 | 
			
		||||
        if (isset($this->cache['commitmess'][$rev])) {
 | 
			
		||||
            return $this->cache['commitmess'][$rev];
 | 
			
		||||
        }
 | 
			
		||||
        $cmd = sprintf(Pluf::f('svn_path', 'svn').' log --no-auth-cache --xml --limit 1 --username=%s --password=%s --revision=%s %s',
 | 
			
		||||
                       escapeshellarg($this->username),
 | 
			
		||||
                       escapeshellarg($this->password),
 | 
			
		||||
                       escapeshellarg($rev),
 | 
			
		||||
                       escapeshellarg($this->repo));
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        $cmd = $this->svnCmd(array('log', '--xml', '--limit', '1'), $this->repo, $rev);
 | 
			
		||||
        try {
 | 
			
		||||
            $xml = simplexml_load_string(self::shell_exec('IDF_Scm_Svn::getCommitMessage', $cmd));
 | 
			
		||||
            $this->cache['commitmess'][$rev] = (string) $xml->logentry->msg;
 | 
			
		||||
@@ -279,12 +258,8 @@ class IDF_Scm_Svn extends IDF_Scm
 | 
			
		||||
        if ($rev == null) {
 | 
			
		||||
            $rev = 'HEAD';
 | 
			
		||||
        }
 | 
			
		||||
        $cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --xml --username=%s --password=%s --revision=%s %s',
 | 
			
		||||
                       escapeshellarg($this->username),
 | 
			
		||||
                       escapeshellarg($this->password),
 | 
			
		||||
                       escapeshellarg($rev),
 | 
			
		||||
                       escapeshellarg($this->repo.'/'.self::smartEncode($filename)));
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        $cmd = $this->svnCmd(array('info', '--xml'),
 | 
			
		||||
                             $this->repo.'/'.self::smartEncode($filename), $rev);
 | 
			
		||||
        $xml = simplexml_load_string(self::shell_exec('IDF_Scm_Svn::getPathInfo', $cmd));
 | 
			
		||||
        if (!isset($xml->entry)) {
 | 
			
		||||
            return false;
 | 
			
		||||
@@ -306,12 +281,9 @@ class IDF_Scm_Svn extends IDF_Scm
 | 
			
		||||
 | 
			
		||||
    public function getFile($def, $cmd_only=false)
 | 
			
		||||
    {
 | 
			
		||||
        $cmd = sprintf(Pluf::f('svn_path', 'svn').' cat --no-auth-cache --username=%s --password=%s --revision=%s %s',
 | 
			
		||||
                       escapeshellarg($this->username),
 | 
			
		||||
                       escapeshellarg($this->password),
 | 
			
		||||
                       escapeshellarg($def->rev),
 | 
			
		||||
                       escapeshellarg($this->repo.'/'.self::smartEncode($def->fullpath)));
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        $cmd = $this->svnCmd(array('cat'),
 | 
			
		||||
                             $this->repo.'/'.self::smartEncode($def->fullpath),
 | 
			
		||||
                             $def->rev);
 | 
			
		||||
        return ($cmd_only) ?
 | 
			
		||||
            $cmd : self::shell_exec('IDF_Scm_Svn::getFile', $cmd);
 | 
			
		||||
    }
 | 
			
		||||
@@ -327,11 +299,7 @@ class IDF_Scm_Svn extends IDF_Scm
 | 
			
		||||
            return $this->cache['branches'];
 | 
			
		||||
        }
 | 
			
		||||
        $res = array();
 | 
			
		||||
        $cmd = sprintf(Pluf::f('svn_path', 'svn').' ls --no-auth-cache --username=%s --password=%s --revision=HEAD %s',
 | 
			
		||||
                       escapeshellarg($this->username),
 | 
			
		||||
                       escapeshellarg($this->password),
 | 
			
		||||
                       escapeshellarg($this->repo.'/branches'));
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        $cmd = $this->svnCmd(array('ls'), $this->repo.'/branches', 'HEAD');
 | 
			
		||||
        self::exec('IDF_Scm_Svn::getBranches', $cmd, $out, $ret);
 | 
			
		||||
        if ($ret == 0) {
 | 
			
		||||
            foreach ($out as $entry) {
 | 
			
		||||
@@ -342,11 +310,8 @@ class IDF_Scm_Svn extends IDF_Scm
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ksort($res);
 | 
			
		||||
        $cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --username=%s --password=%s --revision=HEAD %s',
 | 
			
		||||
                       escapeshellarg($this->username),
 | 
			
		||||
                       escapeshellarg($this->password),
 | 
			
		||||
                       escapeshellarg($this->repo.'/trunk'));
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
 | 
			
		||||
        $cmd = $this->svnCmd(array('info'), $this->repo.'/trunk', 'HEAD');
 | 
			
		||||
        self::exec('IDF_Scm_Svn::getBranches', $cmd, $out, $ret);
 | 
			
		||||
        if ($ret == 0) {
 | 
			
		||||
            $res = array('trunk' => 'trunk') + $res;
 | 
			
		||||
@@ -366,11 +331,7 @@ class IDF_Scm_Svn extends IDF_Scm
 | 
			
		||||
            return $this->cache['tags'];
 | 
			
		||||
        }
 | 
			
		||||
        $res = array();
 | 
			
		||||
        $cmd = sprintf(Pluf::f('svn_path', 'svn').' ls --no-auth-cache --username=%s --password=%s --revision=HEAD %s',
 | 
			
		||||
                       escapeshellarg($this->username),
 | 
			
		||||
                       escapeshellarg($this->password),
 | 
			
		||||
                       escapeshellarg($this->repo.'/tags'));
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        $cmd = $this->svnCmd(array('ls'), $this->repo.'/tags', 'HEAD');
 | 
			
		||||
        self::exec('IDF_Scm_Svn::getTags', $cmd, $out, $ret);
 | 
			
		||||
        if ($ret == 0) {
 | 
			
		||||
            foreach ($out as $entry) {
 | 
			
		||||
@@ -423,12 +384,8 @@ class IDF_Scm_Svn extends IDF_Scm
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        $res = array();
 | 
			
		||||
        $cmd = sprintf(Pluf::f('svn_path', 'svn').' log --no-auth-cache --xml --limit 1 -v --username=%s --password=%s --revision=%s %s',
 | 
			
		||||
                       escapeshellarg($this->username),
 | 
			
		||||
                       escapeshellarg($this->password),
 | 
			
		||||
                       escapeshellarg($commit),
 | 
			
		||||
                       escapeshellarg($this->repo));
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        $cmd = $this->svnCmd(array('log', '--xml', '--limit', '1', '-v'),
 | 
			
		||||
                             $this->repo, $commit);
 | 
			
		||||
        $xmlRes = self::shell_exec('IDF_Scm_Svn::getCommit', $cmd);
 | 
			
		||||
        $xml = simplexml_load_string($xmlRes);
 | 
			
		||||
        $res['author'] = (string) $xml->logentry->author;
 | 
			
		||||
@@ -470,12 +427,7 @@ class IDF_Scm_Svn extends IDF_Scm
 | 
			
		||||
    private function getDiff($rev='HEAD')
 | 
			
		||||
    {
 | 
			
		||||
        $res = array();
 | 
			
		||||
        $cmd = sprintf(Pluf::f('svn_path', 'svn').' diff --no-auth-cache -c %s --username=%s --password=%s %s',
 | 
			
		||||
                       escapeshellarg($rev),
 | 
			
		||||
                       escapeshellarg($this->username),
 | 
			
		||||
                       escapeshellarg($this->password),
 | 
			
		||||
                       escapeshellarg($this->repo));
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        $cmd = $this->svnCmd(array('diff', '-c', $rev), $this->repo);
 | 
			
		||||
        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) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        $cmd = sprintf(Pluf::f('svn_path', 'svn').' log --xml -v --no-auth-cache -r %s --username=%s --password=%s %s',
 | 
			
		||||
                       escapeshellarg($commit),
 | 
			
		||||
                       escapeshellarg($this->username),
 | 
			
		||||
                       escapeshellarg($this->password),
 | 
			
		||||
                       escapeshellarg($this->repo));
 | 
			
		||||
        $cmd = $this->svnCmd(array('log', '--xml', '-v'), $this->repo, $commit);
 | 
			
		||||
        $out = array();
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        $out = self::shell_exec('IDF_Scm_Svn::getChanges', $cmd);
 | 
			
		||||
        $xml = simplexml_load_string($out);
 | 
			
		||||
        if (count($xml) == 0) {
 | 
			
		||||
@@ -570,20 +517,15 @@ class IDF_Scm_Svn extends IDF_Scm
 | 
			
		||||
     *
 | 
			
		||||
     * @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
 | 
			
		||||
            $branch = 'HEAD';
 | 
			
		||||
            $rev = 'HEAD';
 | 
			
		||||
        }
 | 
			
		||||
        $res = array();
 | 
			
		||||
        $cmd = sprintf(Pluf::f('svn_path', 'svn').' log --no-auth-cache --xml -v --limit %s --username=%s --password=%s --revision=%s %s',
 | 
			
		||||
                       escapeshellarg($n),
 | 
			
		||||
                       escapeshellarg($this->username),
 | 
			
		||||
                       escapeshellarg($this->password),
 | 
			
		||||
                       escapeshellarg($branch),
 | 
			
		||||
                       escapeshellarg($this->repo));
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        $cmd = $this->svnCmd(array('log', '--xml', '-v', '--limit', $n),
 | 
			
		||||
                             $this->repo.'@'.$rev);
 | 
			
		||||
        $xmlRes = self::shell_exec('IDF_Scm_Svn::getChangeLog', $cmd);
 | 
			
		||||
        $xml = simplexml_load_string($xmlRes);
 | 
			
		||||
        foreach ($xml->logentry as $entry) {
 | 
			
		||||
@@ -609,12 +551,8 @@ class IDF_Scm_Svn extends IDF_Scm
 | 
			
		||||
    public function getProperties($rev, $path='')
 | 
			
		||||
    {
 | 
			
		||||
        $res = array();
 | 
			
		||||
        $cmd = sprintf(Pluf::f('svn_path', 'svn').' proplist --no-auth-cache --xml --username=%s --password=%s --revision=%s %s',
 | 
			
		||||
                       escapeshellarg($this->username),
 | 
			
		||||
                       escapeshellarg($this->password),
 | 
			
		||||
                       escapeshellarg($rev),
 | 
			
		||||
                       escapeshellarg($this->repo.'/'.self::smartEncode($path)));
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        $cmd = $this->svnCmd(array('proplist', '--xml'),
 | 
			
		||||
                             $this->repo.'/'.self::smartEncode($path), $rev);
 | 
			
		||||
        $xmlProps = self::shell_exec('IDF_Scm_Svn::getProperties', $cmd);
 | 
			
		||||
        $props = simplexml_load_string($xmlProps);
 | 
			
		||||
 | 
			
		||||
@@ -643,13 +581,8 @@ class IDF_Scm_Svn extends IDF_Scm
 | 
			
		||||
    private function getProperty($property, $rev, $path='')
 | 
			
		||||
    {
 | 
			
		||||
        $res = array();
 | 
			
		||||
        $cmd = sprintf(Pluf::f('svn_path', 'svn').' propget --no-auth-cache --xml %s --username=%s --password=%s --revision=%s %s',
 | 
			
		||||
                       escapeshellarg($property),
 | 
			
		||||
                       escapeshellarg($this->username),
 | 
			
		||||
                       escapeshellarg($this->password),
 | 
			
		||||
                       escapeshellarg($rev),
 | 
			
		||||
                       escapeshellarg($this->repo.'/'.self::smartEncode($path)));
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        $cmd = $this->svnCmd(array('propget', $property, '--xml'),
 | 
			
		||||
                             $this->repo.'/'.self::smartEncode($path), $rev);
 | 
			
		||||
        $xmlProp = self::shell_exec('IDF_Scm_Svn::getProperty', $cmd);
 | 
			
		||||
        $prop = simplexml_load_string($xmlProp);
 | 
			
		||||
 | 
			
		||||
@@ -666,16 +599,38 @@ class IDF_Scm_Svn extends IDF_Scm
 | 
			
		||||
    public function getLastCommit($rev='HEAD')
 | 
			
		||||
    {
 | 
			
		||||
        $xmlInfo = '';
 | 
			
		||||
        $cmd = sprintf(Pluf::f('svn_path', 'svn').' info --no-auth-cache --xml --username=%s --password=%s --revision=%s %s',
 | 
			
		||||
                       escapeshellarg($this->username),
 | 
			
		||||
                       escapeshellarg($this->password),
 | 
			
		||||
                       escapeshellarg($rev),
 | 
			
		||||
                       escapeshellarg($this->repo));
 | 
			
		||||
        $cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
 | 
			
		||||
        $cmd = $this->svnCmd(array('info', '--xml'), $this->repo, $rev);
 | 
			
		||||
        $xmlInfo = self::shell_exec('IDF_Scm_Svn::getLastCommit', $cmd);
 | 
			
		||||
 | 
			
		||||
        $xml = simplexml_load_string($xmlInfo);
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -58,7 +58,7 @@ class IDF_Template_IssueComment extends Pluf_Template_Tag
 | 
			
		||||
                      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',
 | 
			
		||||
                                                 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);
 | 
			
		||||
        }
 | 
			
		||||
        if ($wordwrap) $text = Pluf_Text::wrapHtml($text, 69, "\n");
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										35
									
								
								src/IDF/Template/Tag/UploadUrl.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/IDF/Template/Tag/UploadUrl.php
									
									
									
									
									
										Normal 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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -150,7 +150,7 @@ class IDF_Upload extends Pluf_Model
 | 
			
		||||
        if ($this->id == '') {
 | 
			
		||||
            $this->creation_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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getFullPath()
 | 
			
		||||
    {
 | 
			
		||||
        return(Pluf::f('upload_path').'/'.$this->get_project()->shortname.'/files/'.$this->file);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * We drop the information from the timeline.
 | 
			
		||||
     */
 | 
			
		||||
@@ -256,4 +261,4 @@ class IDF_Upload extends Pluf_Model
 | 
			
		||||
        }
 | 
			
		||||
        Pluf_Translation::loadSetLocale($current_locale);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -202,7 +202,11 @@ class IDF_Views_Download
 | 
			
		||||
        $prj->inOr404($upload);
 | 
			
		||||
        $upload->downloads += 1;
 | 
			
		||||
        $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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -109,11 +109,13 @@ class IDF_Views_Issue
 | 
			
		||||
                foreach ($owners as $user => $nb) {
 | 
			
		||||
                    if ($user === '') {
 | 
			
		||||
                        $key = __('Not assigned');
 | 
			
		||||
                        $login = null;
 | 
			
		||||
                    } else {
 | 
			
		||||
                        $obj = Pluf::factory('Pluf_User')->getOne(array('filter'=>'id='.$user));
 | 
			
		||||
                        $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
 | 
			
		||||
@@ -313,42 +315,55 @@ class IDF_Views_Issue
 | 
			
		||||
     *
 | 
			
		||||
     * Only open issues are shown.
 | 
			
		||||
     */
 | 
			
		||||
    public $myIssues_precond = array('IDF_Precondition::accessIssues',
 | 
			
		||||
                                     'Pluf_Precondition::loginRequired');
 | 
			
		||||
    public function myIssues($request, $match)
 | 
			
		||||
    public $userIssues_precond = array('IDF_Precondition::accessIssues');
 | 
			
		||||
    public function userIssues($request, $match)
 | 
			
		||||
    {
 | 
			
		||||
        $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');
 | 
			
		||||
        $ctags = $prj->getTagIdsByStatus('closed');
 | 
			
		||||
        if (count($otags) == 0) $otags[] = 0;
 | 
			
		||||
        if (count($ctags) == 0) $ctags[] = 0;
 | 
			
		||||
        switch ($match[2]) {
 | 
			
		||||
        switch ($match[3]) {
 | 
			
		||||
        case 'submit':
 | 
			
		||||
            $title = sprintf(__('My Submitted %s Issues'), (string) $prj);
 | 
			
		||||
            $f_sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $request->user->id));
 | 
			
		||||
            $titleFormat = __('%s %s Submitted %s Issues');
 | 
			
		||||
            $f_sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $user->id));
 | 
			
		||||
            break;
 | 
			
		||||
        case 'submitclosed':
 | 
			
		||||
            $title = sprintf(__('My Closed Submitted %s Issues'), (string) $prj);
 | 
			
		||||
            $f_sql = new Pluf_SQL('project=%s AND submitter=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $request->user->id));
 | 
			
		||||
            $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, $user->id));
 | 
			
		||||
            break;
 | 
			
		||||
        case 'ownerclosed':
 | 
			
		||||
            $title = sprintf(__('My Closed Working %s Issues'), (string) $prj);
 | 
			
		||||
            $f_sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $ctags).')', array($prj->id, $request->user->id));
 | 
			
		||||
            $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, $user->id));
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            $title = sprintf(__('My Working %s Issues'), (string) $prj);
 | 
			
		||||
            $f_sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $request->user->id));
 | 
			
		||||
            $titleFormat = __('%s %s Working %s Issues');
 | 
			
		||||
            $f_sql = new Pluf_SQL('project=%s AND owner=%s AND status IN ('.implode(', ', $otags).')', array($prj->id, $user->id));
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        $title = sprintf($titleFormat,
 | 
			
		||||
                         $user->first_name,
 | 
			
		||||
                         $user->last_name,
 | 
			
		||||
                         (string) $prj);
 | 
			
		||||
        
 | 
			
		||||
        // 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()));
 | 
			
		||||
        $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()));
 | 
			
		||||
        // 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()));
 | 
			
		||||
        $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()));
 | 
			
		||||
 | 
			
		||||
        // Paginator to paginate the issues
 | 
			
		||||
@@ -359,7 +374,7 @@ class IDF_Views_Issue
 | 
			
		||||
                                       'current_user' => $request->user);
 | 
			
		||||
        $pag->summary = __('This table shows the open issues.');
 | 
			
		||||
        $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_reverse_order = array('modif_dtime');
 | 
			
		||||
        $pag->sort_link_title = true;
 | 
			
		||||
@@ -374,9 +389,10 @@ class IDF_Views_Issue
 | 
			
		||||
        $pag->items_per_page = 10;
 | 
			
		||||
        $pag->no_results_text = __('No issues were found.');
 | 
			
		||||
        $pag->setFromRequest($request);
 | 
			
		||||
        return Pluf_Shortcuts_RenderToResponse('idf/issues/my-issues.html',
 | 
			
		||||
        return Pluf_Shortcuts_RenderToResponse('idf/issues/userIssues.html',
 | 
			
		||||
                                               array('project' => $prj,
 | 
			
		||||
                                                     'page_title' => $title,
 | 
			
		||||
                                                     'login' => $user->login,
 | 
			
		||||
                                                     'nb_submit' => $nb_submit,
 | 
			
		||||
                                                     'nb_owner' => $nb_owner,
 | 
			
		||||
                                                     'nb_submit_closed' => $nb_submit_closed,
 | 
			
		||||
@@ -747,7 +763,13 @@ class IDF_Views_Issue
 | 
			
		||||
        else {
 | 
			
		||||
            // ID-based search
 | 
			
		||||
            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(
 | 
			
		||||
                    'filter' => $sql->gen(),
 | 
			
		||||
                    'order' => 'id ASC'
 | 
			
		||||
 
 | 
			
		||||
@@ -153,10 +153,10 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/create/$#',
 | 
			
		||||
               'model' => 'IDF_Views_Issue',
 | 
			
		||||
               'method' => 'create');
 | 
			
		||||
 | 
			
		||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/my/(\w+)/$#',
 | 
			
		||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/(.*)/(\w+)/$#',
 | 
			
		||||
               'base' => $base,
 | 
			
		||||
               'model' => 'IDF_Views_Issue',
 | 
			
		||||
               'method' => 'myIssues');
 | 
			
		||||
               'method' => 'userIssues');
 | 
			
		||||
 | 
			
		||||
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/attachment/(\d+)/(.*)$#',
 | 
			
		||||
               'base' => $base,
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
<div id="sub-tabs">
 | 
			
		||||
<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>
 | 
			
		||||
{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} |
 | 
			
		||||
<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" />
 | 
			
		||||
 
 | 
			
		||||
@@ -10,12 +10,22 @@
 | 
			
		||||
{if $attachments.count() > 0}
 | 
			
		||||
<hr align="left" class="attach" />
 | 
			
		||||
<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}
 | 
			
		||||
{if $c.changes}
 | 
			
		||||
{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}
 | 
			
		||||
{/if}
 | 
			
		||||
</div></content>
 | 
			
		||||
</entry>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@
 | 
			
		||||
 | 
			
		||||
{if strlen($c.content) > 0}{$c.content|safe}{/if}{if $c.changedIssue()}
 | 
			
		||||
{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}
 | 
			
		||||
- {$a.filename|safe} - {$a.filesize|ssize}
 | 
			
		||||
 
 | 
			
		||||
@@ -71,7 +71,12 @@
 | 
			
		||||
    <tbody>
 | 
			
		||||
    {foreach $ownerStatistics as $key => $value}
 | 
			
		||||
        <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="graph">
 | 
			
		||||
            <table class='graph'>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
{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}
 | 
			
		||||
{$issues.render}
 | 
			
		||||
{if !$user.isAnonymous()}
 | 
			
		||||
@@ -8,10 +8,10 @@
 | 
			
		||||
 | 
			
		||||
{/block}
 | 
			
		||||
{block context}
 | 
			
		||||
{aurl 'owner_url', 'IDF_Views_Issue::myIssues', array($project.shortname, 'owner')}
 | 
			
		||||
{aurl 'submit_url', 'IDF_Views_Issue::myIssues', array($project.shortname, 'submit')}
 | 
			
		||||
{aurl 'owner_closed_url', 'IDF_Views_Issue::myIssues', array($project.shortname, 'ownerclosed')}
 | 
			
		||||
{aurl 'submit_closed_url', 'IDF_Views_Issue::myIssues', array($project.shortname, 'submitclosed')}
 | 
			
		||||
{aurl 'owner_url', 'IDF_Views_Issue::userIssues', array($project.shortname, $login, 'owner')}
 | 
			
		||||
{aurl 'submit_url', 'IDF_Views_Issue::userIssues', array($project.shortname, $login, 'submit')}
 | 
			
		||||
{aurl 'owner_closed_url', 'IDF_Views_Issue::userIssues', array($project.shortname, $login, 'ownerclosed')}
 | 
			
		||||
{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> 
 | 
			
		||||
{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}
 | 
			
		||||
@@ -17,7 +17,7 @@
 | 
			
		||||
{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}">
 | 
			
		||||
{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}
 | 
			
		||||
<img style="float:right; position: relative; max-height: 60px; max-width: 60px;" src="http://www.gravatar.com/avatar/{$submitter.email|md5}.jpg?s=60&d={media}/idf/img/spacer.gif" alt=" " />
 | 
			
		||||
{/if}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,8 +10,8 @@
 | 
			
		||||
{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 $hasIssuesAccess and !$user.isAnonymous()}
 | 
			
		||||
{hotkey 'Shift+m', 'IDF_Views_Issue::myIssues', array($project.shortname, 'submit')}
 | 
			
		||||
{hotkey 'Shift+w', 'IDF_Views_Issue::myIssues', array($project.shortname, 'owner')}
 | 
			
		||||
{hotkey 'Shift+m', 'IDF_Views_Issue::userIssues', array($project.shortname, $user.login, 'submit')}
 | 
			
		||||
{hotkey 'Shift+w', 'IDF_Views_Issue::userIssues', array($project.shortname, $user.login, 'owner')}
 | 
			
		||||
{/if}{/if} //-->
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
<div id="sub-tabs">
 | 
			
		||||
<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">
 | 
			
		||||
<input accesskey="4" type="text" value="{$q}" name="q" size="20" />
 | 
			
		||||
<input type="submit" name="s" value="{trans 'Search'}" />
 | 
			
		||||
 
 | 
			
		||||
@@ -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()}
 | 
			
		||||
<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 != ''}
 | 
			
		||||
    <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}
 | 
			
		||||
    <img style="float:right; position: relative;" src="http://www.gravatar.com/avatar/{$submitter.email|md5}.jpg?s=60&d={media}/idf/img/spacer.gif" alt=" " />
 | 
			
		||||
{/if}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
<table class="form" summary="">
 | 
			
		||||
<tr>
 | 
			
		||||
<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}
 | 
			
		||||
    <img src="http://www.gravatar.com/avatar/{$member.email|md5}.jpg?s=60&d={media}/idf/img/spacer.gif" alt=" " />
 | 
			
		||||
{/if}
 | 
			
		||||
 
 | 
			
		||||
@@ -625,6 +625,7 @@ END;
 | 
			
		||||
            'additions'  => array('new_dir', 'new_dir/new_file'),
 | 
			
		||||
            'deletions'  => array('old_dir', 'old_dir/old_file'),
 | 
			
		||||
            'renames'    => array('dir_with_old_name' => 'new_dir/dir_with_new_name'),
 | 
			
		||||
            'copies'     => array(), // this is always empty
 | 
			
		||||
            'patches'    => array('existing_file'),
 | 
			
		||||
            'properties' => 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