Merged master back into the code review work.

This commit is contained in:
Loic d'Anterroches 2009-07-15 14:18:50 +02:00
commit dd56d681b3
81 changed files with 2555 additions and 1021 deletions

12
AUTHORS
View File

@ -9,4 +9,14 @@ Much appreciated contributors:
Baptiste Michaud <bactisme@gmail.com> - Subversion synchronization
Julien Issler
Manuel Eidenberger <eidenberger@gmail.com>
Patrick Georgi
Ciaran Gultnieks
Mehdi Kabab
Sindre R. Myren
Patrick Georgi <patrick.georgi@coresystems.de>
Adrien Bustany
Charles Melbye
Baptiste Durand-Bret
And all the nice users who spent time reporting issues and promoting
the project. The project could not live without them.

View File

@ -70,6 +70,13 @@ folder. Here is a configuration example:
$cfg['idf_plugin_syncgit_path_gitserve'] = '/home/www/indefero/scripts/gitserve.py'; # yes .py
$cfg['idf_plugin_syncgit_path_authorized_keys'] = '/home/git/.ssh/authorized_keys';
$cfg['idf_plugin_syncgit_sync_file'] = '/tmp/SYNC-GIT';
# Remove the git repositories which do not have a corresponding project
# This is run at cron time
$cfg['idf_plugin_syncgit_remove_orphans'] = false;
# git account home dir
$cfg['idf_plugin_syncgit_git_home_dir'] = '/home/git';
# where are going to be the git repositories
$cfg['idf_plugin_sncgit_base_repositories'] = '/home/git/repositories';
When someone will change his SSH key or add a new one, the
`/tmp/SYNC-GIT` file will be created. The cron job

View File

@ -66,6 +66,8 @@ need to put the following in your configuration file:
$cfg['idf_plugin_syncsvn_authz_file'] = '/home/svn/dav_svn.authz';
$cfg['idf_plugin_syncsvn_passwd_file'] = '/home/svn/dav_svn.passwd';
$cfg['idf_plugin_syncsvn_svn_path'] = '/home/svn/repositories';
// Delete the corresponding repository when deleting the project
$cfg['idf_plugin_syncsvn_remove_orphans'] = false;
You can have more control over the permissions given to the owners,
members, extra authorized users and anonymous users if you want with

View File

@ -37,5 +37,5 @@ set_include_path(get_include_path()
require 'Pluf.php';
Pluf::start(dirname(__FILE__).'/../src/IDF/conf/idf.php');
Pluf_Dispatcher::loadControllers(Pluf::f('idf_views'));
IDF_Plugin_SyncGit_Serve::main($argv, $_ENV);
IDF_Plugin_SyncGit_Serve::main($argv, array_merge($_SERVER, $_ENV));

View File

@ -144,12 +144,14 @@ class IDF_Commit extends Pluf_Model
if ($r->count() > 0) {
return $r[0];
}
if (!isset($change->full_message)) {
$change->full_message = '';
}
$scm = IDF_Scm::get($project);
$commit = new IDF_Commit();
$commit->project = $project;
$commit->scm_id = $change->commit;
$commit->summary = $change->title;
$commit->fullmessage = $change->full_message;
list($commit->summary, $commit->fullmessage) = self::toUTF8(array($change->title, $change->full_message));
$commit->author = $scm->findAuthor($change->author);
$commit->origauthor = $change->author;
$commit->creation_dtime = $change->date;
@ -176,6 +178,39 @@ class IDF_Commit extends Pluf_Model
return $commit;
}
/**
* Convert encoding to UTF8.
*
* If an array is given, the encoding is detected only on the
* first value and then used to convert all the strings.
*
* @param mixed String or array of string to be converted
* @return mixed String or array of string
*/
public static function toUTF8($text)
{
$enc = 'ASCII, UTF-8, ISO-8859-1, JIS, EUC-JP, SJIS';
$ref = $text;
if (is_array($text)) {
$ref = $text[0];
}
if (Pluf_Text_UTF8::check($ref)) {
return $text;
}
$encoding = mb_detect_encoding($ref, $enc, true);
if ($encoding == false) {
$encoding = Pluf_Text_UTF8::detect_cyr_charset($ref);
}
if (is_array($text)) {
foreach ($text as $t) {
$res[] = mb_convert_encoding($t, 'UTF-8', $encoding);
}
return $res;
} else {
return mb_convert_encoding($text, 'UTF-8', $encoding);
}
}
/**
* Returns the timeline fragment for the commit.
*
@ -202,7 +237,7 @@ class IDF_Commit extends Pluf_Model
</tr>
<tr class="extra">
<td colspan="2">
<div class="helptext right">'.__('Commit').'&nbsp;<a href="'.$url.'" class="mono">'.$this->scm_id.'</a>, '.__('by').' '.$user.'</div></td></tr>';
<div class="helptext right">'.sprintf(__('Commit&nbsp;%s, by %s'), '<a href="'.$url.'" class="mono">'.$this->scm_id.'</a>', $user).'</div></td></tr>';
return Pluf_Template::markSafe($out);
}

View File

@ -172,6 +172,7 @@ class IDF_Diff
public static function padLine($line)
{
$line = str_replace("\t", ' ', $line);
$n = strlen($line);
for ($i=0;$i<$n;$i++) {
if (substr($line, $i, 1) != ' ') {

View File

@ -60,7 +60,7 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form
array('required' => true,
'label' => __('Shortname'),
'initial' => '',
'help_text' => __('It must be unique for each project and composed only of letters and digits.'),
'help_text' => __('It must be unique for each project and composed only of letters, digits and dash (-) like "my-project".'),
));
$this->fields['scm'] = new Pluf_Form_Field_Varchar(
@ -133,6 +133,16 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form
'IDF_Form_Admin_ProjectCreate', $params);
}
public function clean_owners()
{
return IDF_Form_MembersConf::checkBadLogins($this->cleaned_data['owners']);
}
public function clean_members()
{
return IDF_Form_MembersConf::checkBadLogins($this->cleaned_data['members']);
}
public function clean_svn_remote_url()
{
$this->cleaned_data['svn_remote_url'] = (!empty($this->cleaned_data['svn_remote_url'])) ? $this->cleaned_data['svn_remote_url'] : '';
@ -150,7 +160,7 @@ class IDF_Form_Admin_ProjectCreate extends Pluf_Form
{
$shortname = $this->cleaned_data['shortname'];
if (preg_match('/[^\-A-Za-z0-9]/', $shortname)) {
throw new Pluf_Form_Invalid(__('This shortname contains illegal characters, please use only letters and digits.'));
throw new Pluf_Form_Invalid(__('This shortname contains illegal characters, please use only letters, digits and dash (-).'));
}
if (mb_substr($shortname, 0, 1) == '-') {
throw new Pluf_Form_Invalid(__('The shortname cannot start with the dash (-) character.'));

View File

@ -61,6 +61,16 @@ class IDF_Form_Admin_ProjectUpdate extends Pluf_Form
));
}
public function clean_owners()
{
return IDF_Form_MembersConf::checkBadLogins($this->cleaned_data['owners']);
}
public function clean_members()
{
return IDF_Form_MembersConf::checkBadLogins($this->cleaned_data['members']);
}
public function save($commit=true)
{
if (!$this->isValid()) {

View File

@ -179,8 +179,11 @@ class IDF_Form_Admin_UserUpdate extends Pluf_Form
function clean_first_name()
{
$first_name = trim($this->cleaned_data['first_name']);
if ($first_name == '---') {
throw new Pluf_Form_Invalid(__('--- is not a valid first name.'));
}
if ($first_name == mb_strtoupper($first_name)) {
return mb_convert_case(mb_strtolower($first_name),
$first_name = mb_convert_case(mb_strtolower($first_name),
MB_CASE_TITLE, 'UTF-8');
}
return $first_name;

View File

@ -67,6 +67,45 @@ class IDF_Form_MembersConf extends Pluf_Form
$this->project->membershipsUpdated();
}
public function clean_owners()
{
return self::checkBadLogins($this->cleaned_data['owners']);
}
public function clean_members()
{
return self::checkBadLogins($this->cleaned_data['members']);
}
/**
* From the input, find the bad logins.
*
* @throws Pluf_Form_Invalid exception when bad logins are found
* @param string Comma, new line delimited list of logins
* @return string Comma, new line delimited list of logins
*/
public static function checkBadLogins($logins)
{
$bad = array();
foreach (preg_split("/\015\012|\015|\012|\,/", $logins, -1, PREG_SPLIT_NO_EMPTY) as $login) {
$sql = new Pluf_SQL('login=%s', array(trim($login)));
try {
$user = Pluf::factory('Pluf_User')->getOne(array('filter'=>$sql->gen()));
if (null == $user) {
$bad[] = $login;
}
} catch (Exception $e) {
$bad[] = $login;
}
}
$n = count($bad);
if ($n) {
$badlogins = Pluf_esc(implode(', ', $bad));
throw new Pluf_Form_Invalid(sprintf(_n('The following login is invalid: %s.', 'The following login are invalids: %s.', $n), $badlogins));
}
return $logins;
}
/**
* The update of the memberships is done in different places. This
* avoids duplicating code.

View File

@ -95,6 +95,10 @@ class IDF_Form_PasswordInputKey extends Pluf_Form
return false;
}
$cr = new Pluf_Crypt(md5(Pluf::f('secret_key')));
return split(':', $cr->decrypt($encrypted), 3);
$f = split(':', $cr->decrypt($encrypted), 3);
if (count($f) != 3) {
return false;
}
return $f;
}
}

View File

@ -59,8 +59,10 @@ class IDF_Form_ReviewCreate extends Pluf_Form
'rows' => 7,
),
));
$sql = new Pluf_SQL('project=%s', array($this->project->id));
$commits = Pluf::factory('IDF_Commit')->getList(array('order' => 'creation_dtime DESC',
'nb' => 10));
'nb' => 10,
'filter' => $sql->gen()));
$choices = array();
foreach ($commits as $c) {
$id = (strlen($c->scm_id) > 10) ? substr($c->scm_id, 0, 10) : $c->scm_id;

View File

@ -86,6 +86,11 @@ class IDF_Form_TabsConf extends Pluf_Form
));
}
public function clean_authorized_users()
{
return IDF_Form_MembersConf::checkBadLogins($this->cleaned_data['authorized_users']);
}
public function save($commit=true)
{
if (!$this->isValid()) {

View File

@ -46,6 +46,16 @@ class IDF_Form_UpdateUpload extends Pluf_Form
'size' => 67,
),
));
$this->fields['changelog'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Description'),
'initial' => $this->upload->changelog,
'widget' => 'Pluf_Form_Widget_TextareaInput',
'widget_attrs' => array(
'cols' => 58,
'rows' => 13,
),
));
$tags = $this->upload->get_tags_list();
for ($i=1;$i<7;$i++) {
$initial = '';
@ -132,6 +142,7 @@ class IDF_Form_UpdateUpload extends Pluf_Form
}
// Create the upload
$this->upload->summary = trim($this->cleaned_data['summary']);
$this->upload->changelog = trim($this->cleaned_data['changelog']);
$this->upload->modif_dtime = gmdate('Y-m-d H:i:s');
$this->upload->update();
$this->upload->batchAssoc('IDF_Tag', $tags);

View File

@ -44,6 +44,16 @@ class IDF_Form_Upload extends Pluf_Form
'size' => 67,
),
));
$this->fields['changelog'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Description'),
'initial' => '',
'widget' => 'Pluf_Form_Widget_TextareaInput',
'widget_attrs' => array(
'cols' => 58,
'rows' => 13,
),
));
$this->fields['file'] = new Pluf_Form_Field_File(
array('required' => true,
'label' => __('File'),
@ -155,6 +165,7 @@ class IDF_Form_Upload extends Pluf_Form
$upload->project = $this->project;
$upload->submitter = $this->user;
$upload->summary = trim($this->cleaned_data['summary']);
$upload->changelog = trim($this->cleaned_data['changelog']);
$upload->file = $this->cleaned_data['file'];
$upload->filesize = filesize(Pluf::f('upload_path').'/'.$this->project->shortname.'/files/'.$this->cleaned_data['file']);
$upload->downloads = 0;

View File

@ -193,7 +193,7 @@ class IDF_Issue extends Pluf_Model
$ic = (in_array($this->status, $request->project->getTagIdsByStatus('closed'))) ? 'issue-c' : 'issue-o';
$out .= sprintf(__('<a href="%1$s" class="%2$s" title="View issue">Issue %3$d</a>, %4$s'), $url, $ic, $this->id, Pluf_esc($this->summary)).'</td>';
$out .= "\n".'<tr class="extra"><td colspan="2">
<div class="helptext right">'.sprintf(__('Creation of <a href="%s" class="%s">issue&nbsp;%d</a>'), $url, $ic, $this->id).', '.__('by').' '.$user.'</div></td></tr>';
<div class="helptext right">'.sprintf(__('Creation of <a href="%s" class="%s">issue&nbsp;%d</a>, by %s'), $url, $ic, $this->id, $user).'</div></td></tr>';
return Pluf_Template::markSafe($out);
}

View File

@ -170,7 +170,7 @@ class IDF_IssueComment extends Pluf_Model
$out .= "\n".'<tr class="extra"><td colspan="2">
<div class="helptext right">'.sprintf(__('Comment on <a href="%s" class="%s">issue&nbsp;%d</a>'), $url, $ic, $issue->id).', '.__('by').' '.$user.'</div></td></tr>';
<div class="helptext right">'.sprintf(__('Comment on <a href="%s" class="%s">issue&nbsp;%d</a>, by %s'), $url, $ic, $issue->id, $user).'</div></td></tr>';
return Pluf_Template::markSafe($out);
}

View File

@ -123,4 +123,9 @@ class IDF_IssueFile extends Pluf_Model
}
$this->modif_dtime = gmdate('Y-m-d H:i:s');
}
function preDelete()
{
@unlink(Pluf::f('upload_issue_path').'/'.$this->attachment);
}
}

View File

@ -0,0 +1,52 @@
<?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) 2008 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 ***** */
/**
* Add the DB based Git cache.
*/
function IDF_Migrations_11GitCache_up($params=null)
{
$models = array(
'IDF_Scm_Cache_Git',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->createTables();
}
}
function IDF_Migrations_11GitCache_down($params=null)
{
$models = array(
'IDF_Scm_Cache_Git',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->dropTables();
}
}

View File

@ -0,0 +1,55 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008 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 ***** */
/**
* Add the private column for the project.
*/
function IDF_Migrations_12DownloadDesc_up($params=null)
{
$table = Pluf::factory('IDF_Upload')->getSqlTable();
$sql = array();
$sql['PostgreSQL'] = 'ALTER TABLE '.$table.' ADD COLUMN "changelog" TEXT DEFAULT \'\'';
$sql['MySQL'] = 'ALTER TABLE '.$table.' ADD COLUMN `changelog` LONGTEXT DEFAULT \'\'';
$db = Pluf::db();
$engine = Pluf::f('db_engine');
if (!isset($sql[$engine])) {
throw new Exception('SQLite complex migration not supported.');
}
$db->execute($sql[$engine]);
}
function IDF_Migrations_12DownloadDesc_down($params=null)
{
$table = Pluf::factory('IDF_Upload')->getSqlTable();
$sql = array();
$sql['PostgreSQL'] = 'ALTER TABLE '.$table.' DROP COLUMN "changelog"';
$sql['MySQL'] = 'ALTER TABLE '.$table.' DROP COLUMN `changelog`';
$db = Pluf::db();
$engine = Pluf::f('db_engine');
if (!isset($sql[$engine])) {
throw new Exception('SQLite complex migration not supported.');
}
$db->execute($sql[$engine]);
}

View File

