diff --git a/AUTHORS b/AUTHORS index 58b79a2..14d79e4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -9,4 +9,14 @@ Much appreciated contributors: Baptiste Michaud - Subversion synchronization Julien Issler Manuel Eidenberger - Patrick Georgi + Ciaran Gultnieks + Mehdi Kabab + Sindre R. Myren + Patrick Georgi + 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. + diff --git a/doc/syncgit.mdtext b/doc/syncgit.mdtext index 9060ab4..4591684 100644 --- a/doc/syncgit.mdtext +++ b/doc/syncgit.mdtext @@ -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 diff --git a/doc/syncsvn.mdtext b/doc/syncsvn.mdtext index 085f222..67b09d6 100644 --- a/doc/syncsvn.mdtext +++ b/doc/syncsvn.mdtext @@ -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 diff --git a/scripts/gitserve.php b/scripts/gitserve.php index 5c5b588..8dffe1a 100644 --- a/scripts/gitserve.php +++ b/scripts/gitserve.php @@ -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)); diff --git a/src/IDF/Commit.php b/src/IDF/Commit.php index 6587148..50c9402 100644 --- a/src/IDF/Commit.php +++ b/src/IDF/Commit.php @@ -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 -
'.__('Commit').' '.$this->scm_id.', '.__('by').' '.$user.'
'; +
'.sprintf(__('Commit %s, by %s'), ''.$this->scm_id.'', $user).'
'; return Pluf_Template::markSafe($out); } diff --git a/src/IDF/Diff.php b/src/IDF/Diff.php index 6dff0e9..508e048 100644 --- a/src/IDF/Diff.php +++ b/src/IDF/Diff.php @@ -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) != ' ') { diff --git a/src/IDF/Form/Admin/ProjectCreate.php b/src/IDF/Form/Admin/ProjectCreate.php index d110f09..3af968b 100644 --- a/src/IDF/Form/Admin/ProjectCreate.php +++ b/src/IDF/Form/Admin/ProjectCreate.php @@ -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.')); diff --git a/src/IDF/Form/Admin/ProjectUpdate.php b/src/IDF/Form/Admin/ProjectUpdate.php index 30cc4cb..cd9995e 100644 --- a/src/IDF/Form/Admin/ProjectUpdate.php +++ b/src/IDF/Form/Admin/ProjectUpdate.php @@ -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()) { diff --git a/src/IDF/Form/Admin/UserUpdate.php b/src/IDF/Form/Admin/UserUpdate.php index 6af03f2..6859859 100644 --- a/src/IDF/Form/Admin/UserUpdate.php +++ b/src/IDF/Form/Admin/UserUpdate.php @@ -179,9 +179,12 @@ 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), - MB_CASE_TITLE, 'UTF-8'); + $first_name = mb_convert_case(mb_strtolower($first_name), + MB_CASE_TITLE, 'UTF-8'); } return $first_name; } diff --git a/src/IDF/Form/MembersConf.php b/src/IDF/Form/MembersConf.php index 35c0413..88ae7f6 100644 --- a/src/IDF/Form/MembersConf.php +++ b/src/IDF/Form/MembersConf.php @@ -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. diff --git a/src/IDF/Form/PasswordInputKey.php b/src/IDF/Form/PasswordInputKey.php index 56aa621..23f7c02 100644 --- a/src/IDF/Form/PasswordInputKey.php +++ b/src/IDF/Form/PasswordInputKey.php @@ -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; } } diff --git a/src/IDF/Form/ReviewCreate.php b/src/IDF/Form/ReviewCreate.php index d10976f..3e5ae4e 100644 --- a/src/IDF/Form/ReviewCreate.php +++ b/src/IDF/Form/ReviewCreate.php @@ -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; diff --git a/src/IDF/Form/TabsConf.php b/src/IDF/Form/TabsConf.php index 9b4eb30..a9087c9 100644 --- a/src/IDF/Form/TabsConf.php +++ b/src/IDF/Form/TabsConf.php @@ -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()) { diff --git a/src/IDF/Form/UpdateUpload.php b/src/IDF/Form/UpdateUpload.php index 651eb52..5b9e19d 100644 --- a/src/IDF/Form/UpdateUpload.php +++ b/src/IDF/Form/UpdateUpload.php @@ -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); diff --git a/src/IDF/Form/Upload.php b/src/IDF/Form/Upload.php index fee3631..91daa03 100644 --- a/src/IDF/Form/Upload.php +++ b/src/IDF/Form/Upload.php @@ -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; diff --git a/src/IDF/Issue.php b/src/IDF/Issue.php index 22642b8..462d3e5 100644 --- a/src/IDF/Issue.php +++ b/src/IDF/Issue.php @@ -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(__('Issue %3$d, %4$s'), $url, $ic, $this->id, Pluf_esc($this->summary)).''; $out .= "\n".' -
'.sprintf(__('Creation of issue %d'), $url, $ic, $this->id).', '.__('by').' '.$user.'
'; +
'.sprintf(__('Creation of issue %d, by %s'), $url, $ic, $this->id, $user).'
'; return Pluf_Template::markSafe($out); } diff --git a/src/IDF/IssueComment.php b/src/IDF/IssueComment.php index fc002dc..16191f2 100644 --- a/src/IDF/IssueComment.php +++ b/src/IDF/IssueComment.php @@ -170,7 +170,7 @@ class IDF_IssueComment extends Pluf_Model $out .= "\n".' -
'.sprintf(__('Comment on issue %d'), $url, $ic, $issue->id).', '.__('by').' '.$user.'
'; +
'.sprintf(__('Comment on issue %d, by %s'), $url, $ic, $issue->id, $user).'
'; return Pluf_Template::markSafe($out); } diff --git a/src/IDF/IssueFile.php b/src/IDF/IssueFile.php index f0745e8..12b6375 100644 --- a/src/IDF/IssueFile.php +++ b/src/IDF/IssueFile.php @@ -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); + } } diff --git a/src/IDF/Migrations/11GitCache.php b/src/IDF/Migrations/11GitCache.php new file mode 100644 index 0000000..4fa1f31 --- /dev/null +++ b/src/IDF/Migrations/11GitCache.php @@ -0,0 +1,52 @@ +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(); + } +} \ No newline at end of file diff --git a/src/IDF/Migrations/12DownloadDesc.php b/src/IDF/Migrations/12DownloadDesc.php new file mode 100644 index 0000000..ffd5772 --- /dev/null +++ b/src/IDF/Migrations/12DownloadDesc.php @@ -0,0 +1,55 @@ +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]); + +} \ No newline at end of file diff --git a/src/IDF/Migrations/Install.php b/src/IDF/Migrations/Install.php index 6f2cb75..feb4e0b 100644 --- a/src/IDF/Migrations/Install.php +++ b/src/IDF/Migrations/Install.php @@ -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', diff --git a/src/IDF/Plugin/SyncGit/Cron.php b/src/IDF/Plugin/SyncGit/Cron.php index 7ac5df5..f8fe730 100644 --- a/src/IDF/Plugin/SyncGit/Cron.php +++ b/src/IDF/Plugin/SyncGit/Cron.php @@ -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(); + } } } } diff --git a/src/IDF/Plugin/SyncSvn.php b/src/IDF/Plugin/SyncSvn.php index 08e8eed..8aa3c49 100644 --- a/src/IDF/Plugin/SyncSvn.php +++ b/src/IDF/Plugin/SyncSvn.php @@ -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; } } @@ -83,6 +86,31 @@ class IDF_Plugin_SyncSvn $ll = exec($cmd, $output, $return); 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 diff --git a/src/IDF/Project.php b/src/IDF/Project.php index 06960d4..cdef2e6 100644 --- a/src/IDF/Project.php +++ b/src/IDF/Project.php @@ -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(); + } + } + } } diff --git a/src/IDF/Scm.php b/src/IDF/Scm.php index fcd60c9..9a8cbac 100644 --- a/src/IDF/Scm.php +++ b/src/IDF/Scm.php @@ -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); - } - return $res; + throw new Pluf_Exception_NotImplemented(); + } + + /** + * 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): + * + *
+     * array('master' => '',
+     *       'foo-branch' => '',
+     *       'design/feature1' => '')
+     * 
+ * + * But with Subversion, as the branches are managed as subfolder + * with a special folder for trunk, you would get something like: + * + *
+     * array('trunk' => 'trunk',
+     *       'foo-branch' => 'branches/foo-branch',)
+     * 
+ * + * @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,12 +345,12 @@ 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)); } - $cache->set($key, true, (int)(Pluf::f('cache_timeout', 300)/2)); } } } diff --git a/src/IDF/Scm/Cache/Git.php b/src/IDF/Scm/Cache/Git.php new file mode 100644 index 0000000..2eebc2a --- /dev/null +++ b/src/IDF/Scm/Cache/Git.php @@ -0,0 +1,134 @@ +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, + ), + ); + } +} \ No newline at end of file diff --git a/src/IDF/Scm/Git.php b/src/IDF/Scm/Git.php index 58ab1f8..9824644 100644 --- a/src/IDF/Scm/Git.php +++ b/src/IDF/Scm/Git.php @@ -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: + * + *
+     * 100644 blob bcd155e609c51b4651aab9838b270cce964670af	AUTHORS
+     * 100644 blob 87b44c5c7df3cc90c031317c1ac8efcfd8a13631	COPYING
+     * 100644 blob 2a0f899cbfe33ea755c343b06a13d7de6c22799f	INSTALL.mdtext
+     * 040000 tree 2f469c4c5318aa4ad48756874373370f6112f77b	doc
+     * 040000 tree 911e0bd2706f0069b04744d6ef41353faf06a0a7	logo
+     * 
+ * + * 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' : '', - escapeshellarg($tree), escapeshellarg($folder)); + $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', ''). - 'GIT_DIR=%s '.Pluf::f('git_path', 'git').' cat-file blob %s', - escapeshellarg($this->repo), - escapeshellarg($request_file_info->hash))); + $cmd = sprintf(Pluf::f('idf_exec_cmd_prefix', ''). + 'GIT_DIR=%s '.Pluf::f('git_path', 'git').' cat-file blob %s', + escapeshellarg($this->repo), + 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; + } } \ No newline at end of file diff --git a/src/IDF/Scm/Mercurial.php b/src/IDF/Scm/Mercurial.php index ffe4b2d..c371844 100644 --- a/src/IDF/Scm/Mercurial.php +++ b/src/IDF/Scm/Mercurial.php @@ -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', - escapeshellarg($this->repo), - $dummy, - escapeshellarg($this->repo . '/' . $request_file_info->file))); + $cmd = sprintf(Pluf::f('hg_path', 'hg').' cat -R %s -r %s %s', + escapeshellarg($this->repo), + 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); } diff --git a/src/IDF/Scm/Svn.php b/src/IDF/Scm/Svn.php index b98437f..0414ea3 100644 --- a/src/IDF/Scm/Svn.php +++ b/src/IDF/Scm/Svn.php @@ -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']; diff --git a/src/IDF/Template/IssueComment.php b/src/IDF/Template/IssueComment.php index 7bca605..7a9e4be 100644 --- a/src/IDF/Template/IssueComment.php +++ b/src/IDF/Template/IssueComment.php @@ -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', '\1', $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) { - return $this->linkIssue($issue, $m[1].' '.$m[2]); + 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 } } + /** + * General call back to convert commits to HTML links. + * + * @param array $m Single regex match. + * @return string Content with converted commits. + */ + function callbackCommits($m) + { + $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]; + } + + /** + * 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) { - if ($this->scm->testHash($m[2]) != 'commit') { - return $m[0]; + $co = $this->scm->getCommit($m[0]); + if (!$co) { + return $m[0]; // not a commit. } - $co = $this->scm->getCommit($m[2]); - return ''.$m[1].$m[2].''; + return ''.$m[0].''; } 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].''.$m[2].''; + return $m[1].''.$m[2].''; } 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 ''.Pluf_esc($title).''; + array($this->project->shortname, $issue->id)).$anchor.'" class="'.$ic.'" title="'.Pluf_esc($issue->summary).'">'.Pluf_esc($title).''; } } diff --git a/src/IDF/Template/MarkdownPrefilter.php b/src/IDF/Template/MarkdownPrefilter.php index 114369a..b3d0c32 100644 --- a/src/IDF/Template/MarkdownPrefilter.php +++ b/src/IDF/Template/MarkdownPrefilter.php @@ -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'), diff --git a/src/IDF/Tests/TestGit.php b/src/IDF/Tests/TestGit.php index e7492cb..9baf254 100644 --- a/src/IDF/Tests/TestGit.php +++ b/src/IDF/Tests/TestGit.php @@ -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); } -} \ No newline at end of file + + /** + * 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)); + } + + } +} diff --git a/src/IDF/Tests/data/git-log-iso-8859-1.txt b/src/IDF/Tests/data/git-log-iso-8859-1.txt new file mode 100644 index 0000000..c42a5ef --- /dev/null +++ b/src/IDF/Tests/data/git-log-iso-8859-1.txt @@ -0,0 +1,19 @@ +commit 11531a9dbc64a65150f2f38fbea7cef9d478a123 +Author: unknown +Date: Fri Jul 3 01:44:11 2009 +0200 + + Quick Profiler entfernt + +commit 11531a9dbc64a65150f2f38fbea7cef9d478a123 +Author: unknown +Date: Wed Jul 1 15:51:22 2009 +0200 + + Anwendungsmenu Divider eingefügt + +commit 11531a9dbc64a65150f2f38fbea7cef9d478a123 +Author: unknown +Date: Wed Jul 1 15:05:41 2009 +0200 + + Anwendungen aufäumen + + diff --git a/src/IDF/Upload.php b/src/IDF/Upload.php index 031552f..4f849be 100644 --- a/src/IDF/Upload.php +++ b/src/IDF/Upload.php @@ -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(__('Download %2$d, %3$s'), $url, $this->id, Pluf_esc($this->summary)).''; $out .= ''; $out .= "\n".' -
'.sprintf(__('Addition of download %d'), $url, $this->id).', '.__('by').' '.$user.'
'; +
'.sprintf(__('Addition of download %d, by %s'), $url, $this->id, $user).'
'; return Pluf_Template::markSafe($out); } diff --git a/src/IDF/Views/Admin.php b/src/IDF/Views/Admin.php index f7c0103..44a2b4a 100644 --- a/src/IDF/Views/Admin.php +++ b/src/IDF/Views/Admin.php @@ -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); } @@ -283,4 +286,89 @@ function IDF_Views_Admin_bool($field, $item) $img = ($item->$field) ? 'day' : 'night'; $text = ($item->$field) ? __('Yes') : __('No'); return sprintf('%s ', $img, $text); -} \ No newline at end of file +} + +/** + * 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; +} diff --git a/src/IDF/Views/Download.php b/src/IDF/Views/Download.php index 396dfd9..072188a 100644 --- a/src/IDF/Views/Download.php +++ b/src/IDF/Views/Download.php @@ -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); } diff --git a/src/IDF/Views/Issue.php b/src/IDF/Views/Issue.php index 68f3ad6..79789b2 100644 --- a/src/IDF/Views/Issue.php +++ b/src/IDF/Views/Issue.php @@ -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', - array($prj->shortname, $issue->id)); - $request->user->setMessage(sprintf(__('Issue %d has been created.'), $urlissue, $issue->id)); + $url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::view', + array($prj->shortname, $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(__('Issue %d 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(__('Issue %d 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(__('Issue %d 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); diff --git a/src/IDF/Views/Project.php b/src/IDF/Views/Project.php index 6766161..60710d5 100644 --- a/src/IDF/Views/Project.php +++ b/src/IDF/Views/Project.php @@ -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, ), diff --git a/src/IDF/Views/Review.php b/src/IDF/Views/Review.php index 1ebe5c8..67596ef 100644 --- a/src/IDF/Views/Review.php +++ b/src/IDF/Views/Review.php @@ -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)}, diff --git a/src/IDF/Views/Source.php b/src/IDF/Views/Source.php index 99152fd..541be76 100644 --- a/src/IDF/Views/Source.php +++ b/src/IDF/Views/Source.php @@ -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); - } + $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 { - $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)); - $cache->set($key, $res); - } - } catch (Exception $e) { - return new Pluf_HTTP_Response_Redirect($fburl); + $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->getTree($commit, $request_file)); + $cache->set($key, $res); } // 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); - } + $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)); + $props = $scm->getProperties($commit, $request_file); + $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 - * - * @return + * Test if an extension is supported by the syntax highlighter. + * + * @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); } /** diff --git a/src/IDF/Views/User.php b/src/IDF/Views/User.php index 75c130d..d63e2a6 100644 --- a/src/IDF/Views/User.php +++ b/src/IDF/Views/User.php @@ -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(''.Pluf_esc(substr($keys[0]->content, 0, 30)).'...
'.__('Troncated for security reasons.').''); + $ssh_key = Pluf_Template::markSafe(''.Pluf_esc(substr($keys[0]->content, 0, 30)).'...
'.__('Truncated for security reasons.').''); } else { $ssh_key = __('You have not upload your public SSH key yet.'); } diff --git a/src/IDF/Views/Wiki.php b/src/IDF/Views/Wiki.php index dcd4eb4..22a9ff1 100644 --- a/src/IDF/Views/Wiki.php +++ b/src/IDF/Views/Wiki.php @@ -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); diff --git a/src/IDF/WikiPage.php b/src/IDF/WikiPage.php index 04cacf8..2733e4f 100644 --- a/src/IDF/WikiPage.php +++ b/src/IDF/WikiPage.php @@ -196,7 +196,7 @@ class IDF_WikiPage extends Pluf_Model $user = $stag->start($this->get_submitter(), $request, '', false); $out .= sprintf(__('%2$s, %3$s'), $url, Pluf_esc($this->title), Pluf_esc($this->summary)).''; $out .= "\n".' -
'.sprintf(__('Creation of page %s'), $url, Pluf_esc($this->title)).', '.__('by').' '.$user.'
'; +
'.sprintf(__('Creation of page %s, by %s'), $url, Pluf_esc($this->title), $user).'
'; return Pluf_Template::markSafe($out); } diff --git a/src/IDF/WikiRevision.php b/src/IDF/WikiRevision.php index ecc6181..8dbf070 100644 --- a/src/IDF/WikiRevision.php +++ b/src/IDF/WikiRevision.php @@ -186,7 +186,7 @@ class IDF_WikiRevision extends Pluf_Model } $out .= ''; $out .= "\n".' -
'.sprintf(__('Change of %s'), $url, Pluf_esc($page->title)).', '.__('by').' '.$user.'
'; +
'.sprintf(__('Change of %s, by %s'), $url, Pluf_esc($page->title), $user).'
'; return Pluf_Template::markSafe($out); } diff --git a/src/IDF/conf/idf.php-dist b/src/IDF/conf/idf.php-dist index 2a8d958..d2974bc 100644 --- a/src/IDF/conf/idf.php-dist +++ b/src/IDF/conf/idf.php-dist @@ -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']; diff --git a/src/IDF/conf/urls.php b/src/IDF/conf/urls.php index b75c869..15b8935 100644 --- a/src/IDF/conf/urls.php +++ b/src/IDF/conf/urls.php @@ -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; diff --git a/src/IDF/locale/fr/idf.po b/src/IDF/locale/fr/idf.po index c215502..73aa046 100644 --- a/src/IDF/locale/fr/idf.po +++ b/src/IDF/locale/fr/idf.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: InDefero\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2009-02-27 15:21+0100\n" -"PO-Revision-Date: 2009-02-27 15:25+0100\n" +"POT-Creation-Date: 2009-06-22 21:06+0200\n" +"PO-Revision-Date: 2009-06-22 21:07+0100\n" "Last-Translator: Loïc d'Anterroches \n" "Language-Team: Translation team \n" "MIME-Version: 1.0\n" @@ -32,9 +32,10 @@ msgstr "projet" #: IDF/IssueFile.php:57 #: IDF/Issue.php:67 #: IDF/Review.php:71 -#: IDF/Upload.php:79 +#: 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 "auteur" @@ -58,37 +59,32 @@ msgstr "changements" #: IDF/IssueFile.php:96 #: IDF/Issue.php:105 #: IDF/Review.php:99 -#: IDF/Upload.php:100 +#: 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 "date de création" -#: IDF/Commit.php:170 +#: IDF/Commit.php:172 #, php-format msgid "New Commit %s - %s (%s)" msgstr "Nouveau commit %s - %s (%s)" -#: IDF/Commit.php:205 -#: IDF/Commit.php:232 -#: IDF/Form/ReviewCreate.php:72 +#: IDF/Commit.php:239 +#, php-format +msgid "Commit %s, by %s" +msgstr "Commit %s, par %s" + +#: 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 "Commit" -#: 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 "par" - #: IDF/Conf.php:61 msgid "key" msgstr "clef" @@ -98,32 +94,40 @@ msgid "value" msgstr "valeur" #: IDF/IssueComment.php:51 +#: IDF/Review/Comment.php:55 msgid "issue" msgstr "ticket" #: IDF/IssueComment.php:58 #: IDF/IssueFile.php:49 +#: IDF/Review/Comment.php:62 #: IDF/Review/FileComment.php:62 msgid "comment" msgstr "commentaire" #: IDF/IssueComment.php:72 +#: IDF/Upload.php:63 #: IDF/WikiRevision.php:85 +#: IDF/Review/Comment.php:76 msgid "changes" msgstr "changements" #: IDF/IssueComment.php:73 +#: IDF/Review/Comment.php:77 msgid "Serialized array of the changes in the issue." msgstr "Tableau sérialisé des changements du ticket." #: IDF/IssueComment.php:143 #: IDF/Issue.php:194 +#: IDF/Review/Comment.php:147 #, php-format msgid "Issue %3$d, %4$s" msgstr "Ticket %3$d, %4$s" #: 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 @@ -133,6 +137,8 @@ msgstr "Résumé :" #: 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 @@ -145,6 +151,8 @@ msgstr "Statut :" #: 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 @@ -154,6 +162,8 @@ msgstr "Propriétaire :" #: 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 @@ -166,7 +176,7 @@ msgstr "Propriétaire :" #: 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:" @@ -174,10 +184,11 @@ msgstr "Étiquettes :" #: IDF/IssueComment.php:173 #, php-format -msgid "Comment on issue %d" -msgstr "Commentaire sur le ticket %d" +msgid "Comment on issue %d, by %s" +msgstr "Commentaire sur le ticket %d, par %s" #: IDF/IssueComment.php:195 +#: IDF/Review/Comment.php:199 #, php-format msgid "%s: Comment on issue %d - %s" msgstr "%s: Commentaire sur le ticket %d - %s" @@ -209,7 +220,7 @@ msgstr "Autre" #: IDF/IssueFile.php:102 #: IDF/Issue.php:111 #: IDF/Review.php:105 -#: IDF/Upload.php:106 +#: IDF/Upload.php:112 #: IDF/WikiPage.php:106 msgid "modification date" msgstr "date de modification" @@ -229,7 +240,7 @@ msgstr "Les personnes intéressées reçoivent un email d'information quand le t #: IDF/Issue.php:92 #: IDF/Review.php:86 -#: IDF/Upload.php:87 +#: IDF/Upload.php:93 #: IDF/WikiPage.php:94 msgid "labels" msgstr "étiquettes" @@ -241,8 +252,8 @@ msgstr "statut" #: IDF/Issue.php:196 #, php-format -msgid "Creation of issue %d" -msgstr "Création du ticket %d" +msgid "Creation of issue %d, by %s" +msgstr "Création du ticket %d, par %s" #: IDF/Issue.php:215 #, php-format @@ -312,33 +323,33 @@ msgstr "lcname" msgid "Lower case version of the name for fast searching." msgstr "Version minuscule du nom pour chercher rapidement." -#: IDF/Upload.php:64 +#: IDF/Upload.php:70 msgid "file" msgstr "fichier" -#: IDF/Upload.php:65 +#: IDF/Upload.php:71 msgid "The path is relative to the upload path." msgstr "Le chemin est relatif au répertoire de mise en ligne." -#: IDF/Upload.php:72 +#: IDF/Upload.php:78 msgid "file size in bytes" msgstr "taille du fichier en octets" -#: IDF/Upload.php:94 +#: IDF/Upload.php:100 msgid "number of downloads" msgstr "nombre de téléchargements" -#: IDF/Upload.php:182 +#: IDF/Upload.php:189 #, php-format msgid "Download %2$d, %3$s" msgstr "Téléchargement %2$d, %3$s" -#: IDF/Upload.php:185 +#: IDF/Upload.php:192 #, php-format -msgid "Addition of download %d" -msgstr "Création du téléchargement %d" +msgid "Addition of download %d, by %s" +msgstr "Création du téléchargement %d, par %s" -#: IDF/Upload.php:203 +#: IDF/Upload.php:210 #, php-format msgid "%s: Download %d added - %s" msgstr "%s: Ajout du téléchargement %d - %s" @@ -405,8 +416,8 @@ msgstr "%2$s, %3$s" #: IDF/WikiPage.php:199 #, php-format -msgid "Creation of page %s" -msgstr "Création de la page %s" +msgid "Creation of page %s, by %s" +msgstr "Création de la page %s, par %s" #: IDF/WikiPage.php:218 #, php-format @@ -427,8 +438,8 @@ msgstr "contenu" #: IDF/WikiRevision.php:189 #, php-format -msgid "Change of %s" -msgstr "Changement de %s" +msgid "Change of %s, by %s" +msgstr "Changement de %s, par %s" #: IDF/WikiRevision.php:209 #, php-format @@ -472,6 +483,16 @@ msgstr "commit" msgid "patch" msgstr "patch" +#: IDF/Review/Comment.php:177 +#, php-format +msgid "Comment on issue %d" +msgstr "Commentaire sur le ticket %d" + +#: IDF/Review/Comment.php:177 +#: IDF/gettexttemplates/idf/source/changelog.html.php:6 +msgid "by" +msgstr "par" + #: IDF/Plugin/SyncSvn.php:75 #: IDF/Plugin/SyncMercurial.php:75 #, php-format @@ -505,9 +526,9 @@ msgstr "Titre" #: 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/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 @@ -574,8 +595,8 @@ msgid "Delete Old Revision of %s" msgstr "Suppression de la vieille version de %s" #: IDF/Views/Wiki.php:316 -#: IDF/Views/Admin.php:90 -#: IDF/Views/Admin.php:242 +#: IDF/Views/Admin.php:93 +#: IDF/Views/Admin.php:245 #, php-format msgid "Update %s" msgstr "Mise à jour de %s" @@ -620,7 +641,7 @@ msgid "%s Project Summary" msgstr "Résumé du projet %s" #: IDF/Views/Project.php:221 -#: IDF/Views/Admin.php:98 +#: IDF/Views/Admin.php:101 msgid "The project has been updated." msgstr "Le projet a été mis à jour." @@ -706,9 +727,9 @@ msgstr "Ce tableau montre les dernières revues de code." #: 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:246 +#: IDF/Views/Issue.php:435 +#: IDF/Views/Issue.php:493 msgid "Id" msgstr "Id" @@ -716,12 +737,12 @@ msgstr "Id" #: 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/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:101 +#: IDF/Form/ReviewCreate.php:103 msgid "Status" msgstr "Statut" @@ -729,9 +750,9 @@ msgstr "Statut" #: 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:249 +#: IDF/Views/Issue.php:438 +#: IDF/Views/Issue.php:496 msgid "Last Updated" msgstr "Dernière mise à jour" @@ -817,29 +838,29 @@ msgstr "Confirmez le changement d'adresse" msgid "Your new email address \"%s\" has been validated. Thank you!" msgstr "Votre nouvelle adresse email \"%s\" a été validée. Merci !" -#: IDF/Views/Source.php:46 +#: IDF/Views/Source.php:50 #, php-format msgid "%s Source Help" msgstr "Aide des sources de %s" -#: IDF/Views/Source.php:61 +#: IDF/Views/Source.php:65 #, php-format msgid "%1$s %2$s Change Log" msgstr "Changements %2$s de %1$s" -#: 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 "Arbre des sources %2$s de %1$s" -#: IDF/Views/Source.php:265 +#: IDF/Views/Source.php:269 #, php-format msgid "%s Commit Details" msgstr "Détails d'un commit de %s" -#: IDF/Views/Source.php:266 +#: IDF/Views/Source.php:270 #, php-format msgid "%s Commit Details - %s" msgstr "Détails d'un commit de %s - %s" @@ -851,9 +872,9 @@ msgstr "Tickets ouverts de %s" #: 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:253 +#: IDF/Views/Issue.php:442 +#: IDF/Views/Issue.php:500 msgid "No issues were found." msgstr "Aucun ticket n'a été trouvé." @@ -881,78 +902,78 @@ msgstr "Mes tickets en cours pour %s" msgid "Submit a new issue" msgstr "Soumettre un nouveau ticket" -#: IDF/Views/Issue.php:178 -#, php-format -msgid "Issue %d has been created." -msgstr "Le ticket %d a été créé." - -#: IDF/Views/Issue.php:197 +#: IDF/Views/Issue.php:194 #, php-format msgid "Issue %s - %s (%s)" msgstr "Ticket %s - %s (%s)" -#: IDF/Views/Issue.php:232 +#: IDF/Views/Issue.php:201 +#, php-format +msgid "Issue %d has been created." +msgstr "Le ticket %d a été créé." + +#: IDF/Views/Issue.php:230 #, php-format msgid "Search Issues - %s" msgstr "Recherche de tickets - %s" -#: IDF/Views/Issue.php:244 +#: IDF/Views/Issue.php:242 msgid "This table shows the found issues." msgstr "Ce tableau montre les tickets trouvés." -#: IDF/Views/Issue.php:274 +#: IDF/Views/Issue.php:272 #, php-format msgid "Issue %d: %s" msgstr "Ticket %d : %s" -#: IDF/Views/Issue.php:298 -#, php-format -msgid "Issue %d has been updated." -msgstr "Le ticket %d a été mise à jour." - -#: IDF/Views/Issue.php:329 +#: IDF/Views/Issue.php:322 #, php-format msgid "Updated Issue %s - %s (%s)" msgstr "Mise à jour ticket %s - %s (%s)" -#: IDF/Views/Issue.php:402 +#: IDF/Views/Issue.php:330 +#, php-format +msgid "Issue %d has been updated." +msgstr "Le ticket %d a été mis à jour." + +#: IDF/Views/Issue.php:396 #, php-format msgid "View %s" msgstr "Voir %s" -#: IDF/Views/Issue.php:422 +#: IDF/Views/Issue.php:416 #, php-format msgid "%s Closed Issues" msgstr "Tickets fermés de %s" -#: IDF/Views/Issue.php:432 +#: IDF/Views/Issue.php:426 msgid "This table shows the closed issues." msgstr "Ce tableau montre les tickets fermés." -#: IDF/Views/Issue.php:474 +#: IDF/Views/Issue.php:468 #, php-format msgid "%1$s Issues with Label %2$s" msgstr "%1$s tickets avec l'étiquette %2$s" -#: IDF/Views/Issue.php:477 +#: IDF/Views/Issue.php:471 #, php-format msgid "%1$s Closed Issues with Label %2$s" msgstr "Tickets fermés de %1$s avec l'étiquette %2$s" -#: IDF/Views/Issue.php:490 +#: IDF/Views/Issue.php:484 #, php-format msgid "This table shows the issues with label %s." msgstr "Ce tableau montre les tickets avec l'étiquette %s." -#: IDF/Views/Issue.php:539 +#: IDF/Views/Issue.php:533 msgid "The issue has been removed from your watch list." msgstr "Le ticket a été supprimé de votre liste de surveillance." -#: IDF/Views/Issue.php:542 +#: IDF/Views/Issue.php:536 msgid "The issue has been added to your watch list." msgstr "Le ticket a été ajouté à votre liste de surveillance." -#: IDF/Views/Issue.php:620 +#: IDF/Views/Issue.php:614 msgid "On your watch list." msgstr "Dans votre liste de surveillance." @@ -967,7 +988,7 @@ msgstr "Ce tableau présente la liste des fichiers en téléchargement." #: IDF/Views/Download.php:64 #: IDF/Views/Download.php:271 -#: IDF/Form/Upload.php:49 +#: 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 @@ -1049,89 +1070,93 @@ msgid "Short Name" msgstr "Nom court" #: IDF/Views/Admin.php:66 -#: IDF/Views/Admin.php:203 +#: IDF/Views/Admin.php:206 #: IDF/Form/Admin/ProjectCreate.php:48 #: IDF/Form/Admin/ProjectUpdate.php:42 msgid "Name" msgstr "Nom" -#: IDF/Views/Admin.php:71 +#: IDF/Views/Admin.php:67 +msgid "Repository Size" +msgstr "Taille du dépôt" + +#: IDF/Views/Admin.php:73 msgid "No projects were found." msgstr "Aucun projet n'a été trouvé." -#: 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 "Créer un projet" -#: IDF/Views/Admin.php:128 +#: IDF/Views/Admin.php:131 msgid "The project has been created." msgstr "Le projet a été créé." -#: IDF/Views/Admin.php:154 +#: IDF/Views/Admin.php:157 #, php-format msgid "Delete %s Project" msgstr "Supprimer le projet %s" -#: IDF/Views/Admin.php:161 +#: IDF/Views/Admin.php:164 msgid "The project has been deleted." msgstr "Le projet a été supprimé." -#: IDF/Views/Admin.php:191 +#: IDF/Views/Admin.php:194 msgid "Not Validated User List" msgstr "Liste des utilisateurs non confirmés" -#: IDF/Views/Admin.php:194 +#: IDF/Views/Admin.php:197 #: IDF/gettexttemplates/idf/gadmin/users/base.html.php:3 msgid "User List" msgstr "Liste des utilisateurs" -#: IDF/Views/Admin.php:197 +#: IDF/Views/Admin.php:200 msgid "This table shows the users in the forge." msgstr "Ce tableau montre les utilisateurs de la forge." -#: IDF/Views/Admin.php:202 +#: IDF/Views/Admin.php:205 msgid "login" msgstr "identifiant" -#: IDF/Views/Admin.php:204 +#: IDF/Views/Admin.php:207 #: IDF/Form/Admin/UserUpdate.php:99 msgid "Staff" msgstr "Staff" -#: IDF/Views/Admin.php:205 +#: IDF/Views/Admin.php:208 msgid "Admin" msgstr "Admin" -#: IDF/Views/Admin.php:206 +#: IDF/Views/Admin.php:209 #: IDF/Form/Admin/UserUpdate.php:110 msgid "Active" msgstr "Actif" -#: IDF/Views/Admin.php:207 +#: IDF/Views/Admin.php:210 msgid "Last Login" msgstr "Dernier login" -#: IDF/Views/Admin.php:212 +#: IDF/Views/Admin.php:215 msgid "No users were found." msgstr "Aucun utilisateur trouvé." -#: IDF/Views/Admin.php:249 +#: IDF/Views/Admin.php:252 msgid "You do not have the rights to update this user." msgstr "Vous n'avez pas les droits pour mettre à jour cet utilisateur." -#: IDF/Views/Admin.php:265 +#: IDF/Views/Admin.php:268 msgid "The user has been updated." msgstr "L'utilisateur a été mis à jour." -#: IDF/Views/Admin.php:284 +#: IDF/Views/Admin.php:287 #: IDF/gettexttemplates/idf/login_form.html.php:7 msgid "Yes" msgstr "Oui" -#: IDF/Views/Admin.php:284 +#: IDF/Views/Admin.php:287 msgid "No" msgstr "Non" @@ -1146,16 +1171,16 @@ msgid "The validation key is not valid. Please copy/paste it from your confirmat msgstr "Nous sommes désolés mais cette clef de confirmation est invalide. Vous devriez peut-être faire directement un copier/coller depuis votre email de confirmation." #: IDF/Form/UserChangeEmail.php:80 -#: IDF/Form/Upload.php:137 +#: 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/UpdateUpload.php:126 #: IDF/Form/WikiUpdate.php:178 -#: IDF/Form/TabsConf.php:92 +#: IDF/Form/TabsConf.php:97 #: IDF/Form/ReviewCommentFile.php:166 #: IDF/Form/IssueCreate.php:233 #: IDF/Form/Password.php:61 @@ -1163,12 +1188,22 @@ msgstr "Nous sommes désolés mais cette clef de confirmation est invalide. Vous #: 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 "Ne peut pas sauvegarder le modèle depuis un formulaire invalide." -#: 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 "Description" + +#: 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 @@ -1177,19 +1212,19 @@ msgstr "Ne peut pas sauvegarder le modèle depuis un formulaire invalide." msgid "Labels" msgstr "Étiquettes" -#: IDF/Form/Upload.php:75 +#: IDF/Form/Upload.php:85 msgid "For security reason, you cannot upload a file with this extension." msgstr "Pour des raisons de sécurité, vous ne pouvez pas mettre en ligne un fichier avec cette extension." -#: 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 "Vous ne pouvez pas fournir plus d'une étiquette de la classe %s à un ticket." -#: 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 @@ -1198,7 +1233,7 @@ msgstr "Vous ne pouvez pas fournir plus d'une étiquette de la classe %s à un t msgid "You provided an invalid label." msgstr "Vous avez donné une étiquette invalide." -#: IDF/Form/Upload.php:176 +#: IDF/Form/Upload.php:187 #, php-format msgid "New download - %s (%s)" msgstr "Nouveau téléchargement - %s (%s)" @@ -1341,7 +1376,7 @@ msgid "The email \"%s\" is already used." msgstr "L'adresse email \"%s\" est déjà utilisée." #: IDF/Form/UserAccount.php:240 -#: IDF/Form/Admin/UserUpdate.php:211 +#: IDF/Form/Admin/UserUpdate.php:214 msgid "The passwords do not match. Please give them again." msgstr "Les mots de passe ne sont pas identiques, veuillez les donner de nouveau." @@ -1374,8 +1409,8 @@ msgid "Shortname" msgstr "Nom court" #: IDF/Form/Admin/ProjectCreate.php:63 -msgid "It must be unique for each project and composed only of letters and digits." -msgstr "Il doit être unique pour chaque projet et ne doit contenir que des lettres et des chiffres." +msgid "It must be unique for each project and composed only of letters, digits and dash (-) like \"my-project\"." +msgstr "Il doit être unique pour chaque projet et ne doit contenir que des lettres, des chiffres et le tiret (-) comme \"mon-projet\"." #: IDF/Form/Admin/ProjectCreate.php:68 msgid "Repository type" @@ -1409,27 +1444,27 @@ msgstr "Propriétaires du projet" msgid "Project members" msgstr "Membres du projet" -#: 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 "Only a remote repository available throught http or https are allowed. For example \"http://somewhere.com/svn/trunk\"." -#: IDF/Form/Admin/ProjectCreate.php:153 -msgid "This shortname contains illegal characters, please use only letters and digits." -msgstr "Ce nom court contient des caractères non autorisés, il ne doit être composé que de lettres et de chiffres." +#: IDF/Form/Admin/ProjectCreate.php:163 +msgid "This shortname contains illegal characters, please use only letters, digits and dash (-)." +msgstr "Ce nom court contient des caractères non autorisés, il ne doit être composé que de lettres, de chiffres et du tiret (-)." -#: IDF/Form/Admin/ProjectCreate.php:156 +#: IDF/Form/Admin/ProjectCreate.php:166 msgid "The shortname cannot start with the dash (-) character." msgstr "Le nom court ne doit pas démarrer avec un tiret (-)." -#: IDF/Form/Admin/ProjectCreate.php:159 +#: IDF/Form/Admin/ProjectCreate.php:169 msgid "The shortname cannot end with the dash (-) character." msgstr "Le nom court ne doit pas se terminer avec un tiret (-)." -#: IDF/Form/Admin/ProjectCreate.php:164 +#: IDF/Form/Admin/ProjectCreate.php:174 msgid "This shortname is already used. Please select another one." msgstr "Ce nom court est déjà utilisé, veuillez en sélectionner un autre." -#: 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 "Cliquez sur l'onglet Administration pour définir la description du projet." @@ -1461,7 +1496,11 @@ msgstr "Si vous donnez les droits de staff à un utilisateur, vous devez vraimen msgid "If the user is not getting the confirmation email or is abusing the system, you can directly enable or disable his account here." msgstr "Si l'utilisateur ne reçoit pas l'email de confirmation ou s'il abuse du système, vous pouvez directement activer ou désactiver son compte ici." -#: IDF/Form/Admin/UserUpdate.php:196 +#: IDF/Form/Admin/UserUpdate.php:183 +msgid "--- is not a valid first name." +msgstr "--- n'est pas un prénom acceptable." + +#: IDF/Form/Admin/UserUpdate.php:199 msgid "A user with this email already exists, please provide another email address." msgstr "Un utilisateur avec cet email existe déjà, merci de fournir une autre adresse email." @@ -1493,14 +1532,6 @@ msgstr "Titre" msgid "The page name must contains only letters, digits and the dash (-) character." msgstr "Le nom de la page ne doit contenir que des lettres, chiffres et le tiret (-)." -#: 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 "Description" - #: IDF/Form/WikiUpdate.php:61 #: IDF/Form/ReviewCommentFile.php:70 #: IDF/Form/WikiCreate.php:71 @@ -1640,7 +1671,7 @@ msgstr "Création de la page" #: 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 "La variable de configuration \"upload_issue_path\" n'a pas été définie." @@ -1664,7 +1695,7 @@ msgid "You need to provide a description of the issue." msgstr "Vous devez fournir une description du problème." #: IDF/Form/IssueCreate.php:203 -#: IDF/Form/ReviewCreate.php:157 +#: IDF/Form/ReviewCreate.php:159 msgid "You provided an invalid status." msgstr "Vous avez fourni un statut invalide." @@ -1697,6 +1728,13 @@ msgstr "Vous devez au moins apporter un commentaire à un fichier." msgid "New Documentation Page %s - %s (%s)" msgstr "Nouvelle page de documentation %s - %s (%s)" +#: IDF/Form/MembersConf.php:104 +#, php-format +msgid "The following login is invalid: %s." +msgid_plural "The following login are invalids: %s." +msgstr[0] "L'identifiant suivant n'est pas valide : %s." +msgstr[1] "Les identifiants suivants ne sont pas valides: %s." + #: IDF/Form/WikiConf.php:49 msgid "Predefined documentation page labels" msgstr "Étiquettes prédéfinies des pages" @@ -1721,35 +1759,35 @@ msgstr "Étiquettes prédéfinies des tickets" msgid "Each issue may have at most one label with each of these classes" msgstr "Chaque ticket ne peut avoir qu'une seule étiquette pour chacune de ces classes." -#: IDF/Form/ReviewCreate.php:90 +#: IDF/Form/ReviewCreate.php:92 msgid "Patch" msgstr "Patch" -#: 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 "Nous n'avons pas réussi à interpréter votre patch. Merci de fournir un patch valid." -#: IDF/Form/ReviewCreate.php:126 +#: IDF/Form/ReviewCreate.php:128 msgid "You provided an invalid commit." msgstr "Vous avez fourni un commit invalide." -#: IDF/Form/ReviewCreate.php:200 +#: IDF/Form/ReviewCreate.php:202 msgid "Initial patch to be reviewed." msgstr "Patch initial à discuter." -#: IDF/Form/ReviewCreate.php:220 +#: IDF/Form/ReviewCreate.php:222 #, php-format msgid "New Code Review %s - %s (%s)" msgstr "Nouvelle revue de code %s - %s (%s)" -#: 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 "Répertoire %1$s non trouvé dans le commit %2$s." -#: 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 "Arbre non valide : %s." @@ -1923,6 +1961,10 @@ msgstr "Arbre des sources" msgid "Change Log" msgstr "Liste des changements" +#: IDF/gettexttemplates/idf/source/base.html.php:6 +msgid "How To Get The Code" +msgstr "Comment obtenir le code" + #: 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 @@ -1972,6 +2014,7 @@ msgstr "Télécharger ce fichier" #: 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 @@ -2056,12 +2099,14 @@ msgstr "mise à :" #: 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 "Révision :" #: 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 "Voir révision" @@ -2186,9 +2231,9 @@ msgstr "Mise à jour" #: 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 "Annuler" @@ -2293,7 +2338,7 @@ msgstr "Supprimer ce projet" #: 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 "Poubelle" @@ -2359,6 +2404,34 @@ msgstr "Pour de gros projets, la suppression peut durer un moment, merci de votr msgid "Change Project Details" msgstr "Changer les détails du projet" +#: IDF/gettexttemplates/idf/gadmin/projects/index.html.php:3 +msgid "Space Usage Statistics" +msgstr "Statistiques de l'utilisation" + +#: IDF/gettexttemplates/idf/gadmin/projects/index.html.php:4 +msgid "Repositories:" +msgstr "Dépôts :" + +#: 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 "Pièces jointes :" + +#: 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 "Téléchargements :" + +#: IDF/gettexttemplates/idf/gadmin/projects/index.html.php:7 +msgid "Database:" +msgstr "Base de données :" + +#: IDF/gettexttemplates/idf/gadmin/projects/index.html.php:8 +msgid "Total Forge:" +msgstr "Total forge:" + #: IDF/gettexttemplates/idf/gadmin/projects/create.html.php:3 msgid "You can select the type of repository you want. In the case of subversion, you can use optionally a remote repository instead of the local one." msgstr "Vous pouvez sélectionner le type de dépôt souhaité. Dans le cas d'un dépôt Subversion, vous pouvez aussi choisir un dépôt distant." @@ -2389,7 +2462,7 @@ msgid "Welcome" msgstr "Bienvenue" #: 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 @@ -2414,6 +2487,10 @@ msgstr "Type de dépôt :" msgid "Repository access:" msgstr "Accès au dépôt :" +#: IDF/gettexttemplates/idf/admin/source.html.php:7 +msgid "Repository size:" +msgstr "Taille des dépôts :" + #: IDF/gettexttemplates/idf/admin/tabs.html.php:3 msgid "You can configure here the project tabs access rights and notification emails." msgstr "Vous pouvez configurer ici les droits d'accès aux onglets et les emails de notification." @@ -2540,7 +2617,7 @@ msgstr "Créé :" #: 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 "Mis à jour :" @@ -2695,7 +2772,7 @@ msgstr "Récupérer votre mot de passe" #: 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 "Instructions" @@ -2881,7 +2958,9 @@ msgid "" msgstr "" "Bonjour %%user%%,\n" "\n" -"Vous avez perdu votre mot de passe et voulez le retrouver.Pour définir un nouveau mot de passe pour votre compte,vous devez juste suivre le lien suivant et un formulaire vouspermettra de donner de nouveau un mot de passe :%%url%%\n" +"Vous avez perdu votre mot de passe et voulez le retrouver. Pour définir un nouveau mot de passe pour votre compte, vous devez juste suivre le lien suivant et un formulaire vous permettra de donner de nouveau un mot de passe :\n" +"\n" +"%%url%%\n" "\n" "Vous pouvez aussi aller sur cette page :\n" "\n" @@ -2891,10 +2970,10 @@ msgstr "" "\n" "%%key%%\n" "\n" -"Si vous n'êtes la personne ayant fait la demandede récupération de votre mot de passe, ignorez simplementcet email et votre mot de passe restera inchangé. \n" +"Si vous n'êtes pas la personne ayant fait la demande de récupération de votre mot de passe, ignorez simplement cet email et votre mot de passe restera inchangé. \n" "\n" "Très cordialement,\n" -"L'équipe de développement.\n" +"l'équipe de développement.\n" #: IDF/gettexttemplates/idf/login_form.html.php:3 msgid "What is your login?" @@ -3028,7 +3107,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" @@ -3058,7 +3137,7 @@ msgstr "" "message. \n" "\n" "Très cordialement,\n" -"L'équipe de développement.\n" +"l'équipe de développement.\n" #: IDF/gettexttemplates/idf/review/view.html.php:4 #, php-format @@ -3104,6 +3183,7 @@ msgstr "Le formulaire contient des erreurs. Merci de les corriger pour soumettre #: 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 "Description :" @@ -3236,11 +3316,6 @@ msgstr "Par %%who%%, %%c.creation_dtime%%" msgid "(No comments were given for this change.)" msgstr "(Aucun commentaire n'a été donné pour ce changement.)" -#: IDF/gettexttemplates/idf/issues/issue-updated-email.txt.php:17 -#: IDF/gettexttemplates/idf/issues/issue-created-email.txt.php:11 -msgid "Attachments:" -msgstr "Pièces jointes :" - #: IDF/gettexttemplates/idf/issues/issue-updated-email.txt.php:18 #: IDF/gettexttemplates/idf/issues/issue-created-email.txt.php:12 msgid "Issue:" @@ -3380,32 +3455,31 @@ msgid "Attention! This file is marked as deprecated, download i msgstr "Attention ! Ce fichier est marqué comme obsolète, téléchargez ce fichier uniquement si vous avez besoin de cette version." #: IDF/gettexttemplates/idf/downloads/view.html.php:5 +msgid "Changes" +msgstr "Changements" + +#: IDF/gettexttemplates/idf/downloads/view.html.php:6 msgid "The form contains some errors. Please correct them to update the file." msgstr "Le formulaire contient des erreurs. Merci de les corriger pour mettre en ligne le fichier." -#: IDF/gettexttemplates/idf/downloads/view.html.php:6 +#: IDF/gettexttemplates/idf/downloads/view.html.php:7 msgid "Update File" msgstr "Mettre à jour le fichier" -#: 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 "Supprimer ce fichier" -#: IDF/gettexttemplates/idf/downloads/view.html.php:11 +#: IDF/gettexttemplates/idf/downloads/view.html.php:12 msgid "Delete this file" msgstr "Supprimer ce fichier" -#: 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 "Mis en ligne :" -#: IDF/gettexttemplates/idf/downloads/view.html.php:14 -#: IDF/gettexttemplates/idf/downloads/delete.html.php:10 -msgid "Downloads:" -msgstr "Téléchargements :" - #: IDF/gettexttemplates/idf/downloads/delete.html.php:3 msgid "Attention! If you want to delete a specific version of your software, maybe, someone is depending on this specific version to run his systems. Are you sure, you will not affect anybody when removing this file?" msgstr "Attention ! Si vous voulez supprimer une version spécifique de votre logiciel, peut-être que quelqu'un dépend encore de cette version. Êtes-vous certain que supprimer ce fichier ne va pas importuner certaines personnes ?" @@ -3436,10 +3510,15 @@ msgid "" msgstr "Chaque fichier doit avoir un nom différent et ce nom ne peut pas être changé. Faites attention de bien mettre le numéro de révision dans le nom du fichier." #: IDF/gettexttemplates/idf/downloads/submit.html.php:6 +#, php-format +msgid "You can use the Markdown syntax for the changes." +msgstr "Vous pouvez utiliser la syntaxe Markdown pour la description." + +#: IDF/gettexttemplates/idf/downloads/submit.html.php:7 msgid "The form contains some errors. Please correct them to submit the file." msgstr "Le formulaire contient des erreurs. Merci de les corriger pour mettre en ligne le fichier." -#: IDF/gettexttemplates/idf/downloads/submit.html.php:7 +#: IDF/gettexttemplates/idf/downloads/submit.html.php:8 msgid "Submit File" msgstr "Mettre en ligne" @@ -3471,3 +3550,8 @@ msgstr "Moi" msgid "Create this documentation page" msgstr "Créer cette page de documentation" +#: IDF/gettexttemplates/idf/downloads/submit.html.php:6 +#, php-format +msgid "You can use the Markdown syntax for the description." +msgstr "Vous pouvez utiliser la syntaxe Markdown pour la description." + diff --git a/src/IDF/locale/idf.pot b/src/IDF/locale/idf.pot index aa67f20..39f6e7e 100644 --- a/src/IDF/locale/idf.pot +++ b/src/IDF/locale/idf.pot @@ -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 \n" "Language-Team: LANGUAGE \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 %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 "" "Issue %3$d, %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 issue %d" +msgid "Comment on issue %d, 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 issue %d" +msgid "Creation of issue %d, 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 "Download %2$d, %3$s" msgstr "" -#: IDF/Upload.php:185 +#: IDF/Upload.php:192 #, php-format -msgid "Addition of download %d" +msgid "Addition of download %d, 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 page %s" +msgid "Creation of page %s, by %s" msgstr "" #: IDF/WikiPage.php:218 @@ -383,7 +389,7 @@ msgstr "" #: IDF/WikiRevision.php:189 #, php-format -msgid "Change of %s" +msgid "Change of %s, 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 issue %d" +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 "Issue %d 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 "Issue %d 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 %d: %s" msgstr "" -#: IDF/Views/Issue.php:298 -#, php-format -msgid "Issue %d 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 "Issue %d 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 "" "Attention! 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 Markdown syntax 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 Markdown syntax for the description." +msgstr "" diff --git a/src/IDF/relations.php b/src/IDF/relations.php index 3f3034f..b0d7da2 100644 --- a/src/IDF/relations.php +++ b/src/IDF/relations.php @@ -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', diff --git a/src/IDF/templates/idf/admin/source.html b/src/IDF/templates/idf/admin/source.html index 27612ab..5fba9ef 100644 --- a/src/IDF/templates/idf/admin/source.html +++ b/src/IDF/templates/idf/admin/source.html @@ -20,7 +20,12 @@ {trans 'Repository access:'} {$repository_access} -{if $remote_svn} +{if $repository_size != -1} + +{trans 'Repository size:'} +{$repository_size|size} + +{/if}{if $remote_svn} {$form.f.svn_username.labelTag}: {if $form.f.svn_username.errors}{$form.f.svn_username.fieldErrors}{/if} diff --git a/src/IDF/templates/idf/admin/tabs.html b/src/IDF/templates/idf/admin/tabs.html index fa86dea..7a3b25a 100644 --- a/src/IDF/templates/idf/admin/tabs.html +++ b/src/IDF/templates/idf/admin/tabs.html @@ -53,6 +53,15 @@ +  + +{blocktrans} +Only project members and admins have write access to the source.
+If you restrict the access to the source, anonymous access is
+not provided and the users must authenticate themselves with their
+password or SSH key.{/blocktrans} + + {$form.f.review_access_rights.labelTag}: {if $form.f.review_access_rights.errors}{$form.f.review_access_rights.fieldErrors}{/if} {$form.f.review_access_rights|unsafe} diff --git a/src/IDF/templates/idf/base-full.html b/src/IDF/templates/idf/base-full.html index 0c09933..317e487 100644 --- a/src/IDF/templates/idf/base-full.html +++ b/src/IDF/templates/idf/base-full.html @@ -73,15 +73,18 @@ {include 'idf/js-hotkeys.html'} {block javascript}{/block} {if $project} -{/if} diff --git a/src/IDF/templates/idf/base.html b/src/IDF/templates/idf/base.html index 39c3dc9..557d579 100644 --- a/src/IDF/templates/idf/base.html +++ b/src/IDF/templates/idf/base.html @@ -75,15 +75,18 @@ {include 'idf/js-hotkeys.html'} {block javascript}{/block} {if $project} -{/if} - + \ No newline at end of file diff --git a/src/IDF/templates/idf/downloads/download-created-email.txt b/src/IDF/templates/idf/downloads/download-created-email.txt index 0022a39..6d7dfb0 100644 --- a/src/IDF/templates/idf/downloads/download-created-email.txt +++ b/src/IDF/templates/idf/downloads/download-created-email.txt @@ -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} diff --git a/src/IDF/templates/idf/downloads/submit.html b/src/IDF/templates/idf/downloads/submit.html index 3773331..ed7b44b 100644 --- a/src/IDF/templates/idf/downloads/submit.html +++ b/src/IDF/templates/idf/downloads/submit.html @@ -19,6 +19,12 @@ +{$form.f.changelog.labelTag}: +{if $form.f.changelog.errors}{$form.f.changelog.fieldErrors}{/if} +{$form.f.changelog|unsafe} + + + {$form.f.file.labelTag}: {if $form.f.file.errors}{$form.f.file.fieldErrors}{/if} {$form.f.file|unsafe} @@ -50,6 +56,8 @@

{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}

+{assign $url = 'http://daringfireball.net/projects/markdown/syntax'} +

{blocktrans}You can use the Markdown syntax for the description.{/blocktrans}

{/block} {block javascript} diff --git a/src/IDF/templates/idf/downloads/view.html b/src/IDF/templates/idf/downloads/view.html index d509e63..f098eb9 100644 --- a/src/IDF/templates/idf/downloads/view.html +++ b/src/IDF/templates/idf/downloads/view.html @@ -6,7 +6,11 @@ {if $deprecated}

{blocktrans}Attention! This file is marked as deprecated, download it only if you are sure you need this specific version.{/blocktrans}

{/if} {$file} - {$file.filesize|size} +{if $file.changelog} +

{trans 'Changes'}

+{markdown $file.changelog, $request} +{/if} {if $form} {if $form.errors} @@ -19,7 +23,7 @@ {/if}
- +
+ + + +{aurl 'url', 'IDF_Views_Download::delete', array($project.shortname, $file.id)} -
{$form.f.summary.labelTag}: {if $form.f.summary.errors}{$form.f.summary.fieldErrors}{/if} @@ -27,6 +31,12 @@
{$form.f.changelog.labelTag}:{if $form.f.changelog.errors}{$form.f.changelog.fieldErrors}{/if} +{$form.f.changelog|unsafe} +
{$form.f.label1.labelTag}: {if $form.f.label1.errors}{$form.f.label1.fieldErrors}{/if}{$form.f.label1|unsafe} @@ -39,7 +49,8 @@
  | {trans 'Cancel'} {trans 'Trash'} {trans 'Delete this file'} +{* float left is a fix for Firefox < 3.5 *} + | {trans 'Cancel'} {trans 'Trash'} {trans 'Delete this file'}
diff --git a/src/IDF/templates/idf/gadmin/projects/index.html b/src/IDF/templates/idf/gadmin/projects/index.html index 011abb3..5c9179b 100644 --- a/src/IDF/templates/idf/gadmin/projects/index.html +++ b/src/IDF/templates/idf/gadmin/projects/index.html @@ -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} +
+

{trans 'Space Usage Statistics'}

+
    +
  • {trans 'Repositories:'} {$size['repositories']|size}
  • +
  • {trans 'Attachments:'} {$size['attachments']|size}
  • +
  • {trans 'Downloads:'} {$size['downloads']|size}
  • +
  • {trans 'Database:'} {$size['database']|size}
  • +
  • {trans 'Total Forge:'} {$size['total']|size}
  • +
+ +
+{/block} + + diff --git a/src/IDF/templates/idf/gadmin/projects/update.html b/src/IDF/templates/idf/gadmin/projects/update.html index bdda67c..5442e72 100644 --- a/src/IDF/templates/idf/gadmin/projects/update.html +++ b/src/IDF/templates/idf/gadmin/projects/update.html @@ -35,9 +35,10 @@   {aurl 'url', 'IDF_Views_Admin::projectDelete', array($project.id)} - -| {trans 'Cancel'} {if $isAdmin} - {trans 'Trash'} {trans 'Delete this project'}
{trans 'You will be ask to confirm.'}
{/if} +{* float left is a fix for Firefox < 3.5 *} + +| {trans 'Cancel'} {if $isAdmin} + {trans 'Trash'} {trans 'Delete this project'}
{trans 'You will be asked to confirm.'}
{/if} diff --git a/src/IDF/templates/idf/index.html b/src/IDF/templates/idf/index.html index 69747d4..5d66fd6 100644 --- a/src/IDF/templates/idf/index.html +++ b/src/IDF/templates/idf/index.html @@ -10,7 +10,7 @@

+ {trans 'Create Project'}

{/if} {else}
    {foreach $projects as $p} -
  • {$p}, {$p.shortdesc}
  • +
  • {if $p.private}{trans 'Private project'} {/if}{$p}{if $p.shortdesc}, {$p.shortdesc}{/if}
  • {/foreach}
{/if} {/block} diff --git a/src/IDF/templates/idf/issues/create.html b/src/IDF/templates/idf/issues/create.html index d2c5716..6e4e73f 100644 --- a/src/IDF/templates/idf/issues/create.html +++ b/src/IDF/templates/idf/issues/create.html @@ -11,14 +11,14 @@ {/if} {if $preview} -

{trans 'Preview'}

+

{trans 'Preview'}


{issuetext $preview, $request}

{/if} - + @@ -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("{/literal}{trans 'Attach another file'}{literal}"); $("#form-show-1").click(function(){ $("#form-attachment-2").show(); $("#form-block-1").hide(); + return false; }); $("#form-attachment-2 td").append("{/literal}{trans 'Attach another file'}{literal}"); $("#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) { diff --git a/src/IDF/templates/idf/issues/view.html b/src/IDF/templates/idf/issues/view.html index 49efb1c..0a08cf7 100644 --- a/src/IDF/templates/idf/issues/view.html +++ b/src/IDF/templates/idf/issues/view.html @@ -53,7 +53,7 @@ {/if} {if $preview} -

{trans 'Preview'}

+

{trans 'Preview'}


{issuetext $preview, $request}
@@ -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("{/literal}{trans 'Attach another file'}{literal}"); $("#form-show-1").click(function(){ $("#form-attachment-2").show(); $("#form-block-1").hide(); + return false; }); $("#form-attachment-2 td").append("{/literal}{trans 'Attach another file'}{literal}"); $("#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) { diff --git a/src/IDF/templates/idf/register/confirmation-email.txt b/src/IDF/templates/idf/register/confirmation-email.txt index eb2422a..7da9008 100644 --- a/src/IDF/templates/idf/register/confirmation-email.txt +++ b/src/IDF/templates/idf/register/confirmation-email.txt @@ -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, diff --git a/src/IDF/templates/idf/source/base.html b/src/IDF/templates/idf/source/base.html index 6aec0c6..534e605 100644 --- a/src/IDF/templates/idf/source/base.html +++ b/src/IDF/templates/idf/source/base.html @@ -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}
-{trans 'Source Tree'} | -{trans 'Change Log'} -{if $inCommit}| {trans 'Commit'}{/if} +{trans 'Source Tree'} | +{trans 'Change Log'} +{if $inCommit}| {trans 'Commit'}{/if} | +{trans 'How To Get The Code'}
{/block} {block title}{$title}{/block} diff --git a/src/IDF/templates/idf/source/changelog.html b/src/IDF/templates/idf/source/changelog.html index e249369..971d3ba 100644 --- a/src/IDF/templates/idf/source/changelog.html +++ b/src/IDF/templates/idf/source/changelog.html @@ -27,22 +27,3 @@
{$form.f.summary.labelTag}:
{/block} -{block context} -{if $scm != 'svn'} -

{trans 'Branches:'}
-{foreach $branches as $branch} -{aurl 'url', 'IDF_Views_Source::changeLog', array($project.shortname, $branch)} -{$branch}
-{/foreach} -

-{else} - -

{trans 'Revision:'} {$commit}

-

- - -

-
-{/if} -{/block} - diff --git a/src/IDF/templates/idf/source/commit.html b/src/IDF/templates/idf/source/commit.html index 56e8ac1..7905eb4 100644 --- a/src/IDF/templates/idf/source/commit.html +++ b/src/IDF/templates/idf/source/commit.html @@ -38,13 +38,21 @@ {/block} {block context} -{if $scm == 'git'} +{if $scm != 'svn'}

{trans 'Branches:'}
-{foreach $branches as $branch} -{aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $branch)} -{$branch}
+{foreach $branches as $branch => $path} +{aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)} +{$branch}
{/foreach}

+{else} +
+

{trans 'Revision:'} {$commit}

+

+ + +

+
{/if} {/block} diff --git a/src/IDF/templates/idf/source/git/changelog.html b/src/IDF/templates/idf/source/git/changelog.html new file mode 100644 index 0000000..c384025 --- /dev/null +++ b/src/IDF/templates/idf/source/git/changelog.html @@ -0,0 +1,10 @@ +{extends "idf/source/changelog.html"} +{block context} +

{trans 'Branches:'}
+{foreach $branches as $branch => $path} +{aurl 'url', 'IDF_Views_Source::changeLog', array($project.shortname, $branch)} +{if $path}{$path}{else}{$branch}{/if}
+{/foreach} +

+{/block} + diff --git a/src/IDF/templates/idf/source/git/file.html b/src/IDF/templates/idf/source/git/file.html index c6ae174..76e9b75 100644 --- a/src/IDF/templates/idf/source/git/file.html +++ b/src/IDF/templates/idf/source/git/file.html @@ -23,9 +23,9 @@ {/block} {block context}

{trans 'Branches:'}
-{foreach $branches as $branch} +{foreach $branches as $branch => $path} {aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)} -{$branch}
+{if $path}{$path}{else}{$branch}{/if}
{/foreach}

{/block} diff --git a/src/IDF/templates/idf/source/git/help.html b/src/IDF/templates/idf/source/git/help.html index eb71a70..9e72ba9 100644 --- a/src/IDF/templates/idf/source/git/help.html +++ b/src/IDF/templates/idf/source/git/help.html @@ -1,5 +1,5 @@ {extends "idf/source/base.html"} -{block docclass}yui-t2{/block} +{block docclass}yui-t2{assign $inHelp=true}{/block} {block body}

{blocktrans}The team behind {$project} is using @@ -8,7 +8,7 @@ code.{/blocktrans}

{trans 'Command-Line Access'}

-

git clone {if $project.private}{$project.getWriteRemoteAccessUrl()}{else}{$project.getRemoteAccessUrl()}{/if}

+

git clone {$project.getSourceAccessUrl($user)}

{aurl 'url', 'IDF_Views_User::myAccount'}

{blocktrans}You may need to provide your SSH key. The synchronization of your SSH key can take a couple of minutes. You can learn more about SSH key authentification.{/blocktrans}

@@ -22,7 +22,7 @@ code.{/blocktrans}

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 diff --git a/src/IDF/templates/idf/source/git/tree.html b/src/IDF/templates/idf/source/git/tree.html index 3bcbcd3..8bc8a68 100644 --- a/src/IDF/templates/idf/source/git/tree.html +++ b/src/IDF/templates/idf/source/git/tree.html @@ -47,15 +47,15 @@ {aurl 'url', 'IDF_Views_Source::download', array($project.shortname, $commit)} -

{trans 'Archive'} {trans 'Download this version'} {trans 'or'} git clone {if $project.private}{$project.getWriteRemoteAccessUrl()}{else}{$project.getRemoteAccessUrl()}{/if} {trans 'Help'}

+

{trans 'Archive'} {trans 'Download this version'} {trans 'or'} git clone {$project.getSourceAccessUrl($user)} {trans 'Help'}

{/block} {block context}

{trans 'Branches:'}
-{foreach $branches as $branch} +{foreach $branches as $branch => $path} {aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)} -{$branch}
+{if $path}{$path}{else}{$branch}{/if}
{/foreach}

{/block} diff --git a/src/IDF/templates/idf/source/mercurial/changelog.html b/src/IDF/templates/idf/source/mercurial/changelog.html new file mode 100644 index 0000000..c7893fe --- /dev/null +++ b/src/IDF/templates/idf/source/mercurial/changelog.html @@ -0,0 +1,10 @@ +{extends "idf/source/changelog.html"} +{block context} +

{trans 'Branches:'}
+{foreach $branches as $branch => $path} +{aurl 'url', 'IDF_Views_Source::changeLog', array($project.shortname, $branch)} +{$branch}
+{/foreach} +

+{/block} + diff --git a/src/IDF/templates/idf/source/mercurial/file.html b/src/IDF/templates/idf/source/mercurial/file.html index bc659e4..a0b969b 100644 --- a/src/IDF/templates/idf/source/mercurial/file.html +++ b/src/IDF/templates/idf/source/mercurial/file.html @@ -24,9 +24,9 @@ {block context}

{trans 'Branches:'}
-{foreach $branches as $branch} +{foreach $branches as $branch => $path} {aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)} -{$branch}
+{$branch}
{/foreach}

{/block} diff --git a/src/IDF/templates/idf/source/mercurial/help.html b/src/IDF/templates/idf/source/mercurial/help.html index d8108c3..2084c0c 100644 --- a/src/IDF/templates/idf/source/mercurial/help.html +++ b/src/IDF/templates/idf/source/mercurial/help.html @@ -1,5 +1,5 @@ {extends "idf/source/base.html"} -{block docclass}yui-t2{/block} +{block docclass}yui-t2{assign $inHelp=true}{/block} {block body}

{blocktrans}The team behind {$project} is using diff --git a/src/IDF/templates/idf/source/mercurial/tree.html b/src/IDF/templates/idf/source/mercurial/tree.html index 6077f33..d097bc9 100644 --- a/src/IDF/templates/idf/source/mercurial/tree.html +++ b/src/IDF/templates/idf/source/mercurial/tree.html @@ -48,9 +48,10 @@ {block context}

{trans 'Branches:'}
-{foreach $branches as $branch} +{foreach $branches as $branch => $path} {aurl 'url', 'IDF_Views_Source::treeBase', array($project.shortname, $branch)} -{$branch}
+{$branch}
{/foreach}

{/block} + diff --git a/src/IDF/templates/idf/source/svn/changelog.html b/src/IDF/templates/idf/source/svn/changelog.html new file mode 100644 index 0000000..b535e5c --- /dev/null +++ b/src/IDF/templates/idf/source/svn/changelog.html @@ -0,0 +1,11 @@ +{extends "idf/source/changelog.html"} +{block context} +
+

{trans 'Revision:'} {$commit}

+

+ + +

+
+{/block} + diff --git a/src/IDF/templates/idf/source/svn/help.html b/src/IDF/templates/idf/source/svn/help.html index bb54f68..b6f9caf 100644 --- a/src/IDF/templates/idf/source/svn/help.html +++ b/src/IDF/templates/idf/source/svn/help.html @@ -1,5 +1,5 @@ {extends "idf/source/base.html"} -{block docclass}yui-t2{/block} +{block docclass}yui-t2{assign $inHelp=true}{/block} {block body}

{blocktrans}The team behind {$project} is using diff --git a/src/IDF/templates/idf/source/svn/tree.html b/src/IDF/templates/idf/source/svn/tree.html index 3ce66c3..54c1fb1 100644 --- a/src/IDF/templates/idf/source/svn/tree.html +++ b/src/IDF/templates/idf/source/svn/tree.html @@ -68,5 +68,12 @@

+

{trans 'Branches:'}
+{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} +{$branch}
+{/foreach} +

{/block} diff --git a/src/IDF/templates/idf/user/myaccount.html b/src/IDF/templates/idf/user/myaccount.html index f871e4e..06ab12f 100644 --- a/src/IDF/templates/idf/user/myaccount.html +++ b/src/IDF/templates/idf/user/myaccount.html @@ -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} diff --git a/src/IDF/templates/idf/wiki/create.html b/src/IDF/templates/idf/wiki/create.html index 2780222..1b2975b 100644 --- a/src/IDF/templates/idf/wiki/create.html +++ b/src/IDF/templates/idf/wiki/create.html @@ -3,7 +3,7 @@ {block body} {if $preview} -

{trans 'Preview of the Page'}

+

{trans 'Preview of the Page'}

{markdown $preview, $request} {/if} diff --git a/src/IDF/templates/idf/wiki/update.html b/src/IDF/templates/idf/wiki/update.html index 3fa6af5..91d579b 100644 --- a/src/IDF/templates/idf/wiki/update.html +++ b/src/IDF/templates/idf/wiki/update.html @@ -3,7 +3,7 @@ {block body} {if $preview} -

{trans 'Preview of the Page'}

+

{trans 'Preview of the Page'}

{markdown $preview, $request} {/if} diff --git a/www/media/idf/css/style.css b/www/media/idf/css/style.css index e1cc85c..5424689 100644 --- a/www/media/idf/css/style.css +++ b/www/media/idf/css/style.css @@ -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"); diff --git a/www/media/idf/img/lock.png b/www/media/idf/img/lock.png new file mode 100644 index 0000000..0466619 Binary files /dev/null and b/www/media/idf/img/lock.png differ