@ -46,6 +46,7 @@ function IDF_Migrations_Install_setup($params=null)
'IDF_Review_Patch',
'IDF_Review_FileComment',
'IDF_Key',
'IDF_Scm_Cache_Git',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
@ -83,6 +84,7 @@ function IDF_Migrations_Install_teardown($params=null)
$perm = Pluf_Permission::getFromString('IDF.project-authorized-user');
if ($perm) $perm->delete();
$models = array(
'IDF_Scm_Cache_Git',
'IDF_Key',
'IDF_Review_FileComment',
'IDF_Review_Patch',

View File

@ -40,7 +40,7 @@ class IDF_Plugin_SyncGit_Cron
$cmd = Pluf::f('idf_plugin_syncgit_path_gitserve', '/dev/null');
$authorized_keys = Pluf::f('idf_plugin_syncgit_path_authorized_keys', false);
if (false == $authorized_keys) {
throw new Pluf_Exception_SettingError('Setting git_path_authorized_keys not set.');
throw new Pluf_Exception_SettingError('Setting idf_plugin_syncgit_path_authorized_keys not set.');
}
if (!is_writable($authorized_keys)) {
throw new Exception('Cannot create file: '.$authorized_keys);
@ -69,6 +69,44 @@ class IDF_Plugin_SyncGit_Cron
}
}
/**
* Remove orphan repositories.
*/
public static function removeOrphanRepositories()
{
$path = Pluf::f('idf_plugin_syncgit_base_repositories', '/home/git/repositories');
if (!is_dir($path) || is_link($path)) {
throw new Pluf_Exception_SettingError(sprintf(
'Directory %s does not exist! Setting "idf_plugin_syncgit_base_repositories not set.',
$path));
}
if (!is_writable($path)) {
throw new Exception(sprintf('Repository %s is not writable.', $path));
}
$projects = array();
foreach (Pluf::factory('IDF_Project')->getList() as $project) {
$projects[] = $project->shortname;
}
unset($project);
$it = new DirectoryIterator($path);
$orphans = array();
while ($it->valid()) {
if (!$it->isDot() && $it->isDir() && !in_array(basename($it->getFileName(), '.git'), $projects)) {
$orphans[] = $it->getPathName();
}
$it->next();
}
if (count($orphans)) {
$cmd = Pluf::f('idf_exec_cmd_prefix', '').'rm -rf '.implode(' ', $orphans);
exec($cmd);
while (list(, $project) = each($orphans)) {
if (is_dir($project)) {
throw new Exception(sprintf('Cannot remove %s directory.', $project));
}
}
}
}
/**
* Check if a sync is needed.
*
@ -79,6 +117,9 @@ class IDF_Plugin_SyncGit_Cron
@unlink(Pluf::f('idf_plugin_syncgit_sync_file'));
self::sync();
self::markExport();
if (Pluf::f('idf_plugin_syncgit_remove_orphans', false)) {
self::removeOrphanRepositories();
}
}
}
}

View File

@ -52,6 +52,9 @@ class IDF_Plugin_SyncSvn
case 'Pluf_User::passwordUpdated':
$plug->processSyncPasswd($params['user']);
break;
case 'IDF_Project::preDelete':
$plug->processSvnDelete($params['project']);
break;
}
}
@ -84,6 +87,31 @@ class IDF_Plugin_SyncSvn
return ($return == 0);
}
/**
* Remove the project from the drive and update the access rights.
*
* @param IDF_Project
* @return bool Success
*/
function processSvnDelete($project)
{
if (!Pluf::f('idf_plugin_syncsvn_remove_orphans', false)) {
return;
}
if ($project->getConf()->getVal('scm') != 'svn') {
return false;
}
$this->SyncAccess($project); // exclude $project
$shortname = $project->shortname;
if (false===($svn_path=Pluf::f('idf_plugin_syncsvn_svn_path',false))) {
throw new Pluf_Exception_SettingError("'idf_plugin_syncsvn_svn_path' must be defined in your configuration file.");
}
if (file_exists($svn_path.'/'.$shortname)) {
$cmd = Pluf::f('idf_exec_cmd_prefix', '').'rm -rf '.$svn_path.'/'.$shortname;
exec($cmd);
}
}
/**
* Synchronise an user's password.
*
@ -156,8 +184,10 @@ class IDF_Plugin_SyncSvn
* We rebuild the complete file each time. This is just to be sure
* not to bork the rights when trying to just edit part of the
* file.
*
* @param IDF_Project Possibly exclude a project (null)
*/
function SyncAccess()
function SyncAccess($exclude=null)
{
$authz_file = Pluf::f('idf_plugin_syncsvn_authz_file');
$access_owners = Pluf::f('idf_plugin_syncsvn_access_owners', 'rw');
@ -170,6 +200,9 @@ class IDF_Plugin_SyncSvn
}
$fcontent = '';
foreach (Pluf::factory('IDF_Project')->getList() as $project) {
if ($exclude and $exclude->id == $project->id) {
continue;
}
$conf = new IDF_Conf();
$conf->setProject($project);
if ($conf->getVal('scm') != 'svn' or

View File

@ -349,16 +349,54 @@ class IDF_Project extends Pluf_Model
return new Pluf_Template_ContextVars($tags);
}
/**
* Get the repository size.
*
* @param bool Force to skip the cache (false)
* @return int Size in byte or -1 if not available
*/
public function getRepositorySize($force=false)
{
$last_eval = $this->getConf()->getVal('repository_size_check_date', 0);
if (!$force and $last_eval > time()-86400) {
return $this->getConf()->getVal('repository_size', -1);
}
$scm = IDF_Scm::get($this);
$this->getConf()->setVal('repository_size', $scm->getRepositorySize());
$this->getConf()->setVal('repository_size_check_date', time());
return $this->getConf()->getVal('repository_size', -1);
}
/**
* Get the access url to the repository.
*
* This will return the right url based on the user.
*
* @param Pluf_User The user (null)
*/
public function getSourceAccessUrl($user=null)
{
$right = $this->getConf()->getVal('source_access_rights', 'all');
if (($user == null or $user->isAnonymous())
and $right == 'all' and !$this->private) {
return $this->getRemoteAccessUrl();
}
return $this->getWriteRemoteAccessUrl($user);
}
/**
* Get the remote access url to the repository.
*
* This will always return the anonymous access url.
*/
public function getRemoteAccessUrl()
{
$conf = $this->getConf();
$scm = $conf->getVal('scm', 'git');
$scms = Pluf::f('allowed_scm');
return call_user_func(array($scms[$scm], 'getRemoteAccessUrl'),
Pluf::loadClass($scms[$scm]);
return call_user_func(array($scms[$scm], 'getAnonymousAccessUrl'),
$this);
}
@ -369,13 +407,13 @@ class IDF_Project extends Pluf_Model
* same as the one to read. For example, you do a checkout with
* git-daemon and push with SSH.
*/
public function getWriteRemoteAccessUrl()
public function getWriteRemoteAccessUrl($user)
{
$conf = $this->getConf();
$scm = $conf->getVal('scm', 'git');
$scms = Pluf::f('allowed_scm');
return call_user_func(array($scms[$scm], 'getWriteRemoteAccessUrl'),
$this);
return call_user_func(array($scms[$scm], 'getAuthAccessUrl'),
$this, $user);
}
/**
@ -541,4 +579,42 @@ class IDF_Project extends Pluf_Model
Pluf_Signal::send('IDF_Project::created',
'IDF_Project', $params);
}
/**
* The delete() call do not like circular references and the
* IDF_Tag is creating some. We predelete to solve these issues.
*/
public function preDelete()
{
/**
* [signal]
*
* IDF_Project::preDelete
*
* [sender]
*
* IDF_Project
*
* [description]
*
* This signal allows an application to perform special
* operations at the deletion of a project.
*
* [parameters]
*
* array('project' => $project)
*
*/
$params = array('project' => $this);
Pluf_Signal::send('IDF_Project::preDelete',
'IDF_Project', $params);
$what = array('IDF_Upload', 'IDF_Review', 'IDF_Issue',
'IDF_WikiPage', 'IDF_Commit',
);
foreach ($what as $m) {
foreach (Pluf::factory($m)->getList(array('filter' => 'project='.(int)$this->id)) as $item) {
$item->delete();
}
}
}
}

View File

@ -22,10 +22,55 @@
# ***** END LICENSE BLOCK ***** */
/**
* Manage differents SCM systems
* Manage differents SCM systems.
*
* This is the base class with the different required methods to be
* implemented by the SCMs. Each SCM backend need to extend this
* class. We are not using an interface because this is not really
* needed.
*
* The philosophy behind the interface is not to provide a wrapper
* around the different SCMs but to provide methods to retrieve in the
* most efficient way the informations to be displayed/needed in the
* web interface. This means that each SCM can use the best options,
* including caching to retrieve the informations.
*
* Note on caching: You must not cache ephemeral information like the
* changelog, but you can cache the commit info (except with
* subversion where you can change commit info...). It is ok to do
* some caching for the lifetime of the IDF_Scm object, for example
* not to retrieve several times the list of branches, etc.
*
* All the output of the methods must be serializable. This means that
* if you are parsing XML you need to correctly cast the results as
* string when needed.
*/
class IDF_Scm
{
/**
* String template for consistent error messages.
*/
public $error_tpl = 'Error command "%s" returns code %d and output: %s';
/**
* Path to the repository.
*/
public $repo = '';
/**
* Corresponding project object.
*/
public $project = null;
/**
* Cache storage.
*
* It must only be used to store data for the lifetime of the
* object. For example if you need to get the list of branches in
* several functions, better to try to get from the cache first.
*/
protected $cache = array();
/**
* Returns an instance of the correct scm backend object.
@ -33,7 +78,7 @@ class IDF_Scm
* @param IDF_Project
* @return Object
*/
public static function get($project=null)
public static function get($project)
{
// Get scm type from project conf ; defaults to git
// We will need to cache the factory
@ -43,46 +88,251 @@ class IDF_Scm
}
/**
* Equivalent to exec but with caching.
* Return the size of the repository in bytes.
*
* @param string Command
* @param &array Output
* @param &int Return value
* @return string Last line of the output
* @return int Size in byte, -1 if the size cannot be evaluated.
*/
public static function exec($command, &$output=array(), &$return=0)
public function getRepositorySize()
{
$command = Pluf::f('idf_exec_cmd_prefix', '').$command;
$key = md5($command);
$cache = Pluf_Cache::factory();
if (null === ($res=$cache->get($key))) {
$ll = exec($command, $output, $return);
if ($return != 0 and Pluf::f('debug_scm', false)) {
throw new IDF_Scm_Exception(sprintf('Error when running command: "%s", return code: %d', $command, $return));
}
$cache->set($key, array($ll, $return, $output));
} else {
list($ll, $return, $output) = $res;
}
return $ll;
return -1;
}
/**
* Equivalent to shell_exec but with caching.
* Returns the URL of the git daemon.
*
* @param string Command
* @return string Output of the command
* @param IDF_Project
* @return string URL
*/
public static function shell_exec($command)
public static function getAnonymousAccessUrl($project)
{
$command = Pluf::f('idf_exec_cmd_prefix', '').$command;
$key = md5($command);
$cache = Pluf_Cache::factory();
if (null === ($res=$cache->get($key))) {
$res = shell_exec($command);
$cache->set($key, $res);
throw new Pluf_Exception_NotImplemented();
}
return $res;
/**
* Returns the URL for SSH access
*
* @param IDF_Project
* @param Pluf_User
* @return string URL
*/
public static function getAuthAccessUrl($project, $user)
{
throw new Pluf_Exception_NotImplemented();
}
/**
* Check if the backend is available for display.
*
* @return bool Available
*/
public function isAvailable()
{
throw new Pluf_Exception_NotImplemented();
}
/**
* Check if a revision or commit is valid.
*
* @param string Revision or commit
* @return bool
*/
public function isValidRevision($rev)
{
throw new Pluf_Exception_NotImplemented();
}
/**
* Returns in which branches a commit/path is.
*
* A commit can be in several branches and some of the SCMs are
* managing branches using subfolders (like Subversion).
*
* This means that to know in which branch we are at the moment,
* one needs to have both the path and the commit.
*
* @param string Commit
* @param string Path
* @return array Branches
*/
public function inBranches($commit, $path)
{
throw new Pluf_Exception_NotImplemented();
}
/**
* Returns the list of branches.
*
* The return value must be a branch indexed array with the
* optional path to access the branch as value. For example with
* git you would get (note that some people are using / in the
* name of their git branches):
*
* <pre>
* array('master' => '',
* 'foo-branch' => '',
* 'design/feature1' => '')
* </pre>
*
* But with Subversion, as the branches are managed as subfolder
* with a special folder for trunk, you would get something like:
*
* <pre>
* array('trunk' => 'trunk',
* 'foo-branch' => 'branches/foo-branch',)
* </pre>
*
* @return array Branches
*/
public function getBranches()
{
throw new Pluf_Exception_NotImplemented();
}
/**
* Returns the list of tags.
*
* The format is the same as for the branches.
*
* @see self::getBranches()
*
* @return array Tags
*/
public function getTags()
{
throw new Pluf_Exception_NotImplemented();
}
/**
* Returns the main branch.
*
* The main branch is the one displayed by default. For example
* master, trunk or tip.
*
* @return string
*/
public function getMainBranch()
{
throw new Pluf_Exception_NotImplemented();
}
/**
* Returns the list of files in a given folder.
*
* The list is an array of standard class objects with attributes
* for each file/directory/external element.
*
* This is the most important method of the SCM backend as this is
* the one conveying the speed feeling of the application. All the
* dirty optimization tricks are allowed there.
*
* @param string Revision or commit
* @param string Folder ('/')
* @param string Branch (null)
* @return array
*/
public function getTree($rev, $folder='/', $branch=null)
{
throw new Pluf_Exception_NotImplemented();
}
/**
* Get commit details.
*
* @param string Commit or revision number
* @param bool Get commit diff (false)
* @return stdClass
*/
public function getCommit($commit, $getdiff=false)
{
throw new Pluf_Exception_NotImplemented();
}
/**
* Get latest changes.
*
* It default to the main branch. If possible you should code in a
* way to avoid repetitive calls to getCommit. Try to be
* efficient.
*
* @param string Branch (null)
* @param int Number of changes (25)
* @return array List of commits
*/
public function getChangeLog($branch=null, $n=10)
{
throw new Pluf_Exception_NotImplemented();
}
/**
* Given the string describing the author from the log find the
* author in the database.
*
* If the input is an array, it will return an array of results.
*
* @param mixed string/array Author
* @return mixed Pluf_User or null or array
*/
public function findAuthor($author)
{
throw new Pluf_Exception_NotImplemented();
}
/**
* Given a revision and a file path, retrieve the file content.
*
* The $cmd_only parameter is to only request the command that is
* used to get the file content. This is used when downloading a
* file at a given revision as it can be passed to a
* Pluf_HTTP_Response_CommandPassThru reponse. This allows to
* stream a large response without buffering it in memory.
*
* The file definition is coming from getPathInfo().
*
* @see self::getPathInfo()
*
* @param stdClass File definition
* @param bool Returns command only (false)
* @return string File content
*/
public function getFile($def, $cmd_only=false)
{
throw new Pluf_Exception_NotImplemented();
}
/**
* Get information about a file or a path.
*
* @param string File or path
* @param string Revision (null)
* @return mixed False or stdClass with info
*/
public function getPathInfo($file, $rev=null)
{
throw new Pluf_Exception_NotImplemented();
}
/**
* Given a revision and possible path returns additional properties.
*
* @param string Revision
* @param string Path ('')
* @return mixed null or array of properties
*/
public function getProperties($rev, $path='')
{
return null;
}
/**
* Generate the command to create a zip archive at a given commit.
*
* @param string Commit
* @param string Prefix ('repository/')
* @return string Command
*/
public function getArchiveCommand($commit, $prefix='repository/')
{
throw new Pluf_Exception_NotImplemented();
}
/**
@ -95,13 +345,13 @@ class IDF_Scm
$key = 'IDF_Scm:'.$project->shortname.':lastsync';
if (null === ($res=$cache->get($key))) {
$scm = IDF_Scm::get($project);
foreach ($scm->getBranches() as $branche) {
foreach ($scm->getChangeLog($branche, 25) as $change) {
if ($scm->isAvailable()) {
foreach ($scm->getChangeLog($scm->getMainBranch(), 25) as $change) {
IDF_Commit::getOrAdd($change, $project);
}
}
$cache->set($key, true, (int)(Pluf::f('cache_timeout', 300)/2));
}
}
}
}

134
src/IDF/Scm/Cache/Git.php Normal file
View File

@ -0,0 +1,134 @@
<?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) 2008 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 ***** */
/**
* This class implements the cache storage for the Git commits.
*
* The storage is simple. Each commit is linked to a project to drop
* the cache when the project is dropped. The key is the commit hash
* and the data is the date, author and one line title of information.
*
* A clean interface is available to bulk set/get a series of commit
* info with the minimum of SQL queries. The goal is to be fast.
*/
class IDF_Scm_Cache_Git extends Pluf_Model
{
public $_model = __CLASS__;
/**
* The current project to limit the search to.
*/
public $_project = null;
/**
* Store in the cache blob infos.
*
* The info is an array of stdClasses, with hash, date, title and
* author properties.
*
* @param array Blob infos
*/
public function store($infos)
{
foreach ($infos as $blob) {
$cache = new IDF_Scm_Cache_Git();
$cache->project = $this->_project;
$cache->githash = $blob->hash;
$blob->title = IDF_Commit::toUTF8($blob->title);
$cache->content = $blob->date.chr(31).$blob->author.chr(31).$blob->title;
$sql = new Pluf_SQL('project=%s AND githash=%s',
array($this->_project->id, $blob->hash));
if (0 == Pluf::factory(__CLASS__)->getCount(array('filter' => $sql->gen()))) {
$cache->create();
}
}
}
/**
* Get for the given hashes the corresponding date, title and
* author.
*
* It returns an hash indexed array with the info. If an hash is
* not in the db, the key is not set.
*
* Note that the hashes must always come from internal tools.
*
* @param array Hashes to get info
* @return array Blob infos
*/
public function retrieve($hashes)
{
$res = array();
$db = $this->getDbConnection();
$hashes = array_map(array($db, 'esc'), $hashes);
$sql = new Pluf_SQL('project=%s AND githash IN ('.implode(', ', $hashes).')',
array($this->_project->id));
foreach (Pluf::factory(__CLASS__)->getList(array('filter' => $sql->gen())) as $blob) {
$tmp = split(chr(31), $blob->content, 3);
$res[$blob->githash] = (object) array(
'hash' => $blob->githash,
'date' => $tmp[0],
'title' => $tmp[2],
'author' => $tmp[1],
);
}
return $res;
}
/**
* The storage is composed of 4 columns, id, project, hash and the
* raw data.
*/
function init()
{
$this->_a['table'] = 'idf_scm_cache_git';
$this->_a['model'] = __CLASS__;
$this->_a['cols'] = array(
// It is mandatory to have an "id" column.
'id' =>
array(
'type' => 'Pluf_DB_Field_Sequence',
'blank' => true,
),
'project' =>
array(
'type' => 'Pluf_DB_Field_Foreignkey',
'model' => 'IDF_Project',
'blank' => false,
),
'githash' =>
array(
'type' => 'Pluf_DB_Field_Varchar',
'blank' => false,
'size' => 40,
'index' => true,
),
'content' =>
array(
'type' => 'Pluf_DB_Field_Text',
'blank' => false,
),
);
}
}

View File

@ -25,14 +25,153 @@
* Git utils.
*
*/
class IDF_Scm_Git
class IDF_Scm_Git extends IDF_Scm
{
public $repo = '';
public $mediumtree_fmt = 'commit %H%nAuthor: %an <%ae>%nTree: %T%nDate: %ai%n%n%s%n%n%b';
public function __construct($repo)
/* ============================================== *
* *
* Common Methods Implemented By All The SCMs *
* *
* ============================================== */
public function __construct($repo, $project=null)
{
$this->repo = $repo;
$this->project = $project;
}
public function getRepositorySize()
{
$cmd = Pluf::f('idf_exec_cmd_prefix', '').'du -sk '
.escapeshellarg($this->repo);
$out = split(' ', shell_exec($cmd), 2);
return (int) $out[0]*1024;
}
public function isAvailable()
{
try {
$branches = $this->getBranches();
} catch (IDF_Scm_Exception $e) {
return false;
}
return (count($branches) > 0);
}
public function getBranches()
{
if (isset($this->cache['branches'])) {
return $this->cache['branches'];
}
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf('GIT_DIR=%s '.Pluf::f('git_path', 'git').' branch',
escapeshellarg($this->repo));
exec($cmd, $out, $return);
if ($return != 0) {
throw new IDF_Scm_Exception(sprintf($this->error_tpl,
$cmd, $return,
implode("\n", $out)));
}
$res = array();
foreach ($out as $b) {
$b = substr($b, 2);
if (false !== strpos($b, '/')) {
$res[$this->getCommit($b)->commit] = $b;
} else {
$res[$b] = '';
}
}
$this->cache['branches'] = $res;
return $res;
}
public function getMainBranch()
{
$possible = array('master', 'main', 'trunk', 'local');
$branches = array_keys($this->getBranches());
foreach ($possible as $p) {
if (in_array($p, $branches)) {
return $p;
}
}
return (isset($branches[0])) ? $branches[0] : 'master';
}
/**
* Note: Running the `git branch --contains $commit` is
* theoritically the best way to do it, until you figure out that
* you cannot cache the result and that it takes several seconds
* to execute on a big tree.
*/
public function inBranches($commit, $path)
{
return (in_array($commit, array_keys($this->getBranches())))
? array($commit) : array();
}
/**
* Git "tree" is not the same as the tree we get here.
*
* With git each commit object stores a related tree object. This
* tree is basically providing what is in the given folder at the
* given commit. It looks something like that:
*
* <pre>
* 100644 blob bcd155e609c51b4651aab9838b270cce964670af AUTHORS
* 100644 blob 87b44c5c7df3cc90c031317c1ac8efcfd8a13631 COPYING
* 100644 blob 2a0f899cbfe33ea755c343b06a13d7de6c22799f INSTALL.mdtext
* 040000 tree 2f469c4c5318aa4ad48756874373370f6112f77b doc
* 040000 tree 911e0bd2706f0069b04744d6ef41353faf06a0a7 logo
* </pre>
*
* You can then follow what is in the given folder (let say doc)
* by using the hash.
*
* This means that you will have not to confuse the git tree and
* the output tree in the following method.
*
* @see http://www.kernel.org/pub/software/scm/git/docs/git-ls-tree.html
*
*/
public function getTree($commit, $folder='/', $branch=null)
{
$folder = ($folder == '/') ? '' : $folder;
// now we grab the info about this commit including its tree.
if (false == ($co = $this->getCommit($commit))) {
return false;
}
if ($folder) {
// As we are limiting to a given folder, we need to find
// the tree corresponding to this folder.
$tinfo = $this->getTreeInfo($commit, $folder);
if (isset($tinfo[0]) and $tinfo[0]->type == 'tree') {
$tree = $tinfo[0]->hash;
} else {
throw new Exception(sprintf(__('Folder %1$s not found in commit %2$s.'), $folder, $commit));
}
} else {
$tree = $co->tree;
}
$res = array();
foreach ($this->getTreeInfo($tree) as $file) {
// Now we grab the files in the current tree with as much
// information as possible.
if ($file->type == 'blob') {
$file->date = $co->date;
$file->log = '----';
$file->author = 'Unknown';
}
$file->fullpath = ($folder) ? $folder.'/'.$file->file : $file->file;
if ($file->type == 'commit') {
// We have a submodule
$file = $this->getSubmodule($file, $commit);
}
$res[] = $file;
}
// Grab the details for each blob and return the list.
return $this->getTreeDetails($res);
}
/**
@ -54,25 +193,12 @@ class IDF_Scm_Git
return ($users->count() > 0) ? $users[0] : null;
}
/**
* Returns the URL of the git daemon.
*
* @param IDF_Project
* @return string URL
*/
public static function getRemoteAccessUrl($project)
public static function getAnonymousAccessUrl($project)
{
return sprintf(Pluf::f('git_remote_url'), $project->shortname);
}
/**
* Returns the URL for SSH access
*
* @param IDF_Project
* @return string URL
*/
public static function getWriteRemoteAccessUrl($project)
public static function getAuthAccessUrl($project, $user)
{
return sprintf(Pluf::f('git_write_remote_url'), $project->shortname);
}
@ -86,114 +212,53 @@ class IDF_Scm_Git
public static function factory($project)
{
$rep = sprintf(Pluf::f('git_repositories'), $project->shortname);
return new IDF_Scm_Git($rep);
return new IDF_Scm_Git($rep, $project);
}
public function isValidRevision($commit)
{
return ('commit' == $this->testHash($commit));
}
/**
* Test a given object hash.
*
* @param string Object hash.
* @param null to be svn client compatible
* @return mixed false if not valid or 'blob', 'tree', 'commit'
*/
public function testHash($hash, $dummy=null)
public function testHash($hash)
{
$cmd = sprintf('GIT_DIR=%s '.Pluf::f('git_path', 'git').' cat-file -t %s',
escapeshellarg($this->repo),
escapeshellarg($hash));
$ret = 0; $out = array();
IDF_Scm::exec($cmd, $out, $ret);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
exec($cmd, $out, $ret);
if ($ret != 0) return false;
return trim($out[0]);
}
/**
* Given a commit hash returns an array of files in it.
*
* A file is a class with the following properties:
*
* 'perm', 'type', 'size', 'hash', 'file'
*
* @param string Commit ('HEAD')
* @param string Base folder ('')
* @return array
*/
public function filesAtCommit($commit='HEAD', $folder='')
{
// now we grab the info about this commit including its tree.
$co = $this->getCommit($commit);
if ($folder) {
// As we are limiting to a given folder, we need to find
// the tree corresponding to this folder.
$found = false;
foreach ($this->getTreeInfo($co->tree, true, $folder) as $file) {
if ($file->type == 'tree' and $file->file == $folder) {
$found = true;
$tree = $file->hash;
break;
}
}
if (!$found) {
throw new Exception(sprintf(__('Folder %1$s not found in commit %2$s.'), $folder, $commit));
}
} else {
$tree = $co->tree;
}
$res = array();
// get the raw log corresponding to this commit to find the
// origin of each file.
$rawlog = array();
$cmd = sprintf('GIT_DIR=%s '.Pluf::f('git_path', 'git').' log --raw --abbrev=40 --pretty=oneline -5000 %s',
escapeshellarg($this->repo), escapeshellarg($commit));
IDF_Scm::exec($cmd, $rawlog);
// We reverse the log to be able to use a fixed efficient
// regex without back tracking.
$rawlog = implode("\n", array_reverse($rawlog));
foreach ($this->getTreeInfo($tree, false) as $file) {
// Now we grab the files in the current tree with as much
// information as possible.
$matches = array();
if ($file->type == 'blob' and preg_match('/^\:\d{6} \d{6} [0-9a-f]{40} '.$file->hash.' .*^([0-9a-f]{40})/msU',
$rawlog, $matches)) {
$fc = $this->getCommit($matches[1]);
$file->date = $fc->date;
$file->log = $fc->title;
$file->author = $fc->author;
} else if ($file->type == 'blob') {
$file->date = $co->date;
$file->log = '----';
$file->author = 'Unknown';
}
$file->fullpath = ($folder) ? $folder.'/'.$file->file : $file->file;
if ($file->type == 'commit') {
// We have a submodule
$file = $this->getSubmodule($file, $commit);
}
$res[] = $file;
}
return $res;
}
/**
* Get the tree info.
*
* @param string Tree hash
* @param bool Do we recurse in subtrees (true)
* @param string Folder in which we want to get the info ('')
* @return array Array of file information.
*/
public function getTreeInfo($tree, $recurse=true, $folder='')
public function getTreeInfo($tree, $folder='')
{
if ('tree' != $this->testHash($tree)) {
if (!in_array($this->testHash($tree), array('tree', 'commit'))) {
throw new Exception(sprintf(__('Not a valid tree: %s.'), $tree));
}
$cmd_tmpl = 'GIT_DIR=%s '.Pluf::f('git_path', 'git').' ls-tree%s -t -l %s %s';
$cmd = sprintf($cmd_tmpl,
escapeshellarg($this->repo),
($recurse) ? ' -r' : '',
$cmd_tmpl = 'GIT_DIR=%s '.Pluf::f('git_path', 'git').' ls-tree -l %s %s';
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf($cmd_tmpl, escapeshellarg($this->repo),
escapeshellarg($tree), escapeshellarg($folder));
$out = array();
$res = array();
IDF_Scm::exec($cmd, $out);
exec($cmd, $out);
foreach ($out as $line) {
list($perm, $type, $hash, $size, $file) = preg_split('/ |\t/', $line, 5, PREG_SPLIT_NO_EMPTY);
$res[] = (object) array('perm' => $perm, 'type' => $type,
@ -211,65 +276,46 @@ class IDF_Scm_Git
* @param string Commit ('HEAD')
* @return false Information
*/
public function getFileInfo($totest, $commit='HEAD')
public function getPathInfo($totest, $commit='HEAD')
{
$cmd_tmpl = 'GIT_DIR=%s '.Pluf::f('git_path', 'git').' ls-tree -r -t -l %s';
$cmd = sprintf($cmd_tmpl,
escapeshellarg($this->repo),
escapeshellarg($commit));
$out = array();
IDF_Scm::exec($cmd, $out);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
exec($cmd, $out);
foreach ($out as $line) {
list($perm, $type, $hash, $size, $file) = preg_split('/ |\t/', $line, 5, PREG_SPLIT_NO_EMPTY);
if ($totest == $file) {
$pathinfo = pathinfo($file);
return (object) array('perm' => $perm, 'type' => $type,
'size' => $size, 'hash' => $hash,
'file' => $file);
'fullpath' => $file,
'file' => $pathinfo['basename']);
}
}
return false;
}
/**
* Get a blob.
*
* @param string request_file_info
* @param null to be svn client compatible
* @return string Raw blob
*/
public function getBlob($request_file_info, $dummy=null)
public function getFile($def, $cmd_only=false)
{
return shell_exec(sprintf(Pluf::f('idf_exec_cmd_prefix', '').
$cmd = sprintf(Pluf::f('idf_exec_cmd_prefix', '').
'GIT_DIR=%s '.Pluf::f('git_path', 'git').' cat-file blob %s',
escapeshellarg($this->repo),
escapeshellarg($request_file_info->hash)));
escapeshellarg($def->hash));
return ($cmd_only) ? $cmd : shell_exec($cmd);
}
/**
* Get the branches.
*
* @return array Branches.
*/
public function getBranches()
{
$out = array();
IDF_Scm::exec(sprintf('GIT_DIR=%s '.Pluf::f('git_path', 'git').' branch',
escapeshellarg($this->repo)), $out);
$res = array();
foreach ($out as $b) {
$res[] = substr($b, 2);
}
return $res;
}
/**
* Get commit details.
*
* @param string Commit ('HEAD').
* @param bool Get commit diff (false).
* @return array Changes.
* @param string Commit
* @param bool Get commit diff (false)
* @return array Changes
*/
public function getCommit($commit='HEAD', $getdiff=false)
public function getCommit($commit, $getdiff=false)
{
if ($getdiff) {
$cmd = sprintf('GIT_DIR=%s '.Pluf::f('git_path', 'git').' show --date=iso --pretty=format:%s %s',
@ -283,7 +329,11 @@ class IDF_Scm_Git
escapeshellarg($commit));
}
$out = array();
IDF_Scm::exec($cmd, $out);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
exec($cmd, $out, $ret);
if ($ret != 0 or count($out) == 0) {
return false;
}
$log = array();
$change = array();
$inchange = false;
@ -315,7 +365,8 @@ class IDF_Scm_Git
"'commit %H%n'",
escapeshellarg($commit));
$out = array();
IDF_Scm::exec($cmd, $out);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
exec($cmd, $out);
$affected = count($out) - 2;
$added = 0;
$removed = 0;
@ -347,7 +398,8 @@ class IDF_Scm_Git
escapeshellarg($this->repo), $n, $this->mediumtree_fmt,
escapeshellarg($commit));
$out = array();
IDF_Scm::exec($cmd, $out);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
exec($cmd, $out);
return self::parseLog($out, 4);
}
@ -355,14 +407,12 @@ class IDF_Scm_Git
* Parse the log lines of a --pretty=medium log output.
*
* @param array Lines.
* @param int Number of lines in the headers (3)
* @return array Change log.
*/
public static function parseLog($lines, $hdrs=3)
public static function parseLog($lines)
{
$res = array();
$c = array();
$hdrs += 2;
$inheads = true;
$next_is_title = false;
foreach ($lines as $line) {
@ -401,19 +451,12 @@ class IDF_Scm_Git
continue;
}
}
$c['full_message'] = trim($c['full_message']);
$c['full_message'] = !empty($c['full_message']) ? trim($c['full_message']) : '';
$res[] = (object) $c;
return $res;
}
/**
* Generate the command to create a zip archive at a given commit.
*
* @param string Commit
* @param string Prefix ('git-repo-dump')
* @return string Command
*/
public function getArchiveCommand($commit, $prefix='git-repo-dump/')
public function getArchiveCommand($commit, $prefix='repository/')
{
return sprintf(Pluf::f('idf_exec_cmd_prefix', '').
'GIT_DIR=%s '.Pluf::f('git_path', 'git').' archive --format=zip --prefix=%s %s',
@ -440,15 +483,232 @@ class IDF_Scm_Git
public function getSubmodule($file, $commit)
{
$file->type = 'extern';
$info = $this->getFileInfo('.gitmodules', $commit);
$file->extern = '';
$info = $this->getPathInfo('.gitmodules', $commit);
if ($info == false) {
return $file;
}
$gitmodules = $this->getBlob($info);
$gitmodules = $this->getFile($info);
if (preg_match('#\[submodule\s+\"'.$file->fullpath.'\"\]\s+path\s=\s(\S+)\s+url\s=\s(\S+)#mi', $gitmodules, $matches)) {
$file->extern = $matches[2];
}
return $file;
}
/**
* Foreach file in the tree, find the details.
*
* @param array Tree information
* @return array Updated tree information
*/
public function getTreeDetails($tree)
{
$n = count($tree);
$details = array();
for ($i=0;$i<$n;$i++) {
if ($tree[$i]->type == 'blob') {
$details[$tree[$i]->hash] = $i;
}
}
if (!count($details)) {
return $tree;
}
$res = $this->getCachedBlobInfo($details);
$toapp = array();
foreach ($details as $blob => $idx) {
if (isset($res[$blob])) {
$tree[$idx]->date = $res[$blob]->date;
$tree[$idx]->log = $res[$blob]->title;
$tree[$idx]->author = $res[$blob]->author;
} else {
$toapp[$blob] = $idx;
}
}
if (count($toapp)) {
$res = $this->appendBlobInfoCache($toapp);
foreach ($details as $blob => $idx) {
if (isset($res[$blob])) {
$tree[$idx]->date = $res[$blob]->date;
$tree[$idx]->log = $res[$blob]->title;
$tree[$idx]->author = $res[$blob]->author;
}
}
}
return $tree;
}
/**
* Append build info cache.
*
* The append method tries to get only the necessary details, so
* instead of going through all the commits one at a time, it will
* try to find a smarter way with regex.
*
* @see self::buildBlobInfoCache
*
* @param array The blob for which we need the information
* @return array The information
*/
public function appendBlobInfoCache($blobs)
{
$rawlog = array();
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf('GIT_DIR=%s '.Pluf::f('git_path', 'git').' log --raw --abbrev=40 --pretty=oneline -5000 --skip=%%s',
escapeshellarg($this->repo));
$skip = 0;
$res = array();
exec(sprintf($cmd, $skip), $rawlog);
while (count($rawlog) and count($blobs)) {
$rawlog = implode("\n", array_reverse($rawlog));
foreach ($blobs as $blob => $idx) {
if (preg_match('/^\:\d{6} \d{6} [0-9a-f]{40} '
.$blob.' .*^([0-9a-f]{40})/msU',
$rawlog, $matches)) {
$fc = $this->getCommit($matches[1]);
$res[$blob] = (object) array('hash' => $blob,
'date' => $fc->date,
'title' => $fc->title,
'author' => $fc->author);
unset($blobs[$blob]);
}
}
$rawlog = array();
$skip += 5000;
if ($skip > 20000) {
// We are in the case of the import of a big old
// repository, we can store as unknown the commit info
// not to try to retrieve them each time.
foreach ($blobs as $blob => $idx) {
$res[$blob] = (object) array('hash' => $blob,
'date' => '0',
'title' => '----',
'author' => 'Unknown');
}
break;
}
exec(sprintf($cmd, $skip), $rawlog);
}
$this->cacheBlobInfo($res);
return $res;
}
/**
* Build the blob info cache.
*
* We build the blob info cache 500 commits at a time.
*/
public function buildBlobInfoCache()
{
$rawlog = array();
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf('GIT_DIR=%s '.Pluf::f('git_path', 'git').' log --raw --abbrev=40 --pretty=oneline -500 --skip=%%s',
escapeshellarg($this->repo));
$skip = 0;
exec(sprintf($cmd, $skip), $rawlog);
while (count($rawlog)) {
$commit = '';
$data = array();
foreach ($rawlog as $line) {
if (substr($line, 0, 1) != ':') {
$commit = $this->getCommit(substr($line, 0, 40));
continue;
}
$blob = substr($line, 56, 40);
$data[] = (object) array('hash' => $blob,
'date' => $commit->date,
'title' => $commit->title,
'author' => $commit->author);
}
$this->cacheBlobInfo($data);
$rawlog = array();
$skip += 500;
exec(sprintf($cmd, $skip), $rawlog);
}
}
/**
* Get blob info.
*
* When we display the tree, we want to know when a given file was
* created, who was the author and at which date. This is a very
* slow operation for git as we need to go through the full
* history, find when then blob was introduced, then grab the
* corresponding commit. This is why we need a cache.
*
* @param array List as keys of blob hashs to get info for
* @return array Hash indexed results, when not found not set
*/
public function getCachedBlobInfo($hashes)
{
$cache = new IDF_Scm_Cache_Git();
$cache->_project = $this->project;
return $cache->retrieve(array_keys($hashes));
}
/**
* Cache blob info.
*
* Given a series of blob info, cache them.
*
* @param array Blob info
* @return bool Success
*/
public function cacheBlobInfo($info)
{
$cache = new IDF_Scm_Cache_Git();
$cache->_project = $this->project;
return $cache->store($info);
}
public function getFileCachedBlobInfo($hashes)
{
$res = array();
$cache = Pluf::f('tmp_folder').'/IDF_Scm_Git-'.md5($this->repo).'.cache.db';
if (!file_exists($cache)) {
return $res;
}
$data = file_get_contents($cache);
if (false === $data) {
return $res;
}
$data = split(chr(30), $data);
foreach ($data as $rec) {
if (isset($hashes[substr($rec, 0, 40)])) {
$tmp = split(chr(31), substr($rec, 40), 3);
$res[substr($rec, 0, 40)] =
(object) array('hash' => substr($rec, 0, 40),
'date' => $tmp[0],
'title' => $tmp[2],
'author' => $tmp[1]);
}
}
return $res;
}
/**
* File cache blob info.
*
* Given a series of blob info, cache them.
*
* @param array Blob info
* @return bool Success
*/
public function fileCacheBlobInfo($info)
{
// Prepare the data
$data = array();
foreach ($info as $file) {
$data[] = $file->hash.$file->date.chr(31).$file->author.chr(31).$file->title;
}
$data = implode(chr(30), $data).chr(30);
$cache = Pluf::f('tmp_folder').'/IDF_Scm_Git-'.md5($this->repo).'.cache.db';
$fp = fopen($cache, 'ab');
if ($fp) {
flock($fp, LOCK_EX);
fwrite($fp, $data, strlen($data));
fclose($fp); // releases the lock too
return true;
}
return false;
}
}

View File

@ -25,22 +25,33 @@
* Mercurial utils.
*
*/
class IDF_Scm_Mercurial
class IDF_Scm_Mercurial extends IDF_Scm
{
public $repo = '';
public function __construct($repo)
public function __construct($repo, $project=null)
{
$this->repo = $repo;
$this->project = $project;
}
public function getRepositorySize()
{
$cmd = Pluf::f('idf_exec_cmd_prefix', '').'du -sk '
.escapeshellarg($this->repo);
$out = split(' ', shell_exec($cmd), 2);
return (int) $out[0]*1024;
}
public static function factory($project)
{
$rep = sprintf(Pluf::f('mercurial_repositories'), $project->shortname);
return new IDF_Scm_Mercurial($rep, $project);
}
public function isAvailable()
{
return true;
}
/**
* Given the string describing the author from the log find the
* author in the database.
*
* @param string Author
* @return mixed Pluf_User or null
*/
public function findAuthor($author)
{
// We extract the email.
@ -53,27 +64,29 @@ class IDF_Scm_Mercurial
return ($users->count() > 0) ? $users[0] : null;
}
/**
* Returns the URL of the git daemon.
*
* @param IDF_Project
* @return string URL
*/
public static function getRemoteAccessUrl($project)
public function getMainBranch()
{
return 'tip';
}
public static function getAnonymousAccessUrl($project)
{
return sprintf(Pluf::f('mercurial_remote_url'), $project->shortname);
}
/**
* Returns this object correctly initialized for the project.
*
* @param IDF_Project
* @return IDF_Scm_Git
*/
public static function factory($project)
public static function getAuthAccessUrl($project, $user)
{
$rep = sprintf(Pluf::f('mercurial_repositories'), $project->shortname);
return new IDF_Scm_Mercurial($rep);
return sprintf(Pluf::f('mercurial_remote_url'), $project->shortname);
}
public function isValidRevision($rev)
{
$cmd = sprintf(Pluf::f('hg_path', 'hg').' log -R %s -r %s',
escapeshellarg($this->repo),
escapeshellarg($rev));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
exec($cmd, $out, $ret);
return ($ret == 0);
}
/**
@ -90,24 +103,15 @@ class IDF_Scm_Mercurial
escapeshellarg($hash));
$ret = 0;
$out = array();
IDF_Scm::exec($cmd, $out, $ret);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
exec($cmd, $out, $ret);
return ($ret != 0) ? false : 'commit';
}
/**
* Given a commit hash returns an array of files in it.
*
* A file is a class with the following properties:
*
* 'perm', 'type', 'size', 'hash', 'file'
*
* @param string Commit ('HEAD')
* @param string Base folder ('')
* @return array
*/
public function filesAtCommit($commit='tip', $folder='')
public function getTree($commit, $folder='/', $branch=null)
{
// now we grab the info about this commit including its tree.
$folder = ($folder == '/') ? '' : $folder;
$co = $this->getCommit($commit);
if ($folder) {
// As we are limiting to a given folder, we need to find
@ -143,26 +147,24 @@ class IDF_Scm_Mercurial
$cmd = sprintf($cmd_tmpl, escapeshellarg($this->repo), $tree, ($recurse) ? '' : '');
$out = array();
$res = array();
IDF_Scm::exec($cmd, $out);
$out_hack = array();
foreach ($out as $line) {
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
exec($cmd, $out);
$tmp_hack = array();
while (null !== ($line = array_pop($out))) {
list($hash, $perm, $exec, $file) = preg_split('/ |\t/', $line, 4);
$file = trim($file);
$dir = explode('/', $file, -1);
$tmp = '';
for ($i=0; $i < count($dir); $i++) {
for ($i=0, $n=count($dir); $i<$n; $i++) {
if ($i > 0) {
$tmp .= '/';
}
$tmp .= $dir[$i];
if (!in_array("empty\t000\t\t$tmp/", $out_hack))
$out_hack[] = "empty\t000\t\t$tmp/";
if (!isset($tmp_hack["empty\t000\t\t$tmp/"])) {
$out[] = "empty\t000\t\t$tmp/";
$tmp_hack["empty\t000\t\t$tmp/"] = 1;
}
$out_hack[] = "$hash\t$perm\t$exec\t$file";
}
foreach ($out_hack as $line) {
list($hash, $perm, $exec, $file) = preg_split('/ |\t/', $line, 4);
$file = trim($file);
if (preg_match('/^(.*)\/$/', $file, $match)) {
$type = 'tree';
$file = $match[1];
@ -187,40 +189,38 @@ class IDF_Scm_Mercurial
return $res;
}
/**
* Get the file info.
*
* @param string Commit ('HEAD')
* @return false Information
*/
public function getFileInfo($totest, $commit='tip')
public function getPathInfo($totest, $commit='tip')
{
$cmd_tmpl = Pluf::f('hg_path', 'hg').' manifest -R %s --debug -r %s';
$cmd = sprintf($cmd_tmpl, escapeshellarg($this->repo), $commit);
$out = array();
$res = array();
IDF_Scm::exec($cmd, $out);
$out_hack = array();
foreach ($out as $line) {
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
exec($cmd, $out);
$tmp_hack = array();
while (null !== ($line = array_pop($out))) {
list($hash, $perm, $exec, $file) = preg_split('/ |\t/', $line, 4);
$file = trim($file);
$dir = explode('/', $file, -1);
$tmp = '';
for ($i=0; $i < count($dir); $i++) {
for ($i=0, $n=count($dir); $i<$n; $i++) {
if ($i > 0) {
$tmp .= '/';
}
$tmp .= $dir[$i];
if (!in_array("empty\t000\t\t$tmp/", $out_hack)) {
$out_hack[] = "emtpy\t000\t\t$tmp/";
if ($tmp == $totest) {
$pathinfo = pathinfo($totest);
return (object) array('perm' => '000', 'type' => 'tree',
'hash' => $hash,
'fullpath' => $totest,
'file' => $pathinfo['basename'],
'commit' => $commit
);
}
if (!isset($tmp_hack["empty\t000\t\t$tmp/"])) {
$out[] = "empty\t000\t\t$tmp/";
$tmp_hack["empty\t000\t\t$tmp/"] = 1;
}
}
$out_hack[] = "$hash\t$perm\t$exec\t$file";
}
foreach ($out_hack as $line) {
list($hash, $perm, $exec, $file) = preg_split('/ |\t/', $line, 4);
$file = trim ($file);
if (preg_match('/^(.*)\/$/', $file, $match)) {
$type = 'tree';
$file = $match[1];
@ -228,30 +228,26 @@ class IDF_Scm_Mercurial
$type = 'blob';
}
if ($totest == $file) {
$pathinfo = pathinfo($totest);
return (object) array('perm' => $perm, 'type' => $type,
'hash' => $hash,
'file' => $file,
'fullpath' => $totest,
'file' => $pathinfo['basename'],
'commit' => $commit
);
}
}
return false;
}
/**
* Get a blob.
*
* @param string request_file_info
* @param null to be svn client compatible
* @return string Raw blob
*/
public function getBlob($request_file_info, $dummy=null)
public function getFile($def, $cmd_only=false)
{
return IDF_Scm::shell_exec(sprintf(Pluf::f('hg_path', 'hg').' cat -R %s -r %s %s',
$cmd = sprintf(Pluf::f('hg_path', 'hg').' cat -R %s -r %s %s',
escapeshellarg($this->repo),
$dummy,
escapeshellarg($this->repo . '/' . $request_file_info->file)));
escapeshellarg($def->commit),
escapeshellarg($this->repo.'/'.$def->file));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
return ($cmd_only) ? $cmd : shell_exec($cmd);
}
/**
@ -261,17 +257,28 @@ class IDF_Scm_Mercurial
*/
public function getBranches()
{
if (isset($this->cache['branches'])) {
return $this->cache['branches'];
}
$out = array();
IDF_Scm::exec(sprintf(Pluf::f('hg_path', 'hg').' branches -R %s',
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
exec(sprintf(Pluf::f('hg_path', 'hg').' branches -R %s',
escapeshellarg($this->repo)), $out);
$res = array();
foreach ($out as $b) {
preg_match('/(\S+).*\S+:(\S+)/', $b, $match);
$res[] = $match[1];
$res[$match[1]] = '';
}
$this->cache['branches'] = $res;
return $res;
}
public function inBranches($commit, $path)
{
return (in_array($commit, array_keys($this->getBranches())))
? array($commit) : array();
}
/**
* Get commit details.
*
@ -286,7 +293,8 @@ class IDF_Scm_Mercurial
$cmd = sprintf($tmpl,
escapeshellarg($commit), escapeshellarg($this->repo));
$out = array();
IDF_Scm::exec($cmd, $out);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
exec($cmd, $out);
$log = array();
$change = array();
$inchange = false;
@ -327,7 +335,8 @@ class IDF_Scm_Mercurial
{
$cmd = sprintf(Pluf::f('hg_path', 'hg').' log -R %s -l%s ', escapeshellarg($this->repo), $n, $commit);
$out = array();
IDF_Scm::exec($cmd, $out);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
exec($cmd, $out);
return self::parseLog($out, 6);
}

View File

@ -22,23 +22,44 @@
# ***** END LICENSE BLOCK ***** */
/**
* SVN utils.
* Subversion backend.
* When a branch is not a branch.
*
* Contrary to most other SCMs, Subversion is using folders to manage
* the branches and so what is either the commit or the branch in
* other SCMs is the revision number with Subversion. So, do not be
* surprised if you have the feeling that the methods are not really
* returning what could be expected from their names.
*/
class IDF_Scm_Svn
class IDF_Scm_Svn extends IDF_Scm
{
public $repo = '';
public $username = '';
public $password = '';
private $assoc = array('dir' => 'tree',
'file' => 'blob');
public function __construct($repo, $username='', $password='')
public function __construct($repo, $project=null)
{
$this->repo = $repo;
$this->username = $username;
$this->password = $password;
$this->project = $project;
$this->cache['commitmess'] = array();
}
public function isAvailable()
{
return true;
}
public function getRepositorySize()
{
if (strpos($this->repo, 'file://') !== 0) {
return -1;
}
$cmd = Pluf::f('idf_exec_cmd_prefix', '').'du -sk '
.escapeshellarg(substr($this->repo, 7));
$out = split(' ', shell_exec($cmd), 2);
return (int) $out[0]*1024;
}
/**
@ -61,7 +82,24 @@ class IDF_Scm_Svn
* @param IDF_Project
* @return string URL
*/
public static function getRemoteAccessUrl($project)
public static function getAnonymousAccessUrl($project)
{
$conf = $project->getConf();
if (false !== ($url=$conf->getVal('svn_remote_url', false))
&& !empty($url)) {
// Remote repository
return $url;
}
return sprintf(Pluf::f('svn_remote_url'), $project->shortname);
}
/**
* Returns the URL of the subversion repository.
*
* @param IDF_Project
* @return string URL
*/
public static function getAuthAccessUrl($project, $user)
{
$conf = $project->getConf();
if (false !== ($url=$conf->getVal('svn_remote_url', false))
@ -85,15 +123,35 @@ class IDF_Scm_Svn
if (false !== ($rep=$conf->getVal('svn_remote_url', false))
&& !empty($rep)) {
// Remote repository
return new IDF_Scm_Svn($rep,
$conf->getVal('svn_username'),
$conf->getVal('svn_password'));
$scm = new IDF_Scm_Svn($rep, $project);
$scm->username = $conf->getVal('svn_username');
$scm->password = $conf->getVal('svn_password');
return $scm;
} else {
$rep = sprintf(Pluf::f('svn_repositories'), $project->shortname);
return new IDF_Scm_Svn($rep);
return new IDF_Scm_Svn($rep, $project);
}
}
/**
* Subversion revisions are either a number or 'HEAD'.
*/
public function isValidRevision($rev)
{
if ($rev == 'HEAD') {
return true;
}
$cmd = sprintf(Pluf::f('svn_path', 'svn').' info --username=%s --password=%s %s@%s',
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo),
escapeshellarg($rev));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
exec($cmd, $out, $ret);
return (0 == $ret);
}
/**
* Test a given object hash.
*
@ -113,7 +171,8 @@ class IDF_Scm_Svn
escapeshellarg($this->password),
escapeshellarg($this->repo.'/'.$path),
escapeshellarg($rev));
$xmlInfo = IDF_Scm::shell_exec($cmd);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xmlInfo = shell_exec($cmd);
// If exception is thrown, return false
try {
@ -132,29 +191,17 @@ class IDF_Scm_Svn
return 'commit';
}
/**
* Given a commit hash returns an array of files in it.
*
* A file is a class with the following properties:
*
* 'perm', 'type', 'size', 'hash', 'file'
*
* @param string Commit ('HEAD')
* @param string Base folder ('')
* @return array
*/
public function filesAtCommit($rev='HEAD', $folder='')
public function getTree($commit, $folder='/', $branch=null)
{
$cmd = sprintf(Pluf::f('svn_path', 'svn').' ls --xml --username=%s --password=%s %s@%s',
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo.'/'.$folder),
escapeshellarg($rev));
$xmlLs = IDF_Scm::shell_exec($cmd);
$xml = simplexml_load_string($xmlLs);
escapeshellarg($commit));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xml = simplexml_load_string(shell_exec($cmd));
$res = array();
$folder = (strlen($folder)) ? $folder.'/' : '';
$folder = (strlen($folder) and ($folder != '/')) ? $folder.'/' : '';
foreach ($xml->list->entry as $entry) {
$file = array();
$file['type'] = $this->assoc[(string) $entry['kind']];
@ -163,10 +210,7 @@ class IDF_Scm_Svn
$file['date'] = gmdate('Y-m-d H:i:s',
strtotime((string) $entry->commit->date));
$file['rev'] = (string) $entry->commit['revision'];
// Get commit message
$currentReposFile = $this->repo.'/'.$folder.$file['file'];
$file['log'] = $this->getCommitMessage($currentReposFile, $rev);
$file['log'] = $this->getCommitMessage($file['rev']);
// Get the size if the type is blob
if ($file['type'] == 'blob') {
$file['size'] = (string) $entry->size;
@ -175,116 +219,154 @@ class IDF_Scm_Svn
$file['perm'] = '';
$res[] = (object) $file;
}
return $res;
}
/**
* Get a commit message for given file and revision.
* Get the commit message of a revision revision.
*
* @param string File
* @param string Commit ('HEAD')
*
* @return String commit message
*/
private function getCommitMessage($file, $rev='HEAD')
private function getCommitMessage($rev='HEAD')
{
if (isset($this->cache['commitmess'][$rev])) {
return $this->cache['commitmess'][$rev];
}
$cmd = sprintf(Pluf::f('svn_path', 'svn').' log --xml --limit 1 --username=%s --password=%s %s@%s',
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($file),
escapeshellarg($this->repo),
escapeshellarg($rev));
$xmlLog = IDF_Scm::shell_exec($cmd);
$xml = simplexml_load_string($xmlLog);
return (string) $xml->logentry->msg;
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xml = simplexml_load_string(shell_exec($cmd));
$this->cache['commitmess'][$rev] = (string) $xml->logentry->msg;
return $this->cache['commitmess'][$rev];
}
/**
* Get the file info.
*
* @param string File
* @param string Commit ('HEAD')
* @return false Information
*/
public function getFileInfo($totest, $rev='HEAD')
public function getPathInfo($filename, $rev=null)
{
if ($rev == null) {
$rev = 'HEAD';
}
$cmd = sprintf(Pluf::f('svn_path', 'svn').' info --xml --username=%s --password=%s %s@%s',
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo.'/'.$totest),
escapeshellarg($this->repo.'/'.$filename),
escapeshellarg($rev));
$xmlInfo = IDF_Scm::shell_exec($cmd);
$xml = simplexml_load_string($xmlInfo);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xml = simplexml_load_string(shell_exec($cmd));
if (!isset($xml->entry)) {
return false;
}
$entry = $xml->entry;
$file = array();
$file['fullpath'] = $totest;
$file['fullpath'] = $filename;
$file['hash'] = (string) $entry->repository->uuid;
$file['type'] = $this->assoc[(string) $entry['kind']];
$file['file'] = $totest;
$file['rev'] = (string) $entry->commit['revision'];
$pathinfo = pathinfo($filename);
$file['file'] = $pathinfo['basename'];
$file['rev'] = $rev;
$file['author'] = (string) $entry->author;
$file['date'] = gmdate('Y-m-d H:i:s', strtotime((string) $entry->commit->date));
$file['size'] = (string) $entry->size;
$file['log'] = '';
return (object) $file;
}
/**
* Get a blob.
*
* @param string request_file_info
* @return string Raw blob
*/
public function getBlob($request_file_info, $rev)
public function getFile($def, $cmd_only=false)
{
$cmd = sprintf(Pluf::f('svn_path', 'svn').' cat --username=%s --password=%s %s@%s',
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo.'/'.$request_file_info->fullpath),
escapeshellarg($rev));
return IDF_Scm::shell_exec($cmd);
escapeshellarg($this->repo.'/'.$def->fullpath),
escapeshellarg($def->rev));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
return ($cmd_only) ? $cmd : shell_exec($cmd);
}
/**
* Get the branches.
* Subversion branches are repository based.
*
* @return array Branches.
* One need to list the folder to know them.
*/
public function getBranches()
{
$res = array('HEAD');
if (isset($this->cache['branches'])) {
return $this->cache['branches'];
}
$res = array();
$cmd = sprintf(Pluf::f('svn_path', 'svn').' ls --username=%s --password=%s %s@HEAD',
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo.'/branches'));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
exec($cmd, $out, $ret);
if ($ret == 0) {
foreach ($out as $entry) {
if (substr(trim($entry), -1) == '/') {
$branch = substr(trim($entry), 0, -1);
$res[$branch] = 'branches/'.$branch;
}
}
}
ksort($res);
$cmd = sprintf(Pluf::f('svn_path', 'svn').' info --username=%s --password=%s %s@HEAD',
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo.'/trunk'));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
exec($cmd, $out, $ret);
if ($ret == 0) {
$res = array('trunk' => 'trunk') + $res;
}
$this->cache['branches'] = $res;
return $res;
}
public function getMainBranch()
{
return 'HEAD';
}
public function inBranches($commit, $path)
{
foreach ($this->getBranches() as $branch => $bpath) {
if ($bpath and 0 === strpos($path, $bpath)) {
return array($branch);
}
}
return array();
}
/**
* Get commit details.
*
* @param string Commit ('HEAD')
* @param string Commit
* @param bool Get commit diff (false)
* @return array Changes
*/
public function getCommit($rev='HEAD', $getdiff=false)
public function getCommit($commit, $getdiff=false)
{
if (!$this->isValidRevision($commit)) {
return false;
}
$res = array();
$cmd = sprintf(Pluf::f('svn_path', 'svn').' log --xml -v --username=%s --password=%s %s@%s',
$cmd = sprintf(Pluf::f('svn_path', 'svn').' log --xml --limit 1 -v --username=%s --password=%s %s@%s',
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo),
escapeshellarg($rev));
$xmlRes = IDF_Scm::shell_exec($cmd);
escapeshellarg($commit));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xmlRes = shell_exec($cmd);
$xml = simplexml_load_string($xmlRes);
$res['author'] = (string) $xml->logentry->author;
$res['date'] = gmdate('Y-m-d H:i:s', strtotime((string) $xml->logentry->date));
$res['title'] = (string) $xml->logentry->msg;
$res['commit'] = (string) $xml->logentry['revision'];
$res['changes'] = ($getdiff) ? $this->getDiff($rev) : '';
$res['changes'] = ($getdiff) ? $this->getDiff($commit) : '';
$res['tree'] = '';
return (object) $res;
}
@ -306,7 +388,8 @@ class IDF_Scm_Svn
$cmd = sprintf(Pluf::f('svnlook_path', 'svnlook').' changed -r %s %s',
escapeshellarg($commit),
escapeshellarg($repo));
$out = IDF_Scm::shell_exec($cmd);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$out = shell_exec($cmd);
$lines = preg_split("/\015\012|\015|\012/", $out);
return (count($lines) > 100);
}
@ -319,60 +402,49 @@ class IDF_Scm_Svn
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo));
return IDF_Scm::shell_exec($cmd);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
return shell_exec($cmd);
}
/**
* Get latest changes.
*
* @param string Commit ('HEAD').
* @param string Revision or ('HEAD').
* @param int Number of changes (10).
*
* @return array Changes.
*/
public function getChangeLog($rev='HEAD', $n=10)
public function getChangeLog($branch=null, $n=10)
{
if ($branch != 'HEAD' and !preg_match('/^\d+$/', $branch)) {
// we accept only revisions or HEAD
$branch = 'HEAD';
}
$res = array();
$cmd = sprintf(Pluf::f('svn_path', 'svn').' log --xml -v --limit %s --username=%s --password=%s %s@%s',
escapeshellarg($n),
escapeshellarg($this->username),
escapeshellarg($this->password),
escapeshellarg($this->repo),
escapeshellarg($rev));
$xmlRes = IDF_Scm::shell_exec($cmd);
escapeshellarg($branch));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xmlRes = shell_exec($cmd);
$xml = simplexml_load_string($xmlRes);
$res = array();
foreach ($xml->logentry as $entry) {
$log = array();
$log['author'] = (string) $entry->author;
$log['date'] = gmdate('Y-m-d H:i:s', strtotime((string) $entry->date));
$log['title'] = (string) $entry->msg;
$split = split("[\n\r]", (string) $entry->msg, 2);
$log['title'] = $split[0];
$log['commit'] = (string) $entry['revision'];
$log['full_message'] = '';
$log['full_message'] = (isset($split[1])) ? trim($split[1]) : '';
$res[] = (object) $log;
}
return $res;
}
/**
* Generate the command to create a zip archive at a given commit.
* Unsupported feature in subversion
*
* @param string dummy
* @param string dummy
* @return Exception
*/
public function getArchiveCommand($commit, $prefix='git-repo-dump/')
{
throw new Exception('Unsupported feature.');
}
/**
* Get additionnals properties on path and revision
*
@ -388,7 +460,8 @@ class IDF_Scm_Svn
escapeshellarg($this->password),
escapeshellarg($this->repo.'/'.$path),
escapeshellarg($rev));
$xmlProps = IDF_Scm::shell_exec($cmd);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xmlProps = shell_exec($cmd);
$props = simplexml_load_string($xmlProps);
// No properties, returns an empty array
@ -423,7 +496,8 @@ class IDF_Scm_Svn
escapeshellarg($this->password),
escapeshellarg($this->repo.'/'.$path),
escapeshellarg($rev));
$xmlProp = IDF_Scm::shell_exec($cmd);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xmlProp = shell_exec($cmd);
$prop = simplexml_load_string($xmlProp);
return (string) $prop->target->property;
@ -445,7 +519,8 @@ class IDF_Scm_Svn
escapeshellarg($this->password),
escapeshellarg($this->repo),
escapeshellarg($rev));
$xmlInfo = IDF_Scm::shell_exec($cmd);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$xmlInfo = shell_exec($cmd);
$xml = simplexml_load_string($xmlInfo);
return (string) $xml->entry->commit['revision'];

View File

@ -37,22 +37,22 @@ class IDF_Template_IssueComment extends Pluf_Template_Tag
$this->project = $request->project;
$this->request = $request;
$this->scm = IDF_Scm::get($request->project);
if ($wordwrap) $text = wordwrap($text, 69, "\n", true);
if ($esc) $text = Pluf_esc($text);
if ($autolink) {
$text = preg_replace('#([a-z]+://[^\s\(\)]+)#i',
'<a href="\1">\1</a>', $text);
}
if ($request->rights['hasIssuesAccess']) {
$text = preg_replace_callback('#(issues?|bugs?|tickets?)\s+(\d+)((\s+and|\s+or|,)\s+(\d+)){0,}#im',
$text = preg_replace_callback('#(issues?|bugs?|tickets?)\s+(\d+)(\#ic\d*){0,1}((\s+and|\s+or|,)\s+(\d+)(\#ic\d*){0,1}){0,}#im',
array($this, 'callbackIssues'), $text);
}
if ($request->rights['hasSourceAccess']) {
$text = preg_replace_callback('#(commit\s+)([0-9a-f]{1,40})#im',
array($this, 'callbackCommit'), $text);
$text = preg_replace_callback('#(commits?\s+)([0-9a-f]{1,40}(?:(?:\s+and|\s+or|,)\s+[0-9a-f]{1,40})*)\b#i',
array($this, 'callbackCommits'), $text);
$text = preg_replace_callback('#(src:)([^\s\(\)]+)#im',
array($this, 'callbackSource'), $text);
}
if ($wordwrap) $text = Pluf_Text::wrapHtml($text, 69, "\n");
if ($nl2br) $text = nl2br($text);
if ($echo) {
echo $text;
@ -66,10 +66,14 @@ class IDF_Template_IssueComment extends Pluf_Template_Tag
*/
function callbackIssues($m)
{
if (count($m) == 3) {
if (count($m) == 3 || count($m) == 4) {
$issue = new IDF_Issue($m[2]);
if ($issue->id > 0 and $issue->project == $this->project->id) {
if (count($m) == 3) {
return $this->linkIssue($issue, $m[1].' '.$m[2]);
} else {
return $this->linkIssue($issue, $m[1].' '.$m[2], $m[3]);
}
} else {
return $m[0]; // not existing issue.
}
@ -96,29 +100,55 @@ class IDF_Template_IssueComment extends Pluf_Template_Tag
}
}
function callbackCommit($m)
/**
* General call back to convert commits to HTML links.
*
* @param array $m Single regex match.
* @return string Content with converted commits.
*/
function callbackCommits($m)
{
if ($this->scm->testHash($m[2]) != 'commit') {
$keyword = rtrim($m[1]);
if ('commits' === $keyword) {
// Multiple commits like 'commits 6e030e6, a25bfc1 and
// 3c094f8'.
return $m[1].preg_replace_callback('#\b[0-9a-f]{4,40}\b#i', array($this, 'callbackCommit'), $m[2]);
} else if ('commit' === $keyword) {
// Single commit like 'commit 6e030e6'.
return $m[1].call_user_func(array($this, 'callbackCommit'), array($m[2]));
}
return $m[0];
}
$co = $this->scm->getCommit($m[2]);
return '<a href="'.Pluf_HTTP_URL_urlForView('IDF_Views_Source::commit', array($this->project->shortname, $co->commit)).'">'.$m[1].$m[2].'</a>';
/**
* Convert plaintext commit to HTML link. Called from callbackCommits.
*
* Regex callback for {@link IDF_Template_IssueComment::callbackCommits()}.
*
* @param array Single regex match.
* @return string HTML A element with commit.
*/
function callbackCommit($m)
{
$co = $this->scm->getCommit($m[0]);
if (!$co) {
return $m[0]; // not a commit.
}
return '<a href="'
.Pluf_HTTP_URL_urlForView('IDF_Views_Source::commit', array($this->project->shortname, $co->commit))
.'">'.$m[0].'</a>';
}
function callbackSource($m)
{
$branches = $this->scm->getBranches();
if (count($branches) == 0) return $m[0];
if (!$this->scm->isAvailable()) return $m[0];
$file = $m[2];
if ('commit' != $this->scm->testHash($branches[0], $file)) {
return $m[0];
}
$request_file_info = $this->scm->getFileInfo($file, $branches[0]);
$request_file_info = $this->scm->getPathInfo($file);
if (!$request_file_info) {
return $m[0];
}
if ($request_file_info->type != 'tree') {
return $m[1].'<a href="'.Pluf_HTTP_URL_urlForView('IDF_Views_Source::tree', array($this->project->shortname, $branches[0], $file)).'">'.$m[2].'</a>';
return $m[1].'<a href="'.Pluf_HTTP_URL_urlForView('IDF_Views_Source::tree', array($this->project->shortname, $this->scm->getMainBranch(), $file)).'">'.$m[2].'</a>';
}
return $m[0];
}
@ -130,10 +160,10 @@ class IDF_Template_IssueComment extends Pluf_Template_Tag
* @param string Name of the link.
* @return string Linked issue.
*/
public function linkIssue($issue, $title)
public function linkIssue($issue, $title, $anchor='')
{
$ic = (in_array($issue->status, $this->project->getTagIdsByStatus('closed'))) ? 'issue-c' : 'issue-o';
return '<a href="'.Pluf_HTTP_URL_urlForView('IDF_Views_Issue::view',
array($this->project->shortname, $issue->id)).'" class="'.$ic.'" title="'.Pluf_esc($issue->summary).'">'.Pluf_esc($title).'</a>';
array($this->project->shortname, $issue->id)).$anchor.'" class="'.$ic.'" title="'.Pluf_esc($issue->summary).'">'.Pluf_esc($title).'</a>';
}
}

View File

@ -91,6 +91,7 @@ class IDF_Template_MarkdownPrefilter extends Pluf_Text_HTML_Filter
public $allowed = array(
'a' => array('href', 'title', 'rel'),
'abbr' => array('title'),
'address' => array(),
'b' => array(),
'blockquote' => array(),
@ -102,9 +103,12 @@ class IDF_Template_MarkdownPrefilter extends Pluf_Text_HTML_Filter
'dl' => array(),
'dt' => array(),
'em' => array(),
'h1' => array(),
'h2' => array(),
'h3' => array(),
'h1' => array('id'),
'h2' => array('id'),
'h3' => array('id'),
'h4' => array('id'),
'h5' => array('id'),
'h6' => array('id'),
'hr' => array(),
'i' => array(),
'img' => array('src', 'class', 'alt', 'height', 'width', 'style'),

View File

@ -39,4 +39,23 @@ class IDF_Tests_TestGit extends UnitTestCase
$this->assertEqual('Fixed the middleware to correctly return a 404 error if the project is', $log[0]->title);
}
/**
* parse a log encoded in iso 8859-1
*/
public function testParseIsoLog()
{
$log_lines = preg_split("/\015\012|\015|\012/", file_get_contents(dirname(__FILE__).'/data/git-log-iso-8859-1.txt'));
$log = IDF_Scm_Git::parseLog($log_lines);
$titles = array(
'Quick Profiler entfernt',
'Anwendungsmenu Divider eingefügt',
'Anwendungen aufäumen'
);
foreach ($log as $change) {
$this->assertEqual(array_shift($titles),
IDF_Commit::toUTF8($change->title));
}
}
}

View File

@ -0,0 +1,19 @@
commit 11531a9dbc64a65150f2f38fbea7cef9d478a123
Author: unknown <a@(none)>
Date: Fri Jul 3 01:44:11 2009 +0200
Quick Profiler entfernt
commit 11531a9dbc64a65150f2f38fbea7cef9d478a123
Author: unknown <a@(none)>
Date: Wed Jul 1 15:51:22 2009 +0200
Anwendungsmenu Divider eingefügt
commit 11531a9dbc64a65150f2f38fbea7cef9d478a123
Author: unknown <a@(none)>
Date: Wed Jul 1 15:05:41 2009 +0200
Anwendungen aufäumen

View File

@ -56,6 +56,12 @@ class IDF_Upload extends Pluf_Model
'size' => 250,
'verbose' => __('summary'),
),
'changelog' =>
array(
'type' => 'Pluf_DB_Field_Text',
'blank' => true,
'verbose' => __('changes'),
),
'file' =>
array(
'type' => 'Pluf_DB_Field_File',
@ -160,6 +166,7 @@ class IDF_Upload extends Pluf_Model
function preDelete()
{
IDF_Timeline::remove($this);
@unlink(Pluf::f('upload_path').'/'.$this->project->shortname.'/files/'.$this->file);
}
/**
@ -182,7 +189,7 @@ class IDF_Upload extends Pluf_Model
$out .= sprintf(__('<a href="%1$s" title="View download">Download %2$d</a>, %3$s'), $url, $this->id, Pluf_esc($this->summary)).'</td>';
$out .= '</tr>';
$out .= "\n".'<tr class="extra"><td colspan="2">
<div class="helptext right">'.sprintf(__('Addition of <a href="%s">download&nbsp;%d</a>'), $url, $this->id).', '.__('by').' '.$user.'</div></td></tr>';
<div class="helptext right">'.sprintf(__('Addition of <a href="%s">download&nbsp;%d</a>, by %s'), $url, $this->id, $user).'</div></td></tr>';
return Pluf_Template::markSafe($out);
}

View File

@ -64,9 +64,11 @@ class IDF_Views_Admin
$list_display = array(
'shortname' => __('Short Name'),
'name' => __('Name'),
array('id', 'IDF_Views_Admin_projectSize', __('Repository Size')),
);
$pag->configure($list_display, array(),
array('shortname'));
$pag->extra_classes = array('', '', 'right');
$pag->items_per_page = 25;
$pag->no_results_text = __('No projects were found.');
$pag->setFromRequest($request);
@ -74,6 +76,7 @@ class IDF_Views_Admin
array(
'page_title' => $title,
'projects' => $pag,
'size' => IDF_Views_Admin_getForgeSize(),
),
$request);
}
@ -284,3 +287,88 @@ function IDF_Views_Admin_bool($field, $item)
$text = ($item->$field) ? __('Yes') : __('No');
return sprintf('<img src="'.Pluf::f('url_media').'/idf/img/%s.png" alt="%s" /> ', $img, $text);
}
/**
* Display the size of the project.
*
* @param string Field
* @param IDF_Project
* @return string
*/
function IDF_Views_Admin_projectSize($field, $project)
{
$size = $project->getRepositorySize();
if ($size == -1) {
return '';
}
return Pluf_Utils::prettySize($size);
}
/**
* Get a forge size.
*
* @return array Associative array with the size of each element
*/
function IDF_Views_Admin_getForgeSize()
{
$res = array();
$res['repositories'] = 0;
foreach (Pluf::factory('IDF_Project')->getList() as $prj) {
$size = $prj->getRepositorySize();
if ($size != -1) {
$res['repositories'] += $size;
}
}
$cmd = Pluf::f('idf_exec_cmd_prefix', '').'du -sk '
.escapeshellarg(Pluf::f('upload_path'));
$out = split(' ', shell_exec($cmd), 2);
$res['downloads'] = $out[0]*1024;
$cmd = Pluf::f('idf_exec_cmd_prefix', '').'du -sk '
.escapeshellarg(Pluf::f('upload_issue_path'));
$out = split(' ', shell_exec($cmd), 2);
$res['attachments'] = $out[0]*1024;
$res['database'] = IDF_Views_Admin_getForgeDbSize();
$res['total'] = $res['repositories'] + $res['downloads'] + $res['attachments'] + $res['database'];
return $res;
}
/**
* Get the database size as given by the database.
*
* @return int Database size
*/
function IDF_Views_Admin_getForgeDbSize()
{
$db = Pluf::db();
if (Pluf::f('db_engine') == 'SQLite') {
return filesize(Pluf::f('db_database'));
}
switch (Pluf::f('db_engine')) {
case 'PostgreSQL':
$sql = 'SELECT relname, pg_total_relation_size(relname) AS size FROM pg_class AS pgc, pg_namespace AS pgn
WHERE pg_table_is_visible(pgc.oid) IS TRUE AND relkind = \'r\'
AND pgc.relnamespace = pgn.oid
AND pgn.nspname NOT IN (\'information_schema\', \'pg_catalog\')';
break;
case 'MySQL':
default:
$sql = 'SHOW TABLE STATUS FROM '.Pluf::f('db_database');
break;
}
$rs = $db->select($sql);
$total = 0;
switch (Pluf::f('db_engine')) {
case 'PostgreSQL':
foreach ($rs as $table) {
$total += $table['size'];
}
break;
case 'MySQL':
default:
foreach ($rs as $table) {
$total += $table['Data_length'] + $table['Index_length'];
}
break;
}
return $total;
}

View File

@ -226,7 +226,7 @@ class IDF_Views_Download
$conf = new IDF_Conf();
$conf->setProject($project);
$st = preg_split("/\015\012|\015|\012/",
$conf->getVal('labels_downloads_predefined', IDF_Form_UploadConf::init_predefined), -1, PREG_SPLIT_NO_EMPTY);
$conf->getVal('labels_download_predefined', IDF_Form_UploadConf::init_predefined), -1, PREG_SPLIT_NO_EMPTY);
$auto = '';
foreach ($st as $s) {
$v = '';
@ -298,7 +298,7 @@ class IDF_Views_Download
*/
public static function getDownloadTags($project)
{
return $project->getTagsFromConfig('labels_downloads_predefined',
return $project->getTagsFromConfig('labels_download_predefined',
IDF_Form_UploadConf::init_predefined);
}

View File

@ -55,6 +55,7 @@ class IDF_Views_Issue
$pag->action = array('IDF_Views_Issue::index', array($prj->shortname));
$pag->sort_order = array('modif_dtime', 'ASC'); // will be reverted
$pag->sort_reverse_order = array('modif_dtime');
$pag->sort_link_title = true;
$pag->extra_classes = array('a-c', '', 'a-c', '');
$list_display = array(
'id' => __('Id'),
@ -62,7 +63,7 @@ class IDF_Views_Issue
array('status', 'IDF_Views_Issue_ShowStatus', __('Status')),
array('modif_dtime', 'Pluf_Paginator_DateAgo', __('Last Updated')),
);
$pag->configure($list_display, array(), array('status', 'modif_dtime'));
$pag->configure($list_display, array(), array('id', 'status', 'modif_dtime'));
$pag->items_per_page = 10;
$pag->no_results_text = __('No issues were found.');
$pag->setFromRequest($request);
@ -131,6 +132,7 @@ class IDF_Views_Issue
$pag->action = array('IDF_Views_Issue::myIssues', 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;
$pag->extra_classes = array('a-c', '', 'a-c', '');
$list_display = array(
'id' => __('Id'),
@ -138,7 +140,7 @@ class IDF_Views_Issue
array('status', 'IDF_Views_Issue_ShowStatus', __('Status')),
array('modif_dtime', 'Pluf_Paginator_DateAgo', __('Last Updated')),
);
$pag->configure($list_display, array(), array('status', 'modif_dtime'));
$pag->configure($list_display, array(), array('id', 'status', 'modif_dtime'));
$pag->items_per_page = 10;
$pag->no_results_text = __('No issues were found.');
$pag->setFromRequest($request);
@ -171,11 +173,8 @@ class IDF_Views_Issue
$params);
if (!isset($request->POST['preview']) and $form->isValid()) {
$issue = $form->save();
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::index',
array($prj->shortname));
$urlissue = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::view',
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::view',
array($prj->shortname, $issue->id));
$request->user->setMessage(sprintf(__('<a href="%s">Issue %d</a> has been created.'), $urlissue, $issue->id));
$to_emails = array();
if (null != $issue->get_owner() and $issue->owner != $issue->submitter) {
$to_emails[] = $issue->get_owner()->email;
@ -201,6 +200,7 @@ class IDF_Views_Issue
$email->sendMail();
}
if ($api) return $issue;
$request->user->setMessage(sprintf(__('<a href="%s">Issue %d</a> has been created.'), $url, $issue->id));
return new Pluf_HTTP_Response_Redirect($url);
}
} else {
@ -291,11 +291,8 @@ class IDF_Views_Issue
$params);
if (!isset($request->POST['preview']) && $form->isValid()) {
$issue = $form->save();
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::index',
array($prj->shortname));
$urlissue = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::view',
array($prj->shortname, $issue->id));
$request->user->setMessage(sprintf(__('<a href="%s">Issue %d</a> has been updated.'), $urlissue, $issue->id));
$comments = $issue->get_comments_list(array('order' => 'id DESC'));
$url .= '#ic' . $comments[0]->id;
// Get the list of interested person + owner + submitter
if (!Pluf_Model_InArray($issue->get_submitter(), $interested)) {
$interested[] = $issue->get_submitter();
@ -304,15 +301,13 @@ class IDF_Views_Issue
!Pluf_Model_InArray($issue->get_owner(), $interested)) {
$interested[] = $issue->get_owner();
}
$comments = $issue->get_comments_list(array('order' => 'id DESC'));
$context = new Pluf_Template_Context(
array(
'issue' => $issue,
'comments' => $comments,
'project' => $prj,
'url_base' => Pluf::f('url_base'),
)
);
));
$tmpl = new Pluf_Template('idf/issues/issue-updated-email.txt');
$text_email = $tmpl->render($context);
$email = new Pluf_Mail_Batch(Pluf::f('from_email'));
@ -334,6 +329,7 @@ class IDF_Views_Issue
$email->sendMail();
}
$email->close();
$request->user->setMessage(sprintf(__('<a href="%s">Issue %d</a> has been updated.'), $url, $issue->id));
return new Pluf_HTTP_Response_Redirect($url);
}
} else {
@ -436,6 +432,7 @@ class IDF_Views_Issue
$pag->action = array('IDF_Views_Issue::listStatus', array($prj->shortname, $status));
$pag->sort_order = array('modif_dtime', 'ASC'); // will be reverted
$pag->sort_reverse_order = array('modif_dtime');
$pag->sort_link_title = true;
$pag->extra_classes = array('a-c', '', 'a-c', '');
$list_display = array(
'id' => __('Id'),
@ -443,7 +440,7 @@ class IDF_Views_Issue
array('status', 'IDF_Views_Issue_ShowStatus', __('Status')),
array('modif_dtime', 'Pluf_Paginator_DateAgo', __('Last Updated')),
);
$pag->configure($list_display, array(), array('status', 'modif_dtime'));
$pag->configure($list_display, array(), array('id', 'status', 'modif_dtime'));
$pag->items_per_page = 10;
$pag->no_results_text = __('No issues were found.');
$pag->setFromRequest($request);
@ -494,6 +491,7 @@ class IDF_Views_Issue
$pag->action = array('IDF_Views_Issue::listLabel', array($prj->shortname, $tag->id, $status));
$pag->sort_order = array('modif_dtime', 'ASC'); // will be reverted
$pag->sort_reverse_order = array('modif_dtime');
$pag->sort_link_title = true;
$pag->extra_classes = array('a-c', '', 'a-c', '');
$list_display = array(
'id' => __('Id'),
@ -501,7 +499,7 @@ class IDF_Views_Issue
array('status', 'IDF_Views_Issue_ShowStatus', __('Status')),
array('modif_dtime', 'Pluf_Paginator_DateAgo', __('Last Updated')),
);
$pag->configure($list_display, array(), array('status', 'modif_dtime'));
$pag->configure($list_display, array(), array('id', 'status', 'modif_dtime'));
$pag->items_per_page = 10;
$pag->no_results_text = __('No issues were found.');
$pag->setFromRequest($request);

View File

@ -342,7 +342,7 @@ class IDF_Views_Project
$conf->setVal($key, $val);
}
$request->user->setMessage(__('The documentation configuration has been saved.'));
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Project::adminDownloads',
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Project::adminWiki',
array($prj->shortname));
return new Pluf_HTTP_Response_Redirect($url);
}
@ -510,6 +510,7 @@ class IDF_Views_Project
'remote_svn' => $remote_svn,
'repository_access' => $prj->getRemoteAccessUrl(),
'repository_type' => $repository_type,
'repository_size' => $prj->getRepositorySize(),
'page_title' => $title,
'form' => $form,
),

View File

@ -207,7 +207,7 @@ class IDF_Views_Review
$files = array();
$reviewers = array();
foreach ($diff->files as $filename => $def) {
$fileinfo = $scm->getFileInfo($filename, $patch->get_commit()->scm_id);
$fileinfo = $scm->getPathInfo($filename, $patch->get_commit()->scm_id);
$sql = new Pluf_SQL('cfile=%s', array($filename));
$cts = $patch->get_filecomments_list(array('filter'=>$sql->gen(),
'order'=>'creation_dtime ASC'));
@ -215,7 +215,7 @@ class IDF_Views_Review
$reviewers[] = $ct->get_submitter();
}
if (count($def['chunks'])) {
$orig_file = ($fileinfo) ? $scm->getBlob($fileinfo) : '';
$orig_file = ($fileinfo) ? $scm->getFile($fileinfo) : '';
$files[$filename] = array(
$diff->fileCompare($orig_file, $def, $filename),
$form->f->{md5($filename)},

View File

@ -27,15 +27,19 @@ Pluf::loadFunction('Pluf_Shortcuts_GetObjectOr404');
Pluf::loadFunction('Pluf_Shortcuts_GetFormForModel');
/**
* View git repository.
* View SCM repository.
*/
class IDF_Views_Source
{
/**
* Extension supported by the syntax highlighter.
*/
public static $supportedExtenstions = array('c', 'cc', 'cpp', 'cs', 'css',
'cyc', 'java', 'bsh', 'csh',
'sh', 'cv', 'py', 'perl', 'php',
'pl', 'pm', 'rb', 'js', 'html',
'html', 'xhtml', 'xml', 'xsl');
'html', 'vala', 'xhtml', 'xml',
'xsl');
/**
* Display help on how to checkout etc.
@ -61,9 +65,14 @@ class IDF_Views_Source
$title = sprintf(__('%1$s %2$s Change Log'), (string) $request->project,
$this->getScmType($request));
$scm = IDF_Scm::get($request->project);
if (!$scm->isAvailable()) {
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::help',
array($request->project->shortname));
return new Pluf_HTTP_Response_Redirect($url);
}
$branches = $scm->getBranches();
$commit = $match[2];
if ('commit' != $scm->testHash($commit)) {
if (!$scm->isValidRevision($commit)) {
if (count($branches) == 0) {
// Redirect to the project source help
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::help',
@ -73,7 +82,7 @@ class IDF_Views_Source
// Redirect to the first branch
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::changeLog',
array($request->project->shortname,
$branches[0]));
$scm->getMainBranch()));
return new Pluf_HTTP_Response_Redirect($url);
}
$changes = $scm->getChangeLog($commit, 25);
@ -84,13 +93,15 @@ class IDF_Views_Source
}
$rchanges = new Pluf_Template_ContextVars($rchanges);
$scmConf = $request->conf->getVal('scm', 'git');
return Pluf_Shortcuts_RenderToResponse('idf/source/changelog.html',
$in_branches = $scm->inBranches($commit, '');
return Pluf_Shortcuts_RenderToResponse('idf/source/'.$scmConf.'/changelog.html',
array(
'page_title' => $title,
'title' => $title,
'changes' => $rchanges,
'commit' => $commit,
'branches' => $branches,
'tree_in' => $in_branches,
'scm' => $scmConf,
),
$request);
@ -99,38 +110,33 @@ class IDF_Views_Source
public $treeBase_precond = array('IDF_Precondition::accessSource');
public function treeBase($request, $match)
{
$title = sprintf(__('%1$s %2$s Source Tree'), (string) $request->project,
$this->getScmType($request));
$title = sprintf(__('%1$s %2$s Source Tree'),
$request->project, $this->getScmType($request));
$scm = IDF_Scm::get($request->project);
$commit = $match[2];
$branches = $scm->getBranches();
if (count($branches) == 0) {
// Redirect to the project home
if (!$scm->isAvailable()) {
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::help',
array($request->project->shortname));
return new Pluf_HTTP_Response_Redirect($url);
}
if ('commit' != $scm->testHash($commit)) {
// Redirect to the first branch
$commit = $match[2];
$cobject = $scm->getCommit($commit);
if (!$cobject) {
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
array($request->project->shortname,
$branches[0]));
$scm->getMainBranch()));
return new Pluf_HTTP_Response_Redirect($url);
}
$branches = $scm->getBranches();
$in_branches = $scm->inBranches($commit, '');
$cache = Pluf_Cache::factory();
$key = sprintf('Project:%s::IDF_Views_Source::treeBase:%s::',
$request->project->id, $commit);
if (null === ($res=$cache->get($key))) {
$res = new Pluf_Template_ContextVars($scm->filesAtCommit($commit));
$res = new Pluf_Template_ContextVars($scm->getTree($commit));
$cache->set($key, $res);
}
$cobject = $scm->getCommit($commit);
$tree_in = in_array($commit, $branches);
$scmConf = $request->conf->getVal('scm', 'git');
$props = null;
if ($scmConf === 'svn') {
$props = $scm->getProperties($commit);
}
return Pluf_Shortcuts_RenderToResponse('idf/source/'.$scmConf.'/tree.html',
array(
'page_title' => $title,
@ -138,7 +144,7 @@ class IDF_Views_Source
'files' => $res,
'cobject' => $cobject,
'commit' => $commit,
'tree_in' => $tree_in,
'tree_in' => $in_branches,
'branches' => $branches,
'props' => $props,
),
@ -148,15 +154,21 @@ class IDF_Views_Source
public $tree_precond = array('IDF_Precondition::accessSource');
public function tree($request, $match)
{
$title = sprintf(__('%1$s %2$s Source Tree'), (string) $request->project,
$this->getScmType($request));
$title = sprintf(__('%1$s %2$s Source Tree'),
$request->project, $this->getScmType($request));
$scm = IDF_Scm::get($request->project);
$branches = $scm->getBranches();
$commit = $match[2];
$request_file = $match[3];
if (!$scm->isAvailable()) {
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::help',
array($request->project->shortname));
return new Pluf_HTTP_Response_Redirect($url);
}
$branches = $scm->getBranches();
$fburl = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
array($request->project->shortname,
$branches[0]));
$scm->getMainBranch()));
if (substr($request_file, -1) == '/') {
$request_file = substr($request_file, 0, -1);
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::tree',
@ -164,11 +176,11 @@ class IDF_Views_Source
$request_file));
return new Pluf_HTTP_Response_Redirect($url, 301);
}
if ('commit' != $scm->testHash($commit, $request_file)) {
if (!$scm->isValidRevision($commit, $request_file)) {
// Redirect to the first branch
return new Pluf_HTTP_Response_Redirect($fburl);
}
$request_file_info = $scm->getFileInfo($request_file, $commit);
$request_file_info = $scm->getPathInfo($request_file, $commit);
if (!$request_file_info) {
// Redirect to the first branch
return new Pluf_HTTP_Response_Redirect($fburl);
@ -177,7 +189,8 @@ class IDF_Views_Source
$info = self::getRequestedFileMimeType($request_file_info,
$commit, $scm);
if (!self::isText($info)) {
$rep = new Pluf_HTTP_Response($scm->getBlob($request_file_info, $commit),
$rep = new Pluf_HTTP_Response($scm->getFile($request_file_info),
$info[0]);
$rep->headers['Content-Disposition'] = 'attachment; filename="'.$info[1].'"';
return $rep;
@ -192,30 +205,25 @@ class IDF_Views_Source
return $this->viewFile($request, $match, $extra);
}
}
$bc = self::makeBreadCrumb($request->project, $commit, $request_file_info->file);
$bc = self::makeBreadCrumb($request->project, $commit, $request_file_info->fullpath);
$page_title = $bc.' - '.$title;
$cobject = $scm->getCommit($commit);
$tree_in = in_array($commit, $branches);
try {
$in_branches = $scm->inBranches($commit, $request_file);
$cache = Pluf_Cache::factory();
$key = sprintf('Project:%s::IDF_Views_Source::tree:%s::%s',
$request->project->id, $commit, $request_file);
if (null === ($res=$cache->get($key))) {
$res = new Pluf_Template_ContextVars($scm->filesAtCommit($commit, $request_file));
$res = new Pluf_Template_ContextVars($scm->getTree($commit, $request_file));
$cache->set($key, $res);
}
} catch (Exception $e) {
return new Pluf_HTTP_Response_Redirect($fburl);
}
// try to find the previous level if it exists.
$prev = split('/', $request_file);
$l = array_pop($prev);
$previous = substr($request_file, 0, -strlen($l.' '));
$scmConf = $request->conf->getVal('scm', 'git');
$props = null;
if ($scmConf === 'svn') {
$props = $scm->getProperties($commit, $request_file);
}
return Pluf_Shortcuts_RenderToResponse('idf/source/'.$scmConf.'/tree.html',
array(
'page_title' => $page_title,
@ -226,7 +234,7 @@ class IDF_Views_Source
'cobject' => $cobject,
'base' => $request_file_info->file,
'prev' => $previous,
'tree_in' => $tree_in,
'tree_in' => $in_branches,
'branches' => $branches,
'props' => $props,
),
@ -256,21 +264,29 @@ class IDF_Views_Source
$scm = IDF_Scm::get($request->project);
$commit = $match[2];
$branches = $scm->getBranches();
if ('commit' != $scm->testHash($commit)) {
if (!$scm->isValidRevision($commit)) {
// Redirect to the first branch
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
array($request->project->shortname,
$branches[0]));
$scm->getMainBranch()));
return new Pluf_HTTP_Response_Redirect($url);
}
$large = $scm->isCommitLarge($commit);
$cobject = $scm->getCommit($commit, !$large);
if (!$cobject) {
// Redirect to the first branch
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
array($request->project->shortname,
$scm->getMainBranch()));
return new Pluf_HTTP_Response_Redirect($url);
}
$title = sprintf(__('%s Commit Details'), (string) $request->project);
$page_title = sprintf(__('%s Commit Details - %s'), (string) $request->project, $commit);
$large = $scm->isCommitLarge($commit);
$cobject = $scm->getCommit($commit, !$large);
$rcommit = IDF_Commit::getOrAdd($cobject, $request->project);
$diff = new IDF_Diff($cobject->changes);
$diff->parse();
$scmConf = $request->conf->getVal('scm', 'git');
$in_branches = $scm->inBranches($commit, '');
return Pluf_Shortcuts_RenderToResponse('idf/source/commit.html',
array(
'page_title' => $page_title,
@ -279,6 +295,7 @@ class IDF_Views_Source
'cobject' => $cobject,
'commit' => $commit,
'branches' => $branches,
'tree_in' => $in_branches,
'scm' => $scmConf,
'rcommit' => $rcommit,
'large_commit' => $large,
@ -292,11 +309,11 @@ class IDF_Views_Source
$scm = IDF_Scm::get($request->project);
$commit = $match[2];
$branches = $scm->getBranches();
if ('commit' != $scm->testHash($commit)) {
if (!$scm->isValidRevision($commit)) {
// Redirect to the first branch
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
array($request->project->shortname,
$branches[0]));
$scm->getMainBranch()));
return new Pluf_HTTP_Response_Redirect($url);
}
$cobject = $scm->getCommit($commit, true);
@ -317,20 +334,17 @@ class IDF_Views_Source
$commit = $extra['commit'];
$request_file = $extra['request_file'];
$request_file_info = $extra['request_file_info'];
$bc = self::makeBreadCrumb($request->project, $commit, $request_file_info->file);
$bc = self::makeBreadCrumb($request->project, $commit, $request_file_info->fullpath);
$page_title = $bc.' - '.$title;
$cobject = $scm->getCommit($commit);
$tree_in = in_array($commit, $branches);
$in_branches = $scm->inBranches($commit, $request_file);
// try to find the previous level if it exists.
$prev = split('/', $request_file);
$l = array_pop($prev);
$previous = substr($request_file, 0, -strlen($l.' '));
$scmConf = $request->conf->getVal('scm', 'git');
$props = null;
if ($scmConf === 'svn') {
$props = $scm->getProperties($commit, $request_file);
}
$content = self::highLight($extra['mime'], $scm->getBlob($request_file_info, $commit));
$content = self::highLight($extra['mime'], $scm->getFile($request_file_info));
return Pluf_Shortcuts_RenderToResponse('idf/source/'.$scmConf.'/file.html',
array(
'page_title' => $page_title,
@ -342,7 +356,7 @@ class IDF_Views_Source
'fullpath' => $request_file,
'base' => $request_file_info->file,
'prev' => $previous,
'tree_in' => $tree_in,
'tree_in' => $in_branches,
'branches' => $branches,
'props' => $props,
),
@ -360,24 +374,24 @@ class IDF_Views_Source
$branches = $scm->getBranches();
$commit = $match[2];
$request_file = $match[3];
if ('commit' != $scm->testHash($commit, $request_file)) {
if (!$scm->isValidRevision($commit)) {
// Redirect to the first branch
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
array($request->project->shortname,
$branches[0]));
$scm->getMainBranch()));
return new Pluf_HTTP_Response_Redirect($url);
}
$request_file_info = $scm->getFileInfo($request_file, $commit);
$request_file_info = $scm->getPathInfo($request_file, $commit);
if (!$request_file_info or $request_file_info->type == 'tree') {
// Redirect to the first branch
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
array($request->project->shortname,
$branches[0]));
$scm->getMainBranch()));
return new Pluf_HTTP_Response_Redirect($url);
}
$info = self::getRequestedFileMimeType($request_file_info,
$commit, $scm);
$rep = new Pluf_HTTP_Response($scm->getBlob($request_file_info, $commit),
$rep = new Pluf_HTTP_Response($scm->getFile($request_file_info),
$info[0]);
$rep->headers['Content-Disposition'] = 'attachment; filename="'.$info[1].'"';
return $rep;
@ -393,11 +407,11 @@ class IDF_Views_Source
$commit = trim($match[2]);
$scm = IDF_Scm::get($request->project);
$branches = $scm->getBranches();
if ('commit' != $scm->testHash($commit)) {
if (!$scm->isValidRevision($commit)) {
// Redirect to the first branch
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Source::treeBase',
array($request->project->shortname,
$branches[0]));
$scm->getMainBranch()));
return new Pluf_HTTP_Response_Redirect($url);
}
$base = $request->project->shortname.'-'.$commit;
@ -424,7 +438,7 @@ class IDF_Views_Source
return $mime;
}
return self::getMimeTypeFromContent($file_info->file,
$scm->getBlob($file_info, $commit));
$scm->getFile($file_info));
}
/**
@ -502,15 +516,16 @@ class IDF_Views_Source
if (0 === strpos($fileinfo[0], 'text/')) {
return true;
}
$ext = 'mdtext php js cpp php-dist h gitignore sh py pl rb diff patch'
$ext = 'mdtext php-dist h gitignore diff patch'
.Pluf::f('idf_extra_text_ext', '');
return (in_array($fileinfo[2], explode(' ', $ext)));
$ext = array_merge(self::$supportedExtenstions, explode(' ' , $ext));
return (in_array($fileinfo[2], $ext));
}
public static function highLight($fileinfo, $content)
{
$pretty = '';
if (IDF_Views_Source::isSupportedExtension($fileinfo[2])) {
if (self::isSupportedExtension($fileinfo[2])) {
$pretty = ' prettyprint';
}
$table = array();
@ -524,13 +539,14 @@ class IDF_Views_Source
}
/**
* @param string the extension to test
* Test if an extension is supported by the syntax highlighter.
*
* @return
* @param string The extension to test
* @return bool
*/
public static function isSupportedExtension($extension)
{
return in_array($extension, IDF_Views_Source::$supportedExtenstions);
return in_array($extension, self::$supportedExtenstions);
}
/**

View File

@ -124,7 +124,7 @@ class IDF_Views_User
}
$keys = $request->user->get_idf_key_list();
if ($keys->count() > 0 and strlen($keys[0]->content) > 30) {
$ssh_key = Pluf_Template::markSafe('<span class="mono">'.Pluf_esc(substr($keys[0]->content, 0, 30)).'...</span><br /><span class="helptext">'.__('Troncated for security reasons.').'</span>');
$ssh_key = Pluf_Template::markSafe('<span class="mono">'.Pluf_esc(substr($keys[0]->content, 0, 30)).'...</span><br /><span class="helptext">'.__('Truncated for security reasons.').'</span>');
} else {
$ssh_key = __('You have not upload your public SSH key yet.');
}

View File

@ -222,7 +222,7 @@ class IDF_Views_Wiki
array($prj->id, $match[2]));
$pages = Pluf::factory('IDF_WikiPage')->getList(array('filter'=>$sql->gen()));
if ($pages->count() != 1) {
throw new Pluf_HTTP_Response_NotFound($request);
return new Pluf_HTTP_Response_NotFound($request);
}
$page = $pages[0];
$oldrev = false;
@ -231,7 +231,7 @@ class IDF_Views_Wiki
$oldrev = Pluf_Shortcuts_GetObjectOr404('IDF_WikiRevision',
$request->GET['rev']);
if ($oldrev->wikipage != $page->id or $oldrev->is_head == true) {
throw new Pluf_HTTP_Response_NotFound($request);
return new Pluf_HTTP_Response_NotFound($request);
}
}
$ptags = self::getWikiTags($prj);
@ -269,7 +269,7 @@ class IDF_Views_Wiki
$page = $oldrev->get_wikipage();
$prj->inOr404($page);
if ($oldrev->is_head == true) {
throw new Pluf_HTTP_Error404($request);
return new Pluf_HTTP_Response_NotFound($request);
}
if ($request->method == 'POST') {
$oldrev->delete();
@ -310,7 +310,7 @@ class IDF_Views_Wiki
array($prj->id, $match[2]));
$pages = Pluf::factory('IDF_WikiPage')->getList(array('filter'=>$sql->gen()));
if ($pages->count() != 1) {
throw new Pluf_HTTP_Error404($request);
return new Pluf_HTTP_Response_NotFound($request);
}
$page = $pages[0];
$title = sprintf(__('Update %s'), $page->title);

View File

@ -196,7 +196,7 @@ class IDF_WikiPage extends Pluf_Model
$user = $stag->start($this->get_submitter(), $request, '', false);
$out .= sprintf(__('<a href="%1$s" title="View page">%2$s</a>, %3$s'), $url, Pluf_esc($this->title), Pluf_esc($this->summary)).'</td>';
$out .= "\n".'<tr class="extra"><td colspan="2">
<div class="helptext right">'.sprintf(__('Creation of <a href="%s">page&nbsp;%s</a>'), $url, Pluf_esc($this->title)).', '.__('by').' '.$user.'</div></td></tr>';
<div class="helptext right">'.sprintf(__('Creation of <a href="%s">page&nbsp;%s</a>, by %s'), $url, Pluf_esc($this->title), $user).'</div></td></tr>';
return Pluf_Template::markSafe($out);
}

View File

@ -186,7 +186,7 @@ class IDF_WikiRevision extends Pluf_Model
}
$out .= '</td></tr>';
$out .= "\n".'<tr class="extra"><td colspan="2">
<div class="helptext right">'.sprintf(__('Change of <a href="%s">%s</a>'), $url, Pluf_esc($page->title)).', '.__('by').' '.$user.'</div></td></tr>';
<div class="helptext right">'.sprintf(__('Change of <a href="%s">%s</a>, by %s'), $url, Pluf_esc($page->title), $user).'</div></td></tr>';
return Pluf_Template::markSafe($out);
}

View File

@ -161,13 +161,19 @@ $cfg['db_database'] = 'website'; # put absolute path to the db if you
# are using SQLite.
#
# The extension of the downloads are limited. You can add extra
# extensions here. It must start with a space.
# extensions here. The list must start with a space.
# $cfg['idf_extra_upload_ext'] = ' ext1 ext2';
#
# By default, the size of the downloads is limited to 2MB.
# $cfg['max_upload_size'] = 2097152; // Size in bytes
# -- From this point you should not need to update anything. --
#
# Time zone
# http://www.php.net/manual/en/timezones.php
#
# $cfg['time_zone'] = 'Europe/Berlin';
$cfg['pear_path'] = '/usr/share/php';
$cfg['login_success_url'] = $cfg['url_base'].$cfg['idf_base'];

View File

@ -26,33 +26,28 @@ $base = Pluf::f('idf_base');
$ctl[] = array('regex' => '#^/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views',
'method' => 'index');
$ctl[] = array('regex' => '#^/login/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views',
'method' => 'login',
'name' => 'login_view');
$ctl[] = array('regex' => '#^/preferences/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_User',
'method' => 'myAccount');
$ctl[] = array('regex' => '#^/dashboard/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_User',
'method' => 'dashboard',
'name' => 'idf_dashboard');
$ctl[] = array('regex' => '#^/dashboard/submitted/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_User',
'method' => 'dashboard',
'params' => false,
@ -60,105 +55,88 @@ $ctl[] = array('regex' => '#^/dashboard/submitted/$#',
$ctl[] = array('regex' => '#^/u/(.*)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_User',
'method' => 'view');
$ctl[] = array('regex' => '#^/logout/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views',
'method' => 'logout');
$ctl[] = array('regex' => '#^/help/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views',
'method' => 'faq');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Project',
'method' => 'home');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/timeline/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Project',
'method' => 'timeline');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/feed/timeline/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Project',
'method' => 'timelineFeed',
'name' => 'idf_project_timeline_feed');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/feed/timeline/token/(.*)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Project',
'method' => 'timelineFeed',
'name' => 'idf_project_timeline_feed_auth');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Issue',
'method' => 'index');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/search/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Issue',
'method' => 'search');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/(\d+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Issue',
'method' => 'view');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/(\d+)/star/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Issue',
'method' => 'star');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/status/(\w+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Issue',
'method' => 'listStatus');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/label/(\d+)/(\w+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Issue',
'method' => 'listLabel');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/create/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Issue',
'method' => 'create');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/my/(\w+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Issue',
'method' => 'myIssues');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/attachment/(\d+)/(.*)$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Issue',
'method' => 'getAttachment');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/view/attachment/(\d+)/(.*)$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Issue',
'method' => 'viewAttachment');
@ -166,61 +144,51 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/issues/view/attachment/(\d+)/(.*)$#',
$ctl[] = array('regex' => '#^/p/([\-\w]+)/source/help/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Source',
'method' => 'help');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/source/tree/([^/]+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Source',
'method' => 'treeBase');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/source/tree/([^/]+)/(.*)$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Source',
'method' => 'tree');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/source/changes/([^/]+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Source',
'method' => 'changeLog');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/source/commit/([^/]+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Source',
'method' => 'commit');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/source/ddiff/([^/]+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Source',
'method' => 'downloadDiff');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/source/download/([^/]+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Source',
'method' => 'download');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/source/file/([^/]+)/(.*)$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Source',
'method' => 'getFile');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/source/treerev/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Source_Svn',
'method' => 'treeRev');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/source/changesrev/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Source_Svn',
'method' => 'changelogRev');
@ -228,43 +196,36 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/source/changesrev/$#',
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Wiki',
'method' => 'index');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/create/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Wiki',
'method' => 'create');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/search/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Wiki',
'method' => 'search');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/label/(\d+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Wiki',
'method' => 'listLabel');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/update/(.*)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Wiki',
'method' => 'update');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/doc/delrev/(\d+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Wiki',
'method' => 'deleteRev');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/page/(.*)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Wiki',
'method' => 'view');
@ -272,37 +233,31 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/page/(.*)/$#',
$ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Download',
'method' => 'index');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/label/(\d+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Download',
'method' => 'listLabel');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/(\d+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Download',
'method' => 'view');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/(\d+)/get/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Download',
'method' => 'download');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/create/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Download',
'method' => 'submit');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/(\d+)/delete/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Download',
'method' => 'delete');
@ -310,25 +265,21 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/downloads/(\d+)/delete/$#',
$ctl[] = array('regex' => '#^/p/([\-\w]+)/review/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Review',
'method' => 'index');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/review/(\d+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Review',
'method' => 'view');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/review/create/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Review',
'method' => 'create');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/review/getpatch/(\d+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Review',
'method' => 'getPatch');
@ -337,43 +288,36 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/review/getpatch/(\d+)/$#',
$ctl[] = array('regex' => '#^/p/([\-\w]+)/admin/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Project',
'method' => 'admin');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/admin/issues/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Project',
'method' => 'adminIssues');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/admin/downloads/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Project',
'method' => 'adminDownloads');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/admin/wiki/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Project',
'method' => 'adminWiki');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/admin/source/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Project',
'method' => 'adminSource');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/admin/members/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Project',
'method' => 'adminMembers');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/admin/tabs/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Project',
'method' => 'adminTabs');
@ -381,19 +325,16 @@ $ctl[] = array('regex' => '#^/p/([\-\w]+)/admin/tabs/$#',
$ctl[] = array('regex' => '#^/help/api/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views',
'method' => 'faqApi');
$ctl[] = array('regex' => '#^/api/p/([\-\w]+)/issues/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Api',
'method' => 'issuesIndex');
$ctl[] = array('regex' => '#^/api/p/([\-\w]+)/issues/create/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Api',
'method' => 'issueCreate');
@ -401,43 +342,36 @@ $ctl[] = array('regex' => '#^/api/p/([\-\w]+)/issues/create/$#',
$ctl[] = array('regex' => '#^/admin/projects/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Admin',
'method' => 'projects');
$ctl[] = array('regex' => '#^/admin/projects/(\d+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Admin',
'method' => 'projectUpdate');
$ctl[] = array('regex' => '#^/admin/projects/create/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Admin',
'method' => 'projectCreate');
$ctl[] = array('regex' => '#^/admin/projects/(\d+)/delete/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Admin',
'method' => 'projectDelete');
$ctl[] = array('regex' => '#^/admin/users/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Admin',
'method' => 'users');
$ctl[] = array('regex' => '#^/admin/users/notvalid/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Admin',
'method' => 'usersNotValidated');
$ctl[] = array('regex' => '#^/admin/users/(\d+)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_Admin',
'method' => 'userUpdate');
@ -445,53 +379,42 @@ $ctl[] = array('regex' => '#^/admin/users/(\d+)/$#',
$ctl[] = array('regex' => '#^/register/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views',
'method' => 'register');
$ctl[] = array('regex' => '#^/register/k/(.*)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views',
'method' => 'registerConfirmation');
$ctl[] = array('regex' => '#^/register/ik/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views',
'method' => 'registerInputKey');
$ctl[] = array('regex' => '#^/password/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views',
'method' => 'passwordRecoveryAsk');
$ctl[] = array('regex' => '#^/password/ik/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views',
'method' => 'passwordRecoveryInputCode');
$ctl[] = array('regex' => '#^/password/k/(.*)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views',
'method' => 'passwordRecovery');
$ctl[] = array('regex' => '#^/preferences/email/ik/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_User',
'method' => 'changeEmailInputKey');
$ctl[] = array('regex' => '#^/preferences/email/ak/(.*)/$#',
'base' => $base,
'priority' => 4,
'model' => 'IDF_Views_User',
'method' => 'changeEmailDo');
return $ctl;

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-02-27 15:21+0100\n"
"POT-Creation-Date: 2009-06-22 21:06+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -24,8 +24,9 @@ msgid "project"
msgstr ""
#: IDF/Commit.php:62 IDF/IssueComment.php:65 IDF/IssueFile.php:57
#: IDF/Issue.php:67 IDF/Review.php:71 IDF/Upload.php:79 IDF/WikiPage.php:78
#: IDF/WikiRevision.php:79 IDF/Review/FileComment.php:69
#: IDF/Issue.php:67 IDF/Review.php:71 IDF/Upload.php:85 IDF/WikiPage.php:78
#: IDF/WikiRevision.php:79 IDF/Review/Comment.php:69
#: IDF/Review/FileComment.php:69
msgid "submitter"
msgstr ""
@ -39,29 +40,28 @@ msgid "changelog"
msgstr ""
#: IDF/Commit.php:99 IDF/IssueComment.php:79 IDF/IssueFile.php:96
#: IDF/Issue.php:105 IDF/Review.php:99 IDF/Upload.php:100 IDF/WikiPage.php:100
#: IDF/WikiRevision.php:92 IDF/Review/Patch.php:83
#: IDF/Issue.php:105 IDF/Review.php:99 IDF/Upload.php:106 IDF/WikiPage.php:100
#: IDF/WikiRevision.php:92 IDF/Review/Patch.php:83 IDF/Review/Comment.php:83
#: IDF/Review/FileComment.php:76
msgid "creation date"
msgstr ""
#: IDF/Commit.php:170
#: IDF/Commit.php:172
#, php-format
msgid "New Commit %s - %s (%s)"
msgstr ""
#: IDF/Commit.php:205 IDF/Commit.php:232 IDF/Form/ReviewCreate.php:72
#: IDF/Commit.php:239
#, php-format
msgid "Commit&nbsp;%s, by %s"
msgstr ""
#: IDF/Commit.php:266 IDF/Form/ReviewCreate.php:74
#: IDF/gettexttemplates/idf/source/base.html.php:5
#: IDF/gettexttemplates/idf/source/changelog.html.php:5
msgid "Commit"
msgstr ""
#: IDF/Commit.php:205 IDF/IssueComment.php:173 IDF/Issue.php:196
#: IDF/Upload.php:185 IDF/WikiPage.php:199 IDF/WikiRevision.php:189
#: IDF/gettexttemplates/idf/source/changelog.html.php:6
msgid "by"
msgstr ""
#: IDF/Conf.php:61 IDF/Conf.php:61
msgid "key"
msgstr ""
@ -70,29 +70,32 @@ msgstr ""
msgid "value"
msgstr ""
#: IDF/IssueComment.php:51
#: IDF/IssueComment.php:51 IDF/Review/Comment.php:55
msgid "issue"
msgstr ""
#: IDF/IssueComment.php:58 IDF/IssueFile.php:49 IDF/Review/FileComment.php:62
#: IDF/IssueComment.php:58 IDF/IssueFile.php:49 IDF/Review/Comment.php:62
#: IDF/Review/FileComment.php:62
msgid "comment"
msgstr ""
#: IDF/IssueComment.php:72 IDF/WikiRevision.php:85
#: IDF/IssueComment.php:72 IDF/Upload.php:63 IDF/WikiRevision.php:85
#: IDF/Review/Comment.php:76
msgid "changes"
msgstr ""
#: IDF/IssueComment.php:73
#: IDF/IssueComment.php:73 IDF/Review/Comment.php:77
msgid "Serialized array of the changes in the issue."
msgstr ""
#: IDF/IssueComment.php:143 IDF/Issue.php:194
#: IDF/IssueComment.php:143 IDF/Issue.php:194 IDF/Review/Comment.php:147
#, php-format
msgid ""
"<a href=\"%1$s\" class=\"%2$s\" title=\"View issue\">Issue %3$d</a>, %4$s"
msgstr ""
#: IDF/IssueComment.php:151 IDF/IssueComment.php:207
#: IDF/Review/Comment.php:155 IDF/Review/Comment.php:211
#: IDF/gettexttemplates/idf/wiki/wiki-updated-email.txt.php:6
#: IDF/gettexttemplates/idf/wiki/wiki-updated-email.txt.php:11
#: IDF/gettexttemplates/idf/issues/issue-updated-email.txt.php:13
@ -101,6 +104,7 @@ msgid "Summary:"
msgstr ""
#: IDF/IssueComment.php:153 IDF/IssueComment.php:209
#: IDF/Review/Comment.php:157 IDF/Review/Comment.php:213
#: IDF/gettexttemplates/idf/review/review-created-email.txt.php:6
#: IDF/gettexttemplates/idf/review/review-updated-email.txt.php:9
#: IDF/gettexttemplates/idf/issues/issue-updated-email.txt.php:7
@ -112,6 +116,7 @@ msgid "Status:"
msgstr ""
#: IDF/IssueComment.php:155 IDF/IssueComment.php:211
#: IDF/Review/Comment.php:159 IDF/Review/Comment.php:215
#: IDF/gettexttemplates/idf/issues/issue-updated-email.txt.php:15
#: IDF/gettexttemplates/idf/issues/view.html.php:12
#: IDF/gettexttemplates/idf/issues/view.html.php:22
@ -119,6 +124,7 @@ msgid "Owner:"
msgstr ""
#: IDF/IssueComment.php:157 IDF/IssueComment.php:213 IDF/WikiRevision.php:175
#: IDF/Review/Comment.php:161 IDF/Review/Comment.php:217
#: IDF/gettexttemplates/idf/wiki/view.html.php:15
#: IDF/gettexttemplates/idf/wiki/wiki-created-email.txt.php:7
#: IDF/gettexttemplates/idf/wiki/delete.html.php:13
@ -131,7 +137,7 @@ msgstr ""
#: IDF/gettexttemplates/idf/issues/view.html.php:13
#: IDF/gettexttemplates/idf/issues/view.html.php:24
#: IDF/gettexttemplates/idf/issues/issue-created-email.txt.php:9
#: IDF/gettexttemplates/idf/downloads/view.html.php:15
#: IDF/gettexttemplates/idf/downloads/view.html.php:16
#: IDF/gettexttemplates/idf/downloads/delete.html.php:11
#: IDF/gettexttemplates/idf/downloads/download-created-email.txt.php:7
msgid "Labels:"
@ -139,10 +145,10 @@ msgstr ""
#: IDF/IssueComment.php:173
#, php-format
msgid "Comment on <a href=\"%s\" class=\"%s\">issue&nbsp;%d</a>"
msgid "Comment on <a href=\"%s\" class=\"%s\">issue&nbsp;%d</a>, by %s"
msgstr ""
#: IDF/IssueComment.php:195
#: IDF/IssueComment.php:195 IDF/Review/Comment.php:199
#, php-format
msgid "%s: Comment on issue %d - %s"
msgstr ""
@ -172,7 +178,7 @@ msgid "Other"
msgstr ""
#: IDF/IssueFile.php:102 IDF/Issue.php:111 IDF/Review.php:105
#: IDF/Upload.php:106 IDF/WikiPage.php:106
#: IDF/Upload.php:112 IDF/WikiPage.php:106
msgid "modification date"
msgstr ""
@ -189,7 +195,7 @@ msgid ""
"Interested users will get an email notification when the issue is changed."
msgstr ""
#: IDF/Issue.php:92 IDF/Review.php:86 IDF/Upload.php:87 IDF/WikiPage.php:94
#: IDF/Issue.php:92 IDF/Review.php:86 IDF/Upload.php:93 IDF/WikiPage.php:94
msgid "labels"
msgstr ""
@ -199,7 +205,7 @@ msgstr ""
#: IDF/Issue.php:196
#, php-format
msgid "Creation of <a href=\"%s\" class=\"%s\">issue&nbsp;%d</a>"
msgid "Creation of <a href=\"%s\" class=\"%s\">issue&nbsp;%d</a>, by %s"
msgstr ""
#: IDF/Issue.php:215
@ -270,33 +276,33 @@ msgstr ""
msgid "Lower case version of the name for fast searching."
msgstr ""
#: IDF/Upload.php:64
#: IDF/Upload.php:70
msgid "file"
msgstr ""
#: IDF/Upload.php:65
#: IDF/Upload.php:71
msgid "The path is relative to the upload path."
msgstr ""
#: IDF/Upload.php:72
#: IDF/Upload.php:78
msgid "file size in bytes"
msgstr ""
#: IDF/Upload.php:94
#: IDF/Upload.php:100
msgid "number of downloads"
msgstr ""
#: IDF/Upload.php:182
#: IDF/Upload.php:189
#, php-format
msgid "<a href=\"%1$s\" title=\"View download\">Download %2$d</a>, %3$s"
msgstr ""
#: IDF/Upload.php:185
#: IDF/Upload.php:192
#, php-format
msgid "Addition of <a href=\"%s\">download&nbsp;%d</a>"
msgid "Addition of <a href=\"%s\">download&nbsp;%d</a>, by %s"
msgstr ""
#: IDF/Upload.php:203
#: IDF/Upload.php:210
#, php-format
msgid "%s: Download %d added - %s"
msgstr ""
@ -361,7 +367,7 @@ msgstr ""
#: IDF/WikiPage.php:199
#, php-format
msgid "Creation of <a href=\"%s\">page&nbsp;%s</a>"
msgid "Creation of <a href=\"%s\">page&nbsp;%s</a>, by %s"
msgstr ""
#: IDF/WikiPage.php:218
@ -383,7 +389,7 @@ msgstr ""
#: IDF/WikiRevision.php:189
#, php-format
msgid "Change of <a href=\"%s\">%s</a>"
msgid "Change of <a href=\"%s\">%s</a>, by %s"
msgstr ""
#: IDF/WikiRevision.php:209
@ -427,6 +433,16 @@ msgstr ""
msgid "patch"
msgstr ""
#: IDF/Review/Comment.php:177
#, php-format
msgid "Comment on <a href=\"%s\" class=\"%s\">issue&nbsp;%d</a>"
msgstr ""
#: IDF/Review/Comment.php:177
#: IDF/gettexttemplates/idf/source/changelog.html.php:6
msgid "by"
msgstr ""
#: IDF/Plugin/SyncSvn.php:75 IDF/Plugin/SyncMercurial.php:75
#, php-format
msgid "The repository %s already exists."
@ -452,8 +468,8 @@ msgstr ""
#: IDF/Views/Wiki.php:62 IDF/Views/Wiki.php:109 IDF/Views/Wiki.php:150
#: IDF/Views/Review.php:58 IDF/Views/User.php:83 IDF/Views/Issue.php:61
#: IDF/Views/Issue.php:137 IDF/Views/Issue.php:249 IDF/Views/Issue.php:442
#: IDF/Views/Issue.php:500 IDF/Views/Download.php:65
#: IDF/Views/Issue.php:137 IDF/Views/Issue.php:247 IDF/Views/Issue.php:436
#: IDF/Views/Issue.php:494 IDF/Views/Download.php:65
#: IDF/Views/Download.php:272 IDF/Form/Upload.php:40
#: IDF/Form/UpdateUpload.php:42 IDF/Form/IssueCreate.php:50
#: IDF/Form/IssueUpdate.php:45 IDF/Form/ReviewCreate.php:45
@ -511,7 +527,7 @@ msgstr ""
msgid "Delete Old Revision of %s"
msgstr ""
#: IDF/Views/Wiki.php:316 IDF/Views/Admin.php:90 IDF/Views/Admin.php:242
#: IDF/Views/Wiki.php:316 IDF/Views/Admin.php:93 IDF/Views/Admin.php:245
#, php-format
msgid "Update %s"
msgstr ""
@ -555,7 +571,7 @@ msgstr ""
msgid "%s Project Summary"
msgstr ""
#: IDF/Views/Project.php:221 IDF/Views/Admin.php:98
#: IDF/Views/Project.php:221 IDF/Views/Admin.php:101
msgid "The project has been updated."
msgstr ""
@ -635,21 +651,21 @@ msgid "This table shows the latest reviews."
msgstr ""
#: IDF/Views/Review.php:57 IDF/Views/User.php:81 IDF/Views/Issue.php:60
#: IDF/Views/Issue.php:136 IDF/Views/Issue.php:248 IDF/Views/Issue.php:441
#: IDF/Views/Issue.php:499
#: IDF/Views/Issue.php:136 IDF/Views/Issue.php:246 IDF/Views/Issue.php:435
#: IDF/Views/Issue.php:493
msgid "Id"
msgstr ""
#: IDF/Views/Review.php:59 IDF/Views/User.php:84 IDF/Views/Issue.php:62
#: IDF/Views/Issue.php:138 IDF/Views/Issue.php:250 IDF/Views/Issue.php:443
#: IDF/Views/Issue.php:501 IDF/Form/IssueCreate.php:92
#: IDF/Form/IssueUpdate.php:88 IDF/Form/ReviewCreate.php:101
#: IDF/Views/Issue.php:138 IDF/Views/Issue.php:248 IDF/Views/Issue.php:437
#: IDF/Views/Issue.php:495 IDF/Form/IssueCreate.php:92
#: IDF/Form/IssueUpdate.php:88 IDF/Form/ReviewCreate.php:103
msgid "Status"
msgstr ""
#: IDF/Views/Review.php:60 IDF/Views/User.php:85 IDF/Views/Issue.php:63
#: IDF/Views/Issue.php:139 IDF/Views/Issue.php:251 IDF/Views/Issue.php:444
#: IDF/Views/Issue.php:502
#: IDF/Views/Issue.php:139 IDF/Views/Issue.php:249 IDF/Views/Issue.php:438
#: IDF/Views/Issue.php:496
msgid "Last Updated"
msgstr ""
@ -732,27 +748,27 @@ msgstr ""
msgid "Your new email address \"%s\" has been validated. Thank you!"
msgstr ""
#: IDF/Views/Source.php:46
#: IDF/Views/Source.php:50
#, php-format
msgid "%s Source Help"
msgstr ""
#: IDF/Views/Source.php:61
#: IDF/Views/Source.php:65
#, php-format
msgid "%1$s %2$s Change Log"
msgstr ""
#: IDF/Views/Source.php:102 IDF/Views/Source.php:151 IDF/Views/Source.php:312
#: IDF/Views/Source.php:108 IDF/Views/Source.php:152 IDF/Views/Source.php:318
#, php-format
msgid "%1$s %2$s Source Tree"
msgstr ""
#: IDF/Views/Source.php:265
#: IDF/Views/Source.php:269
#, php-format
msgid "%s Commit Details"
msgstr ""
#: IDF/Views/Source.php:266
#: IDF/Views/Source.php:270
#, php-format
msgid "%s Commit Details - %s"
msgstr ""
@ -762,8 +778,8 @@ msgstr ""
msgid "%s Open Issues"
msgstr ""
#: IDF/Views/Issue.php:67 IDF/Views/Issue.php:143 IDF/Views/Issue.php:255
#: IDF/Views/Issue.php:448 IDF/Views/Issue.php:506
#: IDF/Views/Issue.php:67 IDF/Views/Issue.php:143 IDF/Views/Issue.php:253
#: IDF/Views/Issue.php:442 IDF/Views/Issue.php:500
msgid "No issues were found."
msgstr ""
@ -791,78 +807,78 @@ msgstr ""
msgid "Submit a new issue"
msgstr ""
#: IDF/Views/Issue.php:178
#, php-format
msgid "<a href=\"%s\">Issue %d</a> has been created."
msgstr ""
#: IDF/Views/Issue.php:197
#: IDF/Views/Issue.php:194
#, php-format
msgid "Issue %s - %s (%s)"
msgstr ""
#: IDF/Views/Issue.php:232
#: IDF/Views/Issue.php:201
#, php-format
msgid "<a href=\"%s\">Issue %d</a> has been created."
msgstr ""
#: IDF/Views/Issue.php:230
#, php-format
msgid "Search Issues - %s"
msgstr ""
#: IDF/Views/Issue.php:244
#: IDF/Views/Issue.php:242
msgid "This table shows the found issues."
msgstr ""
#: IDF/Views/Issue.php:274
#: IDF/Views/Issue.php:272
#, php-format
msgid "Issue <a href=\"%s\">%d</a>: %s"
msgstr ""
#: IDF/Views/Issue.php:298
#, php-format
msgid "<a href=\"%s\">Issue %d</a> has been updated."
msgstr ""
#: IDF/Views/Issue.php:329
#: IDF/Views/Issue.php:322
#, php-format
msgid "Updated Issue %s - %s (%s)"
msgstr ""
#: IDF/Views/Issue.php:402
#: IDF/Views/Issue.php:330
#, php-format
msgid "<a href=\"%s\">Issue %d</a> has been updated."
msgstr ""
#: IDF/Views/Issue.php:396
#, php-format
msgid "View %s"
msgstr ""
#: IDF/Views/Issue.php:422
#: IDF/Views/Issue.php:416
#, php-format
msgid "%s Closed Issues"
msgstr ""
#: IDF/Views/Issue.php:432
#: IDF/Views/Issue.php:426
msgid "This table shows the closed issues."
msgstr ""
#: IDF/Views/Issue.php:474
#: IDF/Views/Issue.php:468
#, php-format
msgid "%1$s Issues with Label %2$s"
msgstr ""
#: IDF/Views/Issue.php:477
#: IDF/Views/Issue.php:471
#, php-format
msgid "%1$s Closed Issues with Label %2$s"
msgstr ""
#: IDF/Views/Issue.php:490
#: IDF/Views/Issue.php:484
#, php-format
msgid "This table shows the issues with label %s."
msgstr ""
#: IDF/Views/Issue.php:539
#: IDF/Views/Issue.php:533
msgid "The issue has been removed from your watch list."
msgstr ""
#: IDF/Views/Issue.php:542
#: IDF/Views/Issue.php:536
msgid "The issue has been added to your watch list."
msgstr ""
#: IDF/Views/Issue.php:620
#: IDF/Views/Issue.php:614
msgid "On your watch list."
msgstr ""
@ -875,7 +891,7 @@ msgstr ""
msgid "This table shows the files to download."
msgstr ""
#: IDF/Views/Download.php:64 IDF/Views/Download.php:271 IDF/Form/Upload.php:49
#: IDF/Views/Download.php:64 IDF/Views/Download.php:271 IDF/Form/Upload.php:59
#: IDF/gettexttemplates/idf/source/mercurial/tree.html.php:6
#: IDF/gettexttemplates/idf/source/svn/tree.html.php:6
#: IDF/gettexttemplates/idf/source/git/tree.html.php:6
@ -952,85 +968,89 @@ msgstr ""
msgid "Short Name"
msgstr ""
#: IDF/Views/Admin.php:66 IDF/Views/Admin.php:203
#: IDF/Views/Admin.php:66 IDF/Views/Admin.php:206
#: IDF/Form/Admin/ProjectCreate.php:48 IDF/Form/Admin/ProjectUpdate.php:42
msgid "Name"
msgstr ""
#: IDF/Views/Admin.php:71
#: IDF/Views/Admin.php:67
msgid "Repository Size"
msgstr ""
#: IDF/Views/Admin.php:73
msgid "No projects were found."
msgstr ""
#: IDF/Views/Admin.php:122
#: IDF/Views/Admin.php:125
#: IDF/gettexttemplates/idf/gadmin/projects/base.html.php:4
#: IDF/gettexttemplates/idf/gadmin/projects/create.html.php:16
#: IDF/gettexttemplates/idf/index.html.php:5
msgid "Create Project"
msgstr ""
#: IDF/Views/Admin.php:128
#: IDF/Views/Admin.php:131
msgid "The project has been created."
msgstr ""
#: IDF/Views/Admin.php:154
#: IDF/Views/Admin.php:157
#, php-format
msgid "Delete %s Project"
msgstr ""
#: IDF/Views/Admin.php:161
#: IDF/Views/Admin.php:164
msgid "The project has been deleted."
msgstr ""
#: IDF/Views/Admin.php:191
#: IDF/Views/Admin.php:194
msgid "Not Validated User List"
msgstr ""
#: IDF/Views/Admin.php:194
#: IDF/Views/Admin.php:197
#: IDF/gettexttemplates/idf/gadmin/users/base.html.php:3
msgid "User List"
msgstr ""
#: IDF/Views/Admin.php:197
#: IDF/Views/Admin.php:200
msgid "This table shows the users in the forge."
msgstr ""
#: IDF/Views/Admin.php:202
#: IDF/Views/Admin.php:205
msgid "login"
msgstr ""
#: IDF/Views/Admin.php:204 IDF/Form/Admin/UserUpdate.php:99
#: IDF/Views/Admin.php:207 IDF/Form/Admin/UserUpdate.php:99
msgid "Staff"
msgstr ""
#: IDF/Views/Admin.php:205
#: IDF/Views/Admin.php:208
msgid "Admin"
msgstr ""
#: IDF/Views/Admin.php:206 IDF/Form/Admin/UserUpdate.php:110
#: IDF/Views/Admin.php:209 IDF/Form/Admin/UserUpdate.php:110
msgid "Active"
msgstr ""
#: IDF/Views/Admin.php:207
#: IDF/Views/Admin.php:210
msgid "Last Login"
msgstr ""
#: IDF/Views/Admin.php:212
#: IDF/Views/Admin.php:215
msgid "No users were found."
msgstr ""
#: IDF/Views/Admin.php:249
#: IDF/Views/Admin.php:252
msgid "You do not have the rights to update this user."
msgstr ""
#: IDF/Views/Admin.php:265
#: IDF/Views/Admin.php:268
msgid "The user has been updated."
msgstr ""
#: IDF/Views/Admin.php:284 IDF/gettexttemplates/idf/login_form.html.php:7
#: IDF/Views/Admin.php:287 IDF/gettexttemplates/idf/login_form.html.php:7
msgid "Yes"
msgstr ""
#: IDF/Views/Admin.php:284
#: IDF/Views/Admin.php:287
msgid "No"
msgstr ""
@ -1045,44 +1065,51 @@ msgid ""
"email."
msgstr ""
#: IDF/Form/UserChangeEmail.php:80 IDF/Form/Upload.php:137
#: IDF/Form/UserChangeEmail.php:80 IDF/Form/Upload.php:147
#: IDF/Form/Register.php:114 IDF/Form/UserAccount.php:119
#: IDF/Form/Admin/ProjectCreate.php:205 IDF/Form/Admin/ProjectUpdate.php:67
#: IDF/Form/Admin/ProjectCreate.php:215 IDF/Form/Admin/ProjectUpdate.php:77
#: IDF/Form/Admin/UserUpdate.php:129 IDF/Form/Admin/ProjectDelete.php:78
#: IDF/Form/UpdateUpload.php:116 IDF/Form/WikiUpdate.php:178
#: IDF/Form/TabsConf.php:92 IDF/Form/ReviewCommentFile.php:166
#: IDF/Form/UpdateUpload.php:126 IDF/Form/WikiUpdate.php:178
#: IDF/Form/TabsConf.php:97 IDF/Form/ReviewCommentFile.php:166
#: IDF/Form/IssueCreate.php:233 IDF/Form/Password.php:61
#: IDF/Form/IssueUpdate.php:232 IDF/Form/ReviewFileComment.php:77
#: IDF/Form/WikiCreate.php:167 IDF/Form/MembersConf.php:64
#: IDF/Form/ReviewCreate.php:185
#: IDF/Form/ReviewCreate.php:187
msgid "Cannot save the model from an invalid form."
msgstr ""
#: IDF/Form/Upload.php:60 IDF/Form/UpdateUpload.php:61
#: IDF/Form/Upload.php:49 IDF/Form/UpdateUpload.php:51
#: IDF/Form/WikiUpdate.php:60 IDF/Form/ReviewCommentFile.php:69
#: IDF/Form/IssueCreate.php:59 IDF/Form/WikiCreate.php:70
#: IDF/Form/ReviewCreate.php:54
msgid "Description"
msgstr ""
#: IDF/Form/Upload.php:70 IDF/Form/UpdateUpload.php:71
#: IDF/Form/WikiUpdate.php:104 IDF/Form/ReviewCommentFile.php:92
#: IDF/Form/IssueCreate.php:120 IDF/Form/IssueUpdate.php:117
#: IDF/Form/WikiCreate.php:93
msgid "Labels"
msgstr ""
#: IDF/Form/Upload.php:75
#: IDF/Form/Upload.php:85
msgid "For security reason, you cannot upload a file with this extension."
msgstr ""
#: IDF/Form/Upload.php:108 IDF/Form/UpdateUpload.php:99
#: IDF/Form/Upload.php:118 IDF/Form/UpdateUpload.php:109
#: IDF/Form/IssueCreate.php:169
#, php-format
msgid "You cannot provide more than label from the %s class to an issue."
msgstr ""
#: IDF/Form/Upload.php:109 IDF/Form/UpdateUpload.php:100
#: IDF/Form/Upload.php:119 IDF/Form/UpdateUpload.php:110
#: IDF/Form/WikiUpdate.php:162 IDF/Form/ReviewCommentFile.php:150
#: IDF/Form/IssueCreate.php:163 IDF/Form/IssueCreate.php:170
#: IDF/Form/WikiCreate.php:151
msgid "You provided an invalid label."
msgstr ""
#: IDF/Form/Upload.php:176
#: IDF/Form/Upload.php:187
#, php-format
msgid "New download - %s (%s)"
msgstr ""
@ -1230,7 +1257,7 @@ msgstr ""
msgid "The email \"%s\" is already used."
msgstr ""
#: IDF/Form/UserAccount.php:240 IDF/Form/Admin/UserUpdate.php:211
#: IDF/Form/UserAccount.php:240 IDF/Form/Admin/UserUpdate.php:214
msgid "The passwords do not match. Please give them again."
msgstr ""
@ -1264,7 +1291,8 @@ msgstr ""
#: IDF/Form/Admin/ProjectCreate.php:63
msgid ""
"It must be unique for each project and composed only of letters and digits."
"It must be unique for each project and composed only of letters, digits and "
"dash (-) like \"my-project\"."
msgstr ""
#: IDF/Form/Admin/ProjectCreate.php:68
@ -1293,31 +1321,31 @@ msgstr ""
msgid "Project members"
msgstr ""
#: IDF/Form/Admin/ProjectCreate.php:144
#: IDF/Form/Admin/ProjectCreate.php:154
msgid ""
"Only a remote repository available throught http or https are allowed. For "
"example \"http://somewhere.com/svn/trunk\"."
msgstr ""
#: IDF/Form/Admin/ProjectCreate.php:153
#: IDF/Form/Admin/ProjectCreate.php:163
msgid ""
"This shortname contains illegal characters, please use only letters and "
"digits."
"This shortname contains illegal characters, please use only letters, digits "
"and dash (-)."
msgstr ""
#: IDF/Form/Admin/ProjectCreate.php:156
#: IDF/Form/Admin/ProjectCreate.php:166
msgid "The shortname cannot start with the dash (-) character."
msgstr ""
#: IDF/Form/Admin/ProjectCreate.php:159
#: IDF/Form/Admin/ProjectCreate.php:169
msgid "The shortname cannot end with the dash (-) character."
msgstr ""
#: IDF/Form/Admin/ProjectCreate.php:164
#: IDF/Form/Admin/ProjectCreate.php:174
msgid "This shortname is already used. Please select another one."
msgstr ""
#: IDF/Form/Admin/ProjectCreate.php:211
#: IDF/Form/Admin/ProjectCreate.php:221
msgid "Click on the Administer tab to set the description of your project."
msgstr ""
@ -1353,7 +1381,11 @@ msgid ""
"you can directly enable or disable his account here."
msgstr ""
#: IDF/Form/Admin/UserUpdate.php:196
#: IDF/Form/Admin/UserUpdate.php:183
msgid "--- is not a valid first name."
msgstr ""
#: IDF/Form/Admin/UserUpdate.php:199
msgid ""
"A user with this email already exists, please provide another email address."
msgstr ""
@ -1387,12 +1419,6 @@ msgid ""
"The page name must contains only letters, digits and the dash (-) character."
msgstr ""
#: IDF/Form/WikiUpdate.php:60 IDF/Form/ReviewCommentFile.php:69
#: IDF/Form/IssueCreate.php:59 IDF/Form/WikiCreate.php:70
#: IDF/Form/ReviewCreate.php:54
msgid "Description"
msgstr ""
#: IDF/Form/WikiUpdate.php:61 IDF/Form/ReviewCommentFile.php:70
#: IDF/Form/WikiCreate.php:71
msgid "This one line description is displayed in the list of pages."
@ -1507,7 +1533,7 @@ msgid "Initial page creation"
msgstr ""
#: IDF/Form/IssueCreate.php:69 IDF/Form/IssueUpdate.php:65
#: IDF/Form/ReviewCreate.php:81
#: IDF/Form/ReviewCreate.php:83
msgid "The \"upload_issue_path\" configuration variable was not set."
msgstr ""
@ -1527,7 +1553,7 @@ msgstr ""
msgid "You need to provide a description of the issue."
msgstr ""
#: IDF/Form/IssueCreate.php:203 IDF/Form/ReviewCreate.php:157
#: IDF/Form/IssueCreate.php:203 IDF/Form/ReviewCreate.php:159
msgid "You provided an invalid status."
msgstr ""
@ -1562,6 +1588,13 @@ msgstr ""
msgid "New Documentation Page %s - %s (%s)"
msgstr ""
#: IDF/Form/MembersConf.php:104
#, php-format
msgid "The following login is invalid: %s."
msgid_plural "The following login are invalids: %s."
msgstr[0] ""
msgstr[1] ""
#: IDF/Form/WikiConf.php:49
msgid "Predefined documentation page labels"
msgstr ""
@ -1587,33 +1620,33 @@ msgstr ""
msgid "Each issue may have at most one label with each of these classes"
msgstr ""
#: IDF/Form/ReviewCreate.php:90
#: IDF/Form/ReviewCreate.php:92
msgid "Patch"
msgstr ""
#: IDF/Form/ReviewCreate.php:117
#: IDF/Form/ReviewCreate.php:119
msgid "We were not able to parse your patch. Please provide a valid patch."
msgstr ""
#: IDF/Form/ReviewCreate.php:126
#: IDF/Form/ReviewCreate.php:128
msgid "You provided an invalid commit."
msgstr ""
#: IDF/Form/ReviewCreate.php:200
#: IDF/Form/ReviewCreate.php:202
msgid "Initial patch to be reviewed."
msgstr ""
#: IDF/Form/ReviewCreate.php:220
#: IDF/Form/ReviewCreate.php:222
#, php-format
msgid "New Code Review %s - %s (%s)"
msgstr ""
#: IDF/Scm/Mercurial.php:123 IDF/Scm/Git.php:137
#: IDF/Scm/Mercurial.php:125 IDF/Scm/Git.php:145
#, php-format
msgid "Folder %1$s not found in commit %2$s."
msgstr ""
#: IDF/Scm/Mercurial.php:140 IDF/Scm/Git.php:187
#: IDF/Scm/Mercurial.php:142 IDF/Scm/Git.php:245
#, php-format
msgid "Not a valid tree: %s."
msgstr ""
@ -1787,6 +1820,10 @@ msgstr ""
msgid "Change Log"
msgstr ""
#: IDF/gettexttemplates/idf/source/base.html.php:6
msgid "How To Get The Code"
msgstr ""
#: IDF/gettexttemplates/idf/source/mercurial/file.html.php:3
#: IDF/gettexttemplates/idf/source/mercurial/tree.html.php:3
#: IDF/gettexttemplates/idf/source/svn/file.html.php:3
@ -1838,6 +1875,7 @@ msgstr ""
#: IDF/gettexttemplates/idf/source/mercurial/file.html.php:8
#: IDF/gettexttemplates/idf/source/mercurial/tree.html.php:15
#: IDF/gettexttemplates/idf/source/svn/tree.html.php:17
#: IDF/gettexttemplates/idf/source/commit.html.php:13
#: IDF/gettexttemplates/idf/source/git/file.html.php:8
#: IDF/gettexttemplates/idf/source/git/tree.html.php:15
@ -1924,12 +1962,14 @@ msgstr ""
#: IDF/gettexttemplates/idf/source/svn/file.html.php:10
#: IDF/gettexttemplates/idf/source/svn/tree.html.php:15
#: IDF/gettexttemplates/idf/source/commit.html.php:14
#: IDF/gettexttemplates/idf/source/changelog.html.php:8
msgid "Revision:"
msgstr ""
#: IDF/gettexttemplates/idf/source/svn/file.html.php:11
#: IDF/gettexttemplates/idf/source/svn/tree.html.php:16
#: IDF/gettexttemplates/idf/source/commit.html.php:15
#: IDF/gettexttemplates/idf/source/changelog.html.php:9
msgid "Go to revision"
msgstr ""
@ -2058,9 +2098,9 @@ msgstr ""
#: IDF/gettexttemplates/idf/review/create.html.php:12
#: IDF/gettexttemplates/idf/issues/view.html.php:18
#: IDF/gettexttemplates/idf/issues/create.html.php:14
#: IDF/gettexttemplates/idf/downloads/view.html.php:7
#: IDF/gettexttemplates/idf/downloads/view.html.php:8
#: IDF/gettexttemplates/idf/downloads/delete.html.php:7
#: IDF/gettexttemplates/idf/downloads/submit.html.php:8
#: IDF/gettexttemplates/idf/downloads/submit.html.php:9
msgid "Cancel"
msgstr ""
@ -2163,7 +2203,7 @@ msgstr ""
#: IDF/gettexttemplates/idf/gadmin/projects/update.html.php:18
#: IDF/gettexttemplates/idf/wiki/view.html.php:11
#: IDF/gettexttemplates/idf/downloads/view.html.php:9
#: IDF/gettexttemplates/idf/downloads/view.html.php:10
msgid "Trash"
msgstr ""
@ -2229,6 +2269,34 @@ msgstr ""
msgid "Change Project Details"
msgstr ""
#: IDF/gettexttemplates/idf/gadmin/projects/index.html.php:3
msgid "Space Usage Statistics"
msgstr ""
#: IDF/gettexttemplates/idf/gadmin/projects/index.html.php:4
msgid "Repositories:"
msgstr ""
#: IDF/gettexttemplates/idf/gadmin/projects/index.html.php:5
#: IDF/gettexttemplates/idf/issues/issue-updated-email.txt.php:17
#: IDF/gettexttemplates/idf/issues/issue-created-email.txt.php:11
msgid "Attachments:"
msgstr ""
#: IDF/gettexttemplates/idf/gadmin/projects/index.html.php:6
#: IDF/gettexttemplates/idf/downloads/view.html.php:15
#: IDF/gettexttemplates/idf/downloads/delete.html.php:10
msgid "Downloads:"
msgstr ""
#: IDF/gettexttemplates/idf/gadmin/projects/index.html.php:7
msgid "Database:"
msgstr ""
#: IDF/gettexttemplates/idf/gadmin/projects/index.html.php:8
msgid "Total Forge:"
msgstr ""
#: IDF/gettexttemplates/idf/gadmin/projects/create.html.php:3
msgid ""
"You can select the type of repository you want. In the case of subversion, "
@ -2265,7 +2333,7 @@ msgid "Welcome"
msgstr ""
#: IDF/gettexttemplates/idf/admin/members.html.php:13
#: IDF/gettexttemplates/idf/admin/source.html.php:7
#: IDF/gettexttemplates/idf/admin/source.html.php:8
#: IDF/gettexttemplates/idf/admin/tabs.html.php:10
#: IDF/gettexttemplates/idf/admin/issue-tracking.html.php:8
#: IDF/gettexttemplates/idf/admin/wiki.html.php:8
@ -2292,6 +2360,10 @@ msgstr ""
msgid "Repository access:"
msgstr ""
#: IDF/gettexttemplates/idf/admin/source.html.php:7
msgid "Repository size:"
msgstr ""
#: IDF/gettexttemplates/idf/admin/tabs.html.php:3
msgid ""
"You can configure here the project tabs access rights and notification "
@ -2426,7 +2498,7 @@ msgstr ""
#: IDF/gettexttemplates/idf/wiki/delete.html.php:12
#: IDF/gettexttemplates/idf/review/view.html.php:24
#: IDF/gettexttemplates/idf/issues/view.html.php:20
#: IDF/gettexttemplates/idf/downloads/view.html.php:13
#: IDF/gettexttemplates/idf/downloads/view.html.php:14
#: IDF/gettexttemplates/idf/downloads/delete.html.php:9
msgid "Updated:"
msgstr ""
@ -2582,7 +2654,7 @@ msgstr ""
#: IDF/gettexttemplates/idf/user/passrecovery-inputkey.html.php:6
#: IDF/gettexttemplates/idf/user/changeemail.html.php:6
#: IDF/gettexttemplates/idf/register/inputkey.html.php:6
#: IDF/gettexttemplates/idf/downloads/submit.html.php:9
#: IDF/gettexttemplates/idf/downloads/submit.html.php:10
msgid "Instructions"
msgstr ""
@ -2913,7 +2985,7 @@ msgid ""
"If you are not interested any longer in taking\n"
"part in the life of the software project or if\n"
"you can't remember having requested the creation\n"
"of an accout, please excuse us and simply ignore\n"
"of an account, please excuse us and simply ignore\n"
"this email. \n"
"\n"
"Yours faithfully,\n"
@ -2967,6 +3039,7 @@ msgstr ""
#: IDF/gettexttemplates/idf/review/view.html.php:28
#: IDF/gettexttemplates/idf/issues/issue-created-email.txt.php:10
#: IDF/gettexttemplates/idf/downloads/download-created-email.txt.php:9
msgid "Description:"
msgstr ""
@ -3101,11 +3174,6 @@ msgstr ""
msgid "(No comments were given for this change.)"
msgstr ""
#: IDF/gettexttemplates/idf/issues/issue-updated-email.txt.php:17
#: IDF/gettexttemplates/idf/issues/issue-created-email.txt.php:11
msgid "Attachments:"
msgstr ""
#: IDF/gettexttemplates/idf/issues/issue-updated-email.txt.php:18
#: IDF/gettexttemplates/idf/issues/issue-created-email.txt.php:12
msgid "Issue:"
@ -3243,32 +3311,31 @@ msgid ""
msgstr ""
#: IDF/gettexttemplates/idf/downloads/view.html.php:5
msgid "The form contains some errors. Please correct them to update the file."
msgid "Changes"
msgstr ""
#: IDF/gettexttemplates/idf/downloads/view.html.php:6
msgid "The form contains some errors. Please correct them to update the file."
msgstr ""
#: IDF/gettexttemplates/idf/downloads/view.html.php:7
msgid "Update File"
msgstr ""
#: IDF/gettexttemplates/idf/downloads/view.html.php:8
#: IDF/gettexttemplates/idf/downloads/view.html.php:10
#: IDF/gettexttemplates/idf/downloads/view.html.php:9
#: IDF/gettexttemplates/idf/downloads/view.html.php:11
msgid "Remove this file"
msgstr ""
#: IDF/gettexttemplates/idf/downloads/view.html.php:11
#: IDF/gettexttemplates/idf/downloads/view.html.php:12
msgid "Delete this file"
msgstr ""
#: IDF/gettexttemplates/idf/downloads/view.html.php:12
#: IDF/gettexttemplates/idf/downloads/view.html.php:13
#: IDF/gettexttemplates/idf/downloads/delete.html.php:8
msgid "Uploaded:"
msgstr ""
#: IDF/gettexttemplates/idf/downloads/view.html.php:14
#: IDF/gettexttemplates/idf/downloads/delete.html.php:10
msgid "Downloads:"
msgstr ""
#: IDF/gettexttemplates/idf/downloads/delete.html.php:3
msgid ""
"<strong>Attention!</strong> If you want to delete a specific version of your "
@ -3304,10 +3371,16 @@ msgid ""
msgstr ""
#: IDF/gettexttemplates/idf/downloads/submit.html.php:6
msgid "The form contains some errors. Please correct them to submit the file."
#, php-format
msgid ""
"You can use the <a href=\"%%url%%\">Markdown syntax</a> for the changes."
msgstr ""
#: IDF/gettexttemplates/idf/downloads/submit.html.php:7
msgid "The form contains some errors. Please correct them to submit the file."
msgstr ""
#: IDF/gettexttemplates/idf/downloads/submit.html.php:8
msgid "Submit File"
msgstr ""
@ -3338,3 +3411,9 @@ msgstr ""
#: IDF/Template/Markdown.php:61
msgid "Create this documentation page"
msgstr ""
#: IDF/gettexttemplates/idf/downloads/submit.html.php:6
#, php-format
msgid ""
"You can use the <a href=\"%%url%%\">Markdown syntax</a> for the description."
msgstr ""

View File

@ -40,6 +40,7 @@ $m['IDF_Review_FileComment'] = array('relate_to' => array('IDF_Review_Patch', 'P
$m['IDF_Key'] = array('relate_to' => array('Pluf_User'));
$m['IDF_Conf'] = array('relate_to' => array('IDF_Project'));
$m['IDF_Commit'] = array('relate_to' => array('IDF_Project', 'Pluf_User'));
$m['IDF_Scm_Cache_Git'] = array('relate_to' => array('IDF_Project'));
Pluf_Signal::connect('Pluf_Template_Compiler::construct_template_tags_modifiers',
array('IDF_Middleware', 'updateTemplateTagsModifiers'));
@ -53,7 +54,8 @@ Pluf_Signal::connect('IDF_Project::created',
array('IDF_Plugin_SyncSvn', 'entry'));
Pluf_Signal::connect('Pluf_User::passwordUpdated',
array('IDF_Plugin_SyncSvn', 'entry'));
Pluf_Signal::connect('IDF_Project::preDelete',
array('IDF_Plugin_SyncSvn', 'entry'));
#
# Mercurial synchronization
Pluf_Signal::connect('IDF_Project::membershipsUpdated',

View File

@ -20,7 +20,12 @@
<th>{trans 'Repository access:'}</th>
<td>{$repository_access}
</td>
</tr>{if $remote_svn}
</tr>{if $repository_size != -1}
<tr>
<th>{trans 'Repository size:'}</th>
<td>{$repository_size|size}
</td>
</tr>{/if}{if $remote_svn}
<tr>
<th>{$form.f.svn_username.labelTag}:</th>
<td>{if $form.f.svn_username.errors}{$form.f.svn_username.fieldErrors}{/if}

View File

@ -53,6 +53,15 @@
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td colspan="2" class="helptext">
{blocktrans}
Only project members and admins have write access to the source.<br />
If you restrict the access to the source, anonymous access is<br />
not provided and the users must authenticate themselves with their<br />
password or SSH key.{/blocktrans}
</td></tr>
<tr>
<th><strong>{$form.f.review_access_rights.labelTag}:</strong></th>
<td>{if $form.f.review_access_rights.errors}{$form.f.review_access_rights.fieldErrors}{/if}
{$form.f.review_access_rights|unsafe}

View File

@ -73,15 +73,18 @@
{include 'idf/js-hotkeys.html'}
{block javascript}{/block}
{if $project}
<script type="text/javascript">{literal}
<!-- //
<script type="text/javascript" charset="utf-8">{literal}
//<![CDATA[
$(document).ready(function(){
var frag = location.hash;
if (frag.length > 3 && frag.substring(0, 3) == '#ic') {
if ($('#preview').length) {
location.hash = '#preview';
}
else if (frag.length > 3 && frag.substring(0, 3) == '#ic') {
$(frag).addClass("issue-comment-focus");
}
});
// -->{/literal}
//]]>{/literal}
</script>{/if}
</body>
</html>

View File

@ -75,15 +75,18 @@
{include 'idf/js-hotkeys.html'}
{block javascript}{/block}
{if $project}
<script type="text/javascript">{literal}
<!-- //
<script type="text/javascript" charset="utf-8">{literal}
//<![CDATA[
$(document).ready(function(){
var frag = location.hash;
if (frag.length > 3 && frag.substring(0, 3) == '#ic') {
if ($('#preview').length) {
location.hash = '#preview';
}
else if (frag.length > 3 && frag.substring(0, 3) == '#ic') {
$(frag).addClass("issue-comment-focus");
}
});
// -->{/literal}
//]]>{/literal}
</script>{/if}
</body>
</html>

View File

@ -10,3 +10,8 @@
{foreach $tags as $tag} {$tag.class|safe}:{$tag.name|safe}
{/foreach}{/if}
{trans 'Download:'} {$urlfile}
{if $file.changelog}
{trans 'Description:'}
{$file.changelog}
{/if}

View File

@ -19,6 +19,12 @@
</td>
</tr>
<tr>
<th>{$form.f.changelog.labelTag}:</th>
<td>{if $form.f.changelog.errors}{$form.f.changelog.fieldErrors}{/if}
{$form.f.changelog|unsafe}
</td>
</tr>
<tr>
<th><strong>{$form.f.file.labelTag}:</strong></th>
<td>{if $form.f.file.errors}{$form.f.file.fieldErrors}{/if}
{$form.f.file|unsafe}
@ -50,6 +56,8 @@
<p>{blocktrans}Each file must have a distinct name and file contents
cannot be changed, so be sure to include release numbers in each file
name.{/blocktrans}</p>
{assign $url = 'http://daringfireball.net/projects/markdown/syntax'}
<p>{blocktrans}You can use the <a href="{$url}">Markdown syntax</a> for the description.{/blocktrans}</p>
</div>
{/block}
{block javascript}

View File

@ -6,7 +6,11 @@
{if $deprecated}<p class="smaller">{blocktrans}<strong>Attention!</strong> This file is marked as deprecated, download it only if you are sure you need this specific version.{/blocktrans}</p>{/if}
<a href="{url 'IDF_Views_Download::download', array($project.shortname, $file.id)}">{$file}</a> - {$file.filesize|size}
</div>
{if $file.changelog}
<h2 class="changes">{trans 'Changes'}</h2>
{markdown $file.changelog, $request}
{/if}
{if $form}
{if $form.errors}
@ -19,7 +23,7 @@
{/if}
<form method="post" enctype="multipart/form-data" action=".">
<table class="form" summary="">
<table class="form download" summary="">
<tr>
<th><strong>{$form.f.summary.labelTag}:</strong></th>
<td>{if $form.f.summary.errors}{$form.f.summary.fieldErrors}{/if}
@ -27,6 +31,12 @@
</td>
</tr>
<tr>
<th>{$form.f.changelog.labelTag}:</th>
<td>{if $form.f.changelog.errors}{$form.f.changelog.fieldErrors}{/if}
{$form.f.changelog|unsafe}
</td>
</tr>
<tr>
<th>{$form.f.label1.labelTag}:</th>
<td>
{if $form.f.label1.errors}{$form.f.label1.fieldErrors}{/if}{$form.f.label1|unsafe}
@ -39,7 +49,8 @@
</tr>
<tr>
<td>&nbsp;</td>{aurl 'url', 'IDF_Views_Download::delete', array($project.shortname, $file.id)}
<td><input type="submit" value="{trans 'Update File'}" name="submit" /> | <a href="{url 'IDF_Views_Download::index', array($project.shortname)}">{trans 'Cancel'}</a> <span class="dellink"><a href="{$url}" title="{trans 'Remove this file'}"><img src="{media '/idf/img/trash.png'}" style="vertical-align: text-bottom;" alt="{trans 'Trash'}" /></a> <a href="{$url}" title="{trans 'Remove this file'}">{trans 'Delete this file'}</a></span>
<td>{* float left is a fix for Firefox < 3.5 *}
<span style="float: left;"><input type="submit" value="{trans 'Update File'}" name="submit" /> | <a href="{url 'IDF_Views_Download::index', array($project.shortname)}">{trans 'Cancel'}</a></span> <span class="dellink"><a href="{$url}" title="{trans 'Remove this file'}"><img src="{media '/idf/img/trash.png'}" style="vertical-align: text-bottom;" alt="{trans 'Trash'}" /></a> <a href="{$url}" title="{trans 'Remove this file'}">{trans 'Delete this file'}</a></span>
</td>
</tr>
</table>

View File

@ -1,8 +1,23 @@
{extends "idf/gadmin/projects/base.html"}
{block docclass}yui-t2{assign $inIndex=true}{/block}
{block docclass}yui-t3{assign $inIndex=true}{/block}
{block body}
{$projects.render}
{/block}
{block context}
<div class="issue-submit-info">
<p><strong>{trans 'Space Usage Statistics'}</strong></p>
<ul>
<li>{trans 'Repositories:'} {$size['repositories']|size}</li>
<li>{trans 'Attachments:'} {$size['attachments']|size}</li>
<li>{trans 'Downloads:'} {$size['downloads']|size}</li>
<li>{trans 'Database:'} {$size['database']|size}</li>
<li><strong>{trans 'Total Forge:'} {$size['total']|size}</strong></li>
</ul>
</div>
{/block}

View File

@ -35,9 +35,10 @@
<tr>
<td>&nbsp;</td>
<td> {aurl 'url', 'IDF_Views_Admin::projectDelete', array($project.id)}
<input type="submit" value="{trans 'Update Project'}" name="submit" />
| <a href="{url 'IDF_Views_Admin::projects'}">{trans 'Cancel'}</a> {if $isAdmin}
<span class="dellink"><a href="{$url}" title="{trans 'Delete this project'}"><img src="{media '/idf/img/trash.png'}" style="vertical-align: text-bottom;" alt="{trans 'Trash'}" /></a> <a href="{$url}" title="{trans 'Delete this project'}">{trans 'Delete this project'}</a><br /><span class="note helptext">{trans 'You will be ask to confirm.'}</span></span>{/if}
{* float left is a fix for Firefox < 3.5 *}
<span style="float: left;"><input type="submit" value="{trans 'Update Project'}" name="submit" />
| <a href="{url 'IDF_Views_Admin::projects'}">{trans 'Cancel'}</a></span> {if $isAdmin}
<span class="dellink"><a href="{$url}" title="{trans 'Delete this project'}"><img src="{media '/idf/img/trash.png'}" style="vertical-align: text-bottom;" alt="{trans 'Trash'}" /></a> <a href="{$url}" title="{trans 'Delete this project'}">{trans 'Delete this project'}</a><br /><span class="note helptext">{trans 'You will be asked to confirm.'}</span></span>{/if}
</td>
</tr>
</table>

View File

@ -10,7 +10,7 @@
<p><a href="{$url}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/add.png'}" alt="+" align="bottom" /></a> <a href="{$url}">{trans 'Create Project'}</a></p>{/if}
{else}
<ul>{foreach $projects as $p}
<li><a href="{url 'IDF_Views_Project::home', array($p.shortname)}">{$p}</a>, {$p.shortdesc}</li>
<li>{if $p.private}<img style="vertical-align: text-bottom;" src="{media '/idf/img/lock.png'}" alt="{trans 'Private project'}" /> {/if}<a href="{url 'IDF_Views_Project::home', array($p.shortname)}">{$p}</a>{if $p.shortdesc}, {$p.shortdesc}{/if}</li>
{/foreach}</ul>
{/if}
{/block}

View File

@ -11,14 +11,14 @@
{/if}
{if $preview}
<h2 class="top">{trans 'Preview'}</h2>
<h2 id="preview" class="top">{trans 'Preview'}</h2>
<div class="issue-comment issue-comment-first issue-comment-last">
<br /><pre class="issue-comment-text">{issuetext $preview, $request}</pre>
</div>
<hr />
{/if}
<form method="post" enctype="multipart/form-data" action="{url 'IDF_Views_Issue::create', array($project.shortname)}" >
<form method="post" enctype="multipart/form-data" action="{url 'IDF_Views_Issue::create', array($project.shortname)}#preview" >
<table class="form" summary="">
<tr>
<th><strong>{$form.f.summary.labelTag}:</strong></th>
@ -107,16 +107,19 @@ $(document).ready(function(){
$("#form-show-0").click(function(){
$("#form-attachment-1").show();
$("#form-block-0").hide();
return false;
});
$("#form-attachment-1 td").append("<span id=\"form-block-1\"><a id=\"form-show-1\" href=\"#\">{/literal}{trans 'Attach another file'}{literal}</a></span>");
$("#form-show-1").click(function(){
$("#form-attachment-2").show();
$("#form-block-1").hide();
return false;
});
$("#form-attachment-2 td").append("<span id=\"form-block-2\"><a id=\"form-show-2\" href=\"#\">{/literal}{trans 'Attach another file'}{literal}</a></span>");
$("#form-show-2").click(function(){
$("#form-attachment-3").show();
$("#form-block-2").hide();
return false;
});
var j=0;
for (j=1;j<4;j=j+1) {

View File

@ -53,7 +53,7 @@
{/if}
{if $preview}
<h2>{trans 'Preview'}</h2>
<h2 id="preview">{trans 'Preview'}</h2>
<div class="issue-comment issue-comment-first issue-comment-last">
<br /><pre class="issue-comment-text">{issuetext $preview, $request}</pre>
</div>
@ -160,16 +160,19 @@ $(document).ready(function(){
$("#form-show-0").click(function(){
$("#form-attachment-1").show();
$("#form-block-0").hide();
return false;
});
$("#form-attachment-1 td").append("<span id=\"form-block-1\"><a id=\"form-show-1\" href=\"#\">{/literal}{trans 'Attach another file'}{literal}</a></span>");
$("#form-show-1").click(function(){
$("#form-attachment-2").show();
$("#form-block-1").hide();
return false;
});
$("#form-attachment-2 td").append("<span id=\"form-block-2\"><a id=\"form-show-2\" href=\"#\">{/literal}{trans 'Attach another file'}{literal}</a></span>");
$("#form-show-2").click(function(){
$("#form-attachment-3").show();
$("#form-block-2").hide();
return false;
});
var j=0;
for (j=1;j<4;j=j+1) {

View File

@ -18,7 +18,7 @@ and provide the following confirmation key:
If you are not interested any longer in taking
part in the life of the software project or if
you can't remember having requested the creation
of an accout, please excuse us and simply ignore
of an account, please excuse us and simply ignore
this email.
Yours faithfully,

View File

@ -1,10 +1,12 @@
{extends "idf/base.html"}
{block tabsource} class="active"{/block}
{block subtabs}
{if !$inHelp and array_key_exists($commit, $branches)}{assign $currentCommit = $commit}{else}{assign $currentCommit = $project.getScmRoot()}{/if}
<div id="sub-tabs">
<a {if $inSourceTree}class="active" {/if}href="{url 'IDF_Views_Source::treeBase', array($project.shortname, $project.getScmRoot())}">{trans 'Source Tree'}</a> |
<a {if $inChangeLog}class="active" {/if}href="{url 'IDF_Views_Source::changeLog', array($project.shortname, $project.getScmRoot())}">{trans 'Change Log'}</a>
{if $inCommit}| {trans 'Commit'}{/if}
<a {if $inSourceTree}class="active" {/if}href="{url 'IDF_Views_Source::treeBase', array($project.shortname, $currentCommit)}">{trans 'Source Tree'}</a> |
<a {if $inChangeLog}class="active" {/if}href="{url 'IDF_Views_Source::changeLog', array($project.shortname, $currentCommit)}">{trans 'Change Log'}</a>
{if $inCommit}| {trans 'Commit'}{/if} |
<a {if $inHelp}class="active" {/if}href="{url 'IDF_Views_Source::help', array($project.shortname)}">{trans 'How To Get The Code'}</a>
</div>
{/block}
{block title}{$title}{/block}

View File

@ -27,22 +27,3 @@
</tbody>
</table>
{/block}
{block context}
{if $scm != 'svn'}
<p><strong>{trans 'Branches:'}</strong><br />
{foreach $branches as $branch}
{aurl 'url', 'IDF_Views_Source::changeLog', array($project.shortname, $branch)}
<span class="label{if $commit == $branch} active{/if}"><a href="{$url}" class="label">{$branch}</a></span><br />
{/foreach}
</p>
{else}
<form class="star" action="{url 'IDF_Views_Source_Svn::changelogRev', array($project.shortname)}" method="get">
<p><strong>{trans 'Revision:'}</strong> {$commit}</p>
<p>
<input accesskey="4" type="text" value="{$commit}" name="rev" size="5" />
<input type="submit" name="s" value="{trans 'Go to revision'}" />
</p>
</form>
{/if}
{/block}

View File

@ -38,13 +38,21 @@
{/block}
{block context}
{if $scm == 'git'}
{if $scm != 'svn'}
<p><strong>{trans 'Branches:'}</strong><br />
{foreach $branches as $branch}
{aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $branch)}
<span class="label{if $commit == $branch} active{/if}"><a href="{$url}" class="label">{$branch}</a></span><br />
{foreach $branches as $branch => $path}
{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)}
<span class="label{if in_array($branch, $tree_in)} active{/if}"><a href="{$url}" class="label">{$branch}</a></span><br />
{/foreach}
</p>
{else}
<form class="star" action="{url 'IDF_Views_Source_Svn::changelogRev', array($project.shortname)}" method="get">
<p><strong>{trans 'Revision:'}</strong> {$commit}</p>
<p>
<input accesskey="4" type="text" value="{$commit}" name="rev" size="5" />
<input type="submit" name="s" value="{trans 'Go to revision'}" />
</p>
</form>
{/if}
{/block}

View File

@ -0,0 +1,10 @@
{extends "idf/source/changelog.html"}
{block context}
<p><strong>{trans 'Branches:'}</strong><br />
{foreach $branches as $branch => $path}
{aurl 'url', 'IDF_Views_Source::changeLog', array($project.shortname, $branch)}
<span class="label{if in_array($branch, $tree_in)} active{/if}"><a href="{$url}" class="label">{if $path}{$path}{else}{$branch}{/if}</a></span><br />
{/foreach}
</p>
{/block}

View File

@ -23,9 +23,9 @@
{/block}
{block context}
<p><strong>{trans 'Branches:'}</strong><br />
{foreach $branches as $branch}
{foreach $branches as $branch => $path}
{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)}
<span class="label{if $commit == $branch} active{/if}"><a href="{$url}" class="label">{$branch}</a></span><br />
<span class="label{if in_array($branch, $tree_in)} active{/if}"><a href="{$url}" class="label">{if $path}{$path}{else}{$branch}{/if}</a></span><br />
{/foreach}
</p>
{/block}

View File

@ -1,5 +1,5 @@
{extends "idf/source/base.html"}
{block docclass}yui-t2{/block}
{block docclass}yui-t2{assign $inHelp=true}{/block}
{block body}
<p>{blocktrans}The team behind {$project} is using
@ -8,7 +8,7 @@ code.{/blocktrans}</p>
<h3>{trans 'Command-Line Access'}</h3>
<p><kbd>git clone {if $project.private}{$project.getWriteRemoteAccessUrl()}{else}{$project.getRemoteAccessUrl()}{/if}</kbd></p>
<p><kbd>git clone {$project.getSourceAccessUrl($user)}</kbd></p>
{aurl 'url', 'IDF_Views_User::myAccount'}
<p>{blocktrans}You may need to <a href="{$url}">provide your SSH key</a>. The synchronization of your SSH key can take a couple of minutes. You can learn more about <a href="http://www.google.com/search?q=public+ssh+key+authentication">SSH key authentification</a>.{/blocktrans}</p>
@ -22,7 +22,7 @@ code.{/blocktrans}</p>
git init
git add .
git commit -m "initial import"
git remote add origin {$project.getWriteRemoteAccessUrl()}
git remote add origin {$project.getWriteRemoteAccessUrl($url)}
git push origin master
</pre>

View File

@ -47,15 +47,15 @@
</tbody>
</table>
{aurl 'url', 'IDF_Views_Source::download', array($project.shortname, $commit)}
<p class="right soft"><a href="{$url}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/package-grey.png'}" alt="{trans 'Archive'}" align="bottom" /></a> <a href="{$url}">{trans 'Download this version'}</a> {trans 'or'} <kbd>git clone {if $project.private}{$project.getWriteRemoteAccessUrl()}{else}{$project.getRemoteAccessUrl()}{/if}</kbd> <a href="{url 'IDF_Views_Source::help', array($project.shortname)}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/help.png'}" alt="{trans 'Help'}" /></a></p>
<p class="right soft"><a href="{$url}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/package-grey.png'}" alt="{trans 'Archive'}" align="bottom" /></a> <a href="{$url}">{trans 'Download this version'}</a> {trans 'or'} <kbd>git clone {$project.getSourceAccessUrl($user)}</kbd> <a href="{url 'IDF_Views_Source::help', array($project.shortname)}"><img style="vertical-align: text-bottom;" src="{media '/idf/img/help.png'}" alt="{trans 'Help'}" /></a></p>
{/block}
{block context}
<p><strong>{trans 'Branches:'}</strong><br />
{foreach $branches as $branch}
{foreach $branches as $branch => $path}
{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)}
<span class="label{if $commit == $branch} active{/if}"><a href="{$url}" class="label">{$branch}</a></span><br />
<span class="label{if in_array($branch, $tree_in)} active{/if}"><a href="{$url}" class="label">{if $path}{$path}{else}{$branch}{/if}</a></span><br />
{/foreach}
</p>
{/block}

View File

@ -0,0 +1,10 @@
{extends "idf/source/changelog.html"}
{block context}
<p><strong>{trans 'Branches:'}</strong><br />
{foreach $branches as $branch => $path}
{aurl 'url', 'IDF_Views_Source::changeLog', array($project.shortname, $branch)}
<span class="label{if in_array($branch, $tree_in)} active{/if}"><a href="{$url}" class="label">{$branch}</a></span><br />
{/foreach}
</p>
{/block}

View File

@ -24,9 +24,9 @@
{block context}
<p><strong>{trans 'Branches:'}</strong><br />
{foreach $branches as $branch}
{foreach $branches as $branch => $path}
{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)}
<span class="label{if $commit == $branch} active{/if}"><a href="{$url}" class="label">{$branch}</a></span><br />
<span class="label{if in_array($branch, $tree_in)} active{/if}"><a href="{$url}" class="label">{$branch}</a></span><br />
{/foreach}
</p>
{/block}

View File

@ -1,5 +1,5 @@
{extends "idf/source/base.html"}
{block docclass}yui-t2{/block}
{block docclass}yui-t2{assign $inHelp=true}{/block}
{block body}
<p>{blocktrans}The team behind {$project} is using

View File

@ -48,9 +48,10 @@
{block context}
<p><strong>{trans 'Branches:'}</strong><br />
{foreach $branches as $branch}
{foreach $branches as $branch => $path}
{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)}
<span class="label{if $commit == $branch} active{/if}"><a href="{$url}" class="label">{$branch}</a></span><br />
<span class="label{if in_array($branch, $tree_in)} active{/if}"><a href="{$url}" class="label">{$branch}</a></span><br />
{/foreach}
</p>
{/block}

View File

@ -0,0 +1,11 @@
{extends "idf/source/changelog.html"}
{block context}
<form class="star" action="{url 'IDF_Views_Source_Svn::changelogRev', array($project.shortname)}" method="get">
<p><strong>{trans 'Revision:'}</strong> {$commit}</p>
<p>
<input accesskey="4" type="text" value="{$commit}" name="rev" size="5" />
<input type="submit" name="s" value="{trans 'Go to revision'}" />
</p>
</form>
{/block}

View File

@ -1,5 +1,5 @@
{extends "idf/source/base.html"}
{block docclass}yui-t2{/block}
{block docclass}yui-t2{assign $inHelp=true}{/block}
{block body}
<p>{blocktrans}The team behind {$project} is using

View File

@ -68,5 +68,12 @@
<input type="hidden" name="sourcefile" value="{$base}"/>
<input type="submit" name="s" value="{trans 'Go to revision'}" /></p>
</form>
<p><strong>{trans 'Branches:'}</strong><br />
{foreach $branches as $branch => $path}
{if $path}{aurl 'url', 'IDF_Views_Source::tree', array($project.shortname, 'HEAD', $path)}
{else}{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, 'HEAD')}{/if}
<span class="label{if in_array($branch, $tree_in)} active{/if}"><a href="{$url}" class="label">{$branch}</a></span><br />
{/foreach}
</p>
{/block}

View File

@ -96,6 +96,7 @@ $(document).ready(function() {
$("#form-show-0").click(function(){
$(".pass-info").show();
$("#form-block-0").hide();
return false;
});
$(".pass-info").hide();
});{/literal}

View File

@ -3,7 +3,7 @@
{block body}
{if $preview}
<h2 class="top">{trans 'Preview of the Page'}</h2>
<h2 id="preview" class="top">{trans 'Preview of the Page'}</h2>
{markdown $preview, $request}
{/if}

View File

@ -3,7 +3,7 @@
{block body}
{if $preview}
<h2 class="top">{trans 'Preview of the Page'}</h2>
<h2 id="preview" class="top">{trans 'Preview of the Page'}</h2>
{markdown $preview, $request}
{/if}

View File

@ -24,7 +24,7 @@ body {
}
.top {
margin-top: 0;
margin-top: 5px;
}
a:link {
@ -54,7 +54,6 @@ a:active{
.dellink {
float: right;
position: relative;
margin-top: -1.6em;
}
.dellink a {
@ -192,6 +191,10 @@ span.px-header-title {
white-space: nowrap;
}
span.px-header-title a, span.px-header-title a:link, span.px-header-title a:visited, span.px-header-title a:active {
color: #000;
}
/**
* Issue
*/
@ -221,7 +224,9 @@ div.issue-comment-first {
div.issue-comment-signin {
-moz-border-radius: 0 0 3px 3px;
-webkit-border-radius: 0 0 3px 3px;
-webkit-border-radius: 3px;
-webkit-border-top-left-radius: 0;
-webkit-border-top-right-radius: 0;
background-color: #d3d7cf;
padding: 4px;
}
@ -302,6 +307,7 @@ h1.project-title {
z-index: 100;
text-align: right;
padding-right: 5px;
margin-bottom: 0;
}
.note {
@ -332,10 +338,16 @@ div.container {
/**
* Tabs
*/
#main-tabs {
line-height: normal;
}
#main-tabs a {
background-color: #d3d7cf;
-moz-border-radius: 3px 3px 0 0;
-webkit-border-radius: 3px 3px 0 0;
-webkit-border-radius: 3px;
-webkit-border-bottom-left-radius: 0;
-webkit-border-bottom-right-radius: 0;
padding: 4px 4px 0 4px;
text-decoration: none;
color: #2e3436;
@ -349,7 +361,8 @@ div.container {
#sub-tabs {
background-color: #a5e26a;
-moz-border-radius: 0 3px 3px 3px;
-webkit-border-radius: 0 3px 3px 3px;
-webkit-border-radius: 3px;
-webkit-border-top-left-radius: 0;
padding: 4px;
}
@ -616,13 +629,17 @@ div.download-file {
background-repeat: no-repeat;
background-position: 1em 1em;
font-size: 140%;
margin-bottom: 3em;
margin-bottom: 1.5em;
background-color: #bbe394;
width: 40%;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
table.download {
margin-top: 1.5em;
}
/**
* Wiki
*/
@ -678,7 +695,9 @@ div.deprecated-page {
padding-left: 0px;
background-color: #eeeeec;
-moz-border-radius: 3px 0 0 3px;
-webkit-border-radius: 3px 0 0 3px;
-webkit-border-radius: 3px;
-webkit-border-top-right-radius: 0;
-webkit-border-bottom-right-radius: 0;
color: #888a85;
clear: both;
background-image: url("../img/ceondo.png");

BIN
www/media/idf/img/lock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 B