Merge branch 'master' of git://projects.ceondo.com/indefero

master
Thomas Keller 2010-06-21 23:23:26 +02:00
commit 2ee665ac96
35 changed files with 5318 additions and 605 deletions

View File

@ -23,6 +23,8 @@ Much appreciated contributors:
Ludovic Bellière
Brian Armstrong
Raphaël Emourgeon
Jakub Viták
Vladimir Solomatin
And all the nice users who spent time reporting issues and promoting
the project. The project could not live without them.

View File

@ -79,3 +79,12 @@ the following configuration variables:
* **idf_plugin_syncsvn_access_public ('r')**: Anonymous access.
* **idf_plugin_syncsvn_access_private ('')**: Anonymous access in the case of a private project.
## svn: Can't open file '/root/.subversion/servers': Permission denied error
If you get the error:
svn: Can't open file '/root/.subversion/servers': Permission denied
Check the [fix available](http://projects.ceondo.com/p/indefero/issues/458/)

0
scripts/SyncMercurial.sh 100644 → 100755
View File

View File

@ -0,0 +1,69 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* This script process the queue of items.
*
* At the moment the queue is only used for the webhooks, but it would
* be good in the future to use it for indexing and email
* notifications.
*
*/
require dirname(__FILE__).'/../src/IDF/conf/path.php';
require 'Pluf.php';
Pluf::start(dirname(__FILE__).'/../src/IDF/conf/idf.php');
Pluf_Dispatcher::loadControllers(Pluf::f('idf_views'));
#;*/ ::
$lock_file = Pluf::f('idf_queuecron_lock',
Pluf::f('tmp_folder', '/tmp').'/queuecron.lock');
if (file_exists($lock_file)) {
Pluf_Log::event(array('queuecron.php', 'skip'));
return;
}
file_put_contents($lock_file, time(), LOCK_EX);
/**
* [signal]
*
* queuecron.php::run
*
* [sender]
*
* queuecron.php
*
* [description]
*
* This signal allows an application to perform a set of tasks when
* the queue cron job is run. This is done usually every 5 minutes.
*
* [parameters]
*
* array()
*
*/
$params = array();
Pluf_Signal::send('queuecron.php::run', 'queuecron.php', $params);
unlink($lock_file);

View File

@ -0,0 +1,29 @@
#!/bin/sh
#
# This hook does only one thing:
#
# 1. It calls the svnpostrevpropchange.php script with the current repository
# and revision as argument. The svnpostrevpropchange.php script will then
# trigger the 'svnpostrevpropchange.php::run' event with the repository
# path, revision, username, property name and action as arguments together
# with merged $_ENV and $_SERVER array.
#
# This hook is normally installed automatically at the creation of your
# repository if you have everything configured correctly. If you want
# to enable it later, you need to symlink it as "post-revprop-change" in your
# $REPOSITORY/hooks folder. It needs to be executable.
#
# www$ chmod +x /home/www/indefero/scripts/svn-post-revprop-change
# www$ cd /home/svn/repositories/project/hooks
# www$ ln -s /home/www/indefero/scripts/svn-post-revprop-change post-revprop-change
#
SCRIPTDIR=$(dirname $(readlink -f $0))
PHP_POST_REVPROP=$SCRIPTDIR/svnpostrevpropchange.php
echo php $PHP_POST_REVPROP "$1" "$2" "$3" "$4" "$5" | at now > /dev/null 2>&1
REPOS="$1"
REV="$2"
USER="$3"
PROPNAME="$4"
ACTION="$5"

View File

@ -0,0 +1,70 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008-2010 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* This script allows you to hook into the post-revprop-change action
* of your subversion repository. I am using it to perform near real
* time backup of the repositories on indefero.net.
*/
require dirname(__FILE__).'/../src/IDF/conf/path.php';
require 'Pluf.php';
Pluf::start(dirname(__FILE__).'/../src/IDF/conf/idf.php');
Pluf_Dispatcher::loadControllers(Pluf::f('idf_views'));
/**
* [signal]
*
* svnpostrevpropchange.php::run
*
* [sender]
*
* svnpostrevpropchange.php
*
* [description]
*
* This signal allows an application to perform a set of tasks on a
* post property revision change of a subversion repository.
*
* [parameters]
*
* array('repo_dir' => '/path/to/subversion/repository',
* 'revision' => 1234,
* 'user' => 'username',
* 'propname' => 'changed-property',
* 'action' => 'the action M, A or D',
* 'env' => array_merge($_ENV, $_SERVER));
*
*/
$params = array('repo_dir' => $argv[1],
'revision' => $argv[2],
'user' => $argv[3],
'propname' => $argv[4],
'action' => $argv[5],
'env' => array_merge($_ENV, $_SERVER));
Pluf_Signal::send('svnpostrevpropchange.php::run', 'svnpostrevpropchange.php',
$params);

View File

@ -264,9 +264,38 @@ class IDF_Commit extends Pluf_Model
*/
public function notify($conf, $create=true)
{
// Now we add to the queue, soon we will push everything in
// the queue, including email notifications and indexing.
// Even if the url is empty, we add to the queue as some
// plugins may want to do something with this information in
// an asynchronous way.
$project = $this->get_project();
$scm = $project->getConf()->getVal('scm', 'git');
$url = str_replace(array('%p', '%r'),
array($project->shortname, $this->scm_id),
$conf->getVal('webhook_url', ''));
$payload = array('to_send' => array(
'project' => $project->shortname,
'rev' => $this->scm_id,
'scm' => $scm,
'summary' => $this->summary,
'fullmessage' => $this->fullmessage,
'author' => $this->origauthor,
'creation_date' => $this->creation_dtime,
),
'project_id' => $project->id,
'authkey' => $project->getPostCommitHookKey(),
'url' => $url,
);
$item = new IDF_Queue();
$item->type = 'new_commit';
$item->payload = $payload;
$item->create();
if ('' == $conf->getVal('source_notification_email', '')) {
return;
}
$current_locale = Pluf_Translation::getLocale();
$langs = Pluf::f('languages', array('en'));
Pluf_Translation::loadSetLocale($langs[0]);

View File

@ -276,6 +276,7 @@ class IDF_Form_IssueCreate extends Pluf_Form
$comment->create();
// If we have a file, create the IDF_IssueFile and attach
// it to the comment.
$created_files = array();
for ($i=1;$i<4;$i++) {
if ($this->cleaned_data['attachment'.$i]) {
$file = new IDF_IssueFile();
@ -283,8 +284,36 @@ class IDF_Form_IssueCreate extends Pluf_Form
$file->submitter = $this->user;
$file->comment = $comment;
$file->create();
$created_files[] = $file;
}
}
/**
* [signal]
*
* IDF_Issue::create
*
* [sender]
*
* IDF_Form_IssueCreate
*
* [description]
*
* This signal allows an application to perform a set of tasks
* just after the creation of an issue. The comment contains
* the description of the issue.
*
* [parameters]
*
* array('issue' => $issue,
* 'comment' => $comment,
* 'files' => $attached_files);
*
*/
$params = array('issue' => $issue,
'comment' => $comment,
'files' => $created_files);
Pluf_Signal::send('IDF_Issue::create', 'IDF_Form_IssueCreate',
$params);
return $issue;
}

View File

@ -305,6 +305,7 @@ class IDF_Form_IssueUpdate extends IDF_Form_IssueCreate
$this->issue->submitter != $this->user->id) {
$this->issue->setAssoc($this->user); // interested user.
}
$attached_files = array();
for ($i=1;$i<4;$i++) {
if ($this->cleaned_data['attachment'.$i]) {
$file = new IDF_IssueFile();
@ -312,8 +313,36 @@ class IDF_Form_IssueUpdate extends IDF_Form_IssueCreate
$file->submitter = $this->user;
$file->comment = $comment;
$file->create();
$attached_files[] = $file;
}
}
/**
* [signal]
*
* IDF_Issue::update
*
* [sender]
*
* IDF_Form_IssueUpdate
*
* [description]
*
* This signal allows an application to perform a set of tasks
* just after the update of an issue.
*
* [parameters]
*
* array('issue' => $issue,
* 'comment' => $comment,
* 'files' => $attached_files);
*
*/
$params = array('issue' => $this->issue,
'comment' => $comment,
'files' => $attached_files);
Pluf_Signal::send('IDF_Issue::update', 'IDF_Form_IssueUpdate',
$params);
return $this->issue;
}
}

View File

@ -205,6 +205,30 @@ class IDF_Form_ReviewCreate extends Pluf_Form
$patch->patch = $this->cleaned_data['patch'];
$patch->create();
$patch->notify($this->project->getConf());
/**
* [signal]
*
* IDF_Review::create
*
* [sender]
*
* IDF_Form_ReviewCreate
*
* [description]
*
* This signal allows an application to perform a set of tasks
* just after the creation of a review and the notification.
*
* [parameters]
*
* array('review' => $review,
* 'patch' => $patch);
*
*/
$params = array('review' => $review,
'patch' => $patch);
Pluf_Signal::send('IDF_Review::create', 'IDF_Form_ReviewCreate',
$params);
return $review;
}

View File

@ -34,7 +34,7 @@ class IDF_Form_SourceConf extends Pluf_Form
public function initFields($extra=array())
{
$this->conf = $extra['conf'];
if ($this->conf->getVal('scm', 'git') == 'svn') {
if ($extra['remote_svn']) {
$this->fields['svn_username'] = new Pluf_Form_Field_Varchar(
array('required' => false,
'label' => __('Repository username'),
@ -49,6 +49,16 @@ class IDF_Form_SourceConf extends Pluf_Form
'widget' => 'Pluf_Form_Widget_PasswordInput',
));
}
Pluf::loadFunction('Pluf_HTTP_URL_urlForView');
$url = Pluf_HTTP_URL_urlForView('idf_faq').'#webhooks';
$this->fields['webhook_url'] = new Pluf_Form_Field_Url(
array('required' => false,
'label' => __('Webhook URL'),
'initial' => $this->conf->getVal('webhook_url', ''),
'help_text' => sprintf(__('Learn more about the <a href="%s">post-commit web hooks</a>.'), $url),
'widget_attrs' => array('size' => 35),
));
}
}

View File

@ -146,6 +146,28 @@ class IDF_Form_UpdateUpload extends Pluf_Form
$this->upload->modif_dtime = gmdate('Y-m-d H:i:s');
$this->upload->update();
$this->upload->batchAssoc('IDF_Tag', $tags);
/**
* [signal]
*
* IDF_Upload::update
*
* [sender]
*
* IDF_Form_UpdateUpload
*
* [description]
*
* This signal allows an application to perform a set of tasks
* just after the update of an uploaded file.
*
* [parameters]
*
* array('upload' => $upload);
*
*/
$params = array('upload' => $this->upload);
Pluf_Signal::send('IDF_Upload::update',
'IDF_Form_UpdateUpload', $params);
return $this->upload;
}
}

View File

@ -176,6 +176,28 @@ class IDF_Form_Upload extends Pluf_Form
}
// Send the notification
$upload->notify($this->project->getConf());
/**
* [signal]
*
* IDF_Upload::create
*
* [sender]
*
* IDF_Form_Upload
*
* [description]
*
* This signal allows an application to perform a set of tasks
* just after the upload of a file and after the notification run.
*
* [parameters]
*
* array('upload' => $upload);
*
*/
$params = array('upload' => $upload);
Pluf_Signal::send('IDF_Upload::create', 'IDF_Form_Upload',
$params);
return $upload;
}
}

179
src/IDF/Gconf.php 100644
View File

@ -0,0 +1,179 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* Configuration of the objects.
*
* It is just storing a list of key/value pairs associated to
* different objects. If you use this table for your model, do not
* forget to drop the corresponding keys in your preDelete call.
*/
class IDF_Gconf extends Pluf_Model
{
public $_model = __CLASS__;
public $datacache = null;
public $dirty = array();
public $f = null;
protected $_mod = null;
function init()
{
$this->_a['table'] = 'idf_gconf';
$this->_a['model'] = __CLASS__;
$this->_a['cols'] = array(
// It is mandatory to have an "id" column.
'id' =>
array(
'type' => 'Pluf_DB_Field_Sequence',
//It is automatically added.
'blank' => true,
),
'model_class' =>
array(
'type' => 'Pluf_DB_Field_Varchar',
'blank' => false,
'size' => 150,
'verbose' => __('model class'),
),
'model_id' =>
array(
'type' => 'Pluf_DB_Field_Integer',
'blank' => false,
'verbose' => __('model id'),
),
'vkey' =>
array(
'type' => 'Pluf_DB_Field_Varchar',
'blank' => false,
'size' => 50,
'verbose' => __('key'),
),
'vdesc' =>
array(
'type' => 'Pluf_DB_Field_Text',
'blank' => false,
'verbose' => __('value'),
),
);
$this->_a['idx'] = array('model_vkey_idx' =>
array(
'col' => 'model_class, model_id, vkey',
'type' => 'unique',
),
);
$this->f = new IDF_Config_DataProxy($this);
}
function setModel($model)
{
$this->datacache = null;
$this->_mod = $model;
}
function initCache()
{
$this->datacache = array();
$this->dirty = array();
$sql = new Pluf_SQL('model_class=%s AND model_id=%s',
array($this->_mod->_model, $this->_mod->id));
foreach ($this->getList(array('filter' => $sql->gen())) as $val) {
$this->datacache[$val->vkey] = $val->vdesc;
$this->dirty[$val->vkey] = $val->id;
}
}
/**
* FIXME: This is not efficient when setting a large number of
* values in a loop.
*/
function setVal($key, $value)
{
if (!is_null($this->getVal($key, null))
and $value == $this->getVal($key)) {
return;
}
if (isset($this->dirty[$key])) {
// we get to check if deleted by other process + update
$conf = new IDF_Gconf($this->dirty[$key]);
if ($conf->id == $this->dirty[$key]) {
$conf->vdesc = $value;
$conf->update();
$this->datacache[$key] = $value;
return;
}
}
// we insert
$conf = new IDF_Gconf();
$conf->model_class = $this->_mod->_model;
$conf->model_id = $this->_mod->id;
$conf->vkey = $key;
$conf->vdesc = $value;
$conf->create();
$this->datacache[$key] = $value;
$this->dirty[$key] = $conf->id;
}
function getVal($key, $default='')
{
if ($this->datacache === null) {
$this->initCache();
}
return (isset($this->datacache[$key])) ? $this->datacache[$key] : $default;
}
function delVal($key, $initcache=true)
{
$gconf = new IDF_Gconf();
$sql = new Pluf_SQL('vkey=%s AND model_class=%s AND model_id=%s', array($key, $this->_mod->_model, $this->_mod->id));
foreach ($gconf->getList(array('filter' => $sql->gen())) as $c) {
$c->delete();
}
if ($initcache) {
$this->initCache();
}
}
/**
* Drop the conf of a model.
*
* If your model is using this table, just add the following line
* in your preDelete() method:
*
* IDF_Gconf::dropForModel($this)
*
* It will take care of the cleaning.
*/
static public function dropForModel($model)
{
$table = Pluf::factory(__CLASS__)->getSqlTable();
$sql = new Pluf_SQL('model_class=%s AND model_id=%s',
array($model->_model, $model->id));
$db = &Pluf::db();
$db->execute('DELETE FROM '.$table.' WHERE '.$sql->gen());
}
static public function dropUser($signal, &$params)
{
self::dropForModel($params['user']);
}
}

View File

@ -0,0 +1,53 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* Add the new IDF_Queue model.
*
*/
function IDF_Migrations_14Queue_up($params=null)
{
$models = array(
'IDF_Queue',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->createTables();
}
}
function IDF_Migrations_14Queue_down($params=null)
{
$models = array(
'IDF_Queue',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->dropTables();
}
}

View File

@ -0,0 +1,53 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* Add the new IDF_Gconf model.
*
*/
function IDF_Migrations_15AddGconf_up($params=null)
{
$models = array(
'IDF_Gconf',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->createTables();
}
}
function IDF_Migrations_15AddGconf_down($params=null)
{
$models = array(
'IDF_Gconf',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
foreach ($models as $model) {
$schema->model = new $model();
$schema->dropTables();
}
}

View File

@ -51,6 +51,8 @@ function IDF_Migrations_Backup_run($folder, $name=null)
'IDF_Review_FileComment',
'IDF_Key',
'IDF_Scm_Cache_Git',
'IDF_Queue',
'IDF_Gconf',
);
$db = Pluf::db();
// Now, for each table, we dump the content in json, this is a
@ -94,6 +96,8 @@ function IDF_Migrations_Backup_restore($folder, $name)
'IDF_Review_FileComment',
'IDF_Key',
'IDF_Scm_Cache_Git',
'IDF_Queue',
'IDF_Gconf',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);

View File

@ -48,6 +48,8 @@ function IDF_Migrations_Install_setup($params=null)
'IDF_Review_FileComment',
'IDF_Key',
'IDF_Scm_Cache_Git',
'IDF_Queue',
'IDF_Gconf',
);
$db = Pluf::db();
$schema = new Pluf_DB_Schema($db);
@ -85,6 +87,8 @@ function IDF_Migrations_Install_teardown($params=null)
$perm = Pluf_Permission::getFromString('IDF.project-authorized-user');
if ($perm) $perm->delete();
$models = array(
'IDF_Gconf',
'IDF_Queue',
'IDF_Scm_Cache_Git',
'IDF_Key',
'IDF_Review_FileComment',

View File

@ -99,6 +99,7 @@ class IDF_Plugin_SyncGit_Cron
if (count($orphans)) {
$cmd = Pluf::f('idf_exec_cmd_prefix', '').'rm -rf '.implode(' ', $orphans);
exec($cmd);
clearstatcache();
while (list(, $project) = each($orphans)) {
if (is_dir($project)) {
throw new Exception(sprintf('Cannot remove %s directory.', $project));

View File

@ -196,6 +196,8 @@ class IDF_Plugin_SyncGit_Serve
if (!file_exists($fullpath)) {
mkdir($fullpath, 0750, true);
}
$out = array();
$res = 0;
exec(sprintf(Pluf::f('idf_exec_cmd_prefix', '').
Pluf::f('git_path', 'git').' --git-dir=%s init', escapeshellarg($fullpath)),
$out, $res);
@ -214,6 +216,8 @@ class IDF_Plugin_SyncGit_Serve
$fullpath.'/hooks/post-update'));
return;
}
$out = array();
$res = 0;
exec(sprintf(Pluf::f('idf_exec_cmd_prefix', '').'ln -s %s %s',
escapeshellarg($p),
escapeshellarg($fullpath.'/hooks/post-update')),

View File

@ -87,6 +87,36 @@ class IDF_Plugin_SyncSvn
escapeshellarg($svn_path.'/'.$shortname));
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$ll = exec($cmd, $output, $return);
if ($return != 0) {
Pluf_Log::error(array('IDF_Plugin_SyncSvn::processSvnCreate',
'Error',
array('path' => $svn_path.'/'.$shortname,
'output' => $output)));
return;
}
$p = realpath(dirname(__FILE__).'/../../../scripts/svn-post-commit');
exec(sprintf(Pluf::f('idf_exec_cmd_prefix', '').'ln -s %s %s',
escapeshellarg($p),
escapeshellarg($svn_path.'/'.$shortname.'/hooks/post-commit')),
$out, $res);
if ($res != 0) {
Pluf_Log::warn(array('IDF_Plugin_SyncSvn::processSvnCreate',
'post-commit hook creation error.',
$svn_path.'/'.$shortname.'/hooks/post-commit'));
return;
}
$p = realpath(dirname(__FILE__).'/../../../scripts/svn-post-revprop-change');
exec(sprintf(Pluf::f('idf_exec_cmd_prefix', '').'ln -s %s %s',
escapeshellarg($p),
escapeshellarg($svn_path.'/'.$shortname.'/hooks/post-revprop-change')),
$out, $res);
if ($res != 0) {
Pluf_Log::warn(array('IDF_Plugin_SyncSvn::processSvnCreate',
'post-revprop-change hook creation error.',
$svn_path.'/'.$shortname.'/hooks/post-revprop-change'));
return;
}
return ($return == 0);
}

View File

@ -366,12 +366,13 @@ class IDF_Project extends Pluf_Model
public function getRepositorySize($force=false)
{
$last_eval = $this->getConf()->getVal('repository_size_check_date', 0);
if (!$force and $last_eval > time()-86400) {
if (Pluf::f('idf_no_size_check', false) or
(!$force and $last_eval > time()-172800)) {
return $this->getConf()->getVal('repository_size', -1);
}
$this->getConf()->setVal('repository_size_check_date', time());
$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);
}
@ -429,6 +430,17 @@ class IDF_Project extends Pluf_Model
$this, $user, $commit);
}
/**
* Get the post commit hook key.
*
* The goal is to get something predictable but from which one
* cannot reverse find the secret key.
*/
public function getPostCommitHookKey()
{
return md5($this->id.sha1(Pluf::f('secret_key')).$this->shortname);
}
/**
* Get the root name of the project scm
*
@ -623,7 +635,7 @@ class IDF_Project extends Pluf_Model
Pluf_Signal::send('IDF_Project::preDelete',
'IDF_Project', $params);
$what = array('IDF_Upload', 'IDF_Review', 'IDF_Issue',
'IDF_WikiPage', 'IDF_Commit',
'IDF_WikiPage', 'IDF_Commit', 'IDF_Tag',
);
foreach ($what as $m) {
foreach (Pluf::factory($m)->getList(array('filter' => 'project='.(int)$this->id)) as $item) {

222
src/IDF/Queue.php 100644
View File

@ -0,0 +1,222 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
n# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* Queue system for the management of asynchronous operations.
*
* Anybody can add an item to the queue and any application can
* register itself to process an item from the queue.
*
* An item in the queue is considered as fully processed when all the
* handlers have processed it successfully.
*
* To push a new item in the queue:
*
* <code>
* $item = new IDF_Queue();
* $item->type = 'new_commit';
* $item->payload = array('what', 'ever', array('data'));
* $item->create();
* </code>
*
* To process one item from the queue, you first need to register an
* handler, by adding the following in your relations.php file before
* the return statement or in your config file.
*
* <code>
* Pluf_Signal::connect('IDF_Queue::processItem',
* array('YourApp_Class', 'processItem'));
* </code>
*
* The processItem method will be called with two arguments, the first
* is the name of the signal ('IDF_Queue::processItem') and the second
* is an array with:
*
* <code>
* array('item' => $item,
* 'res' => array('OtherApp_Class::handler' => false,
* 'FooApp_Class::processItem' => true));
* </code>
*
* When you process an item, you need first to check if the type is
* corresponding to what you want to work with, then you need to check
* in 'res' if you have not already processed successfully the item,
* that is the key 'YourApp_Class::processItem' must be set to true,
* and then you can process the item. At the end of your processing,
* you need to modify by reference the 'res' key to add your status.
*
* All the data except for the type is in the payload, this makes the
* queue flexible to manage many different kind of tasks.
*
*/
class IDF_Queue extends Pluf_Model
{
public $_model = __CLASS__;
function init()
{
$this->_a['table'] = 'idf_queue';
$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,
),
'status' =>
array(
'type' => 'Pluf_DB_Field_Integer',
'blank' => false,
'choices' => array(
'pending' => 0,
'in_progress' => 1,
'need_retry' => 2,
'done' => 3,
'error' => 4,
),
'default' => 0,
),
'trials' =>
array(
'type' => 'Pluf_DB_Field_Integer',
'default' => 0,
),
'type' =>
array(
'type' => 'Pluf_DB_Field_Varchar',
'blank' => false,
'size' => 50,
),
'payload' =>
array(
'type' => 'Pluf_DB_Field_Serialized',
'blank' => false,
),
'results' =>
array(
'type' => 'Pluf_DB_Field_Serialized',
'blank' => false,
),
'lasttry_dtime' =>
array(
'type' => 'Pluf_DB_Field_Datetime',
'blank' => true,
),
'creation_dtime' =>
array(
'type' => 'Pluf_DB_Field_Datetime',
'blank' => true,
),
);
}
function preSave($create=false)
{
if ($create) {
$this->creation_dtime = gmdate('Y-m-d H:i:s');
$this->lasttry_dtime = gmdate('Y-m-d H:i:s');
$this->results = array();
$this->trials = 0;
$this->status = 0;
}
}
/**
* The current item is going to be processed.
*/
function processItem()
{
/**
* [signal]
*
* IDF_Queue::processItem
*
* [sender]
*
* IDF_Queue
*
* [description]
*
* This signal allows an application to run an asynchronous
* job. The handler gets the queue item and the results from
* the previous run. If the handler key is not set, then the
* job was not run. If set it can be either true (already done)
* or false (error at last run).
*
* [parameters]
*
* array('item' => $item, 'res' => $res)
*
*/
$params = array('item' => $this, 'res' => $this->results);
Pluf_Signal::send('IDF_Queue::processItem',
'IDF_Queue', $params);
$this->status = 3; // Success
foreach ($params['res'] as $handler=>$ok) {
if (!$ok) {
$this->status = 2; // Set to need retry
$this->trials += 1;
break;
}
}
$this->results = $params['res'];
$this->lasttry_dtime = gmdate('Y-m-d H:i:s');
$this->update();
}
/**
* Parse the queue.
*
* It is a signal handler to just hook itself at the right time in
* the cron job performing the maintainance work.
*
* The processing relies on the fact that no other processing jobs
* must run at the same time. That is, your cron job must use a
* lock file or something like to not run in parallel.
*
* The processing is simple, first get 500 queue items, mark them
* as being processed and for each of them call the processItem()
* method which will trigger another event for processing.
*
* If you are processing more than 500 items per batch, you need
* to switch to a different solution.
*
*/
public static function process($sender, &$params)
{
$where = 'status=0 OR status=2';
$items = Pluf::factory('IDF_Queue')->getList(array('filter'=>$where,
'nb'=> 500));
Pluf_Log::event(array('IDF_Queue::process', $items->count()));
foreach ($items as $item) {
$item->status = 1;
$item->update();
}
foreach ($items as $item) {
$item->status = 1;
$item->processItem();
}
}
}

View File

@ -125,7 +125,7 @@ class IDF_Scm_Git extends IDF_Scm
return $this->cache['tags'];
}
$cmd = Pluf::f('idf_exec_cmd_prefix', '')
.sprintf('GIT_DIR=%s %s tag',
.sprintf('GIT_DIR=%s %s for-each-ref --format="%%(taggerdate:iso)%%(committerdate:iso) %%(objectname) %%(refname)" refs/tags',
escapeshellarg($this->repo),
Pluf::f('git_path', 'git'));
self::exec('IDF_Scm_Git::getTags', $cmd, $out, $return);
@ -134,12 +134,15 @@ class IDF_Scm_Git extends IDF_Scm
$cmd, $return,
implode("\n", $out)));
}
rsort($out);
$res = array();
foreach ($out as $b) {
if (false !== strpos($b, '/')) {
$res[$this->getCommit($b)->commit] = $b;
$elts = explode(' ', $b, 5);
$tag = substr(trim($elts[4]), 10);
if (false !== strpos($tag, '/')) {
$res[$elts[3]] = $b;
} else {
$res[$b] = '';
$res[$tag] = '';
}
}
$this->cache['tags'] = $res;

View File

@ -344,25 +344,51 @@ function IDF_Views_Admin_projectSize($field, $project)
*
* @return array Associative array with the size of each element
*/
function IDF_Views_Admin_getForgeSize()
function IDF_Views_Admin_getForgeSize($force=false)
{
$conf = new IDF_Gconf();
$conf->setModel((object) array('_model'=>'IDF_Forge', 'id'=> 1));
$res = array();
$res['repositories'] = 0;
foreach (Pluf::factory('IDF_Project')->getList() as $prj) {
$size = $prj->getRepositorySize();
$size = $prj->getRepositorySize($force);
if ($size != -1) {
$res['repositories'] += $size;
}
}
$cmd = Pluf::f('idf_exec_cmd_prefix', '').'du -sk '
.escapeshellarg(Pluf::f('upload_path'));
$out = explode(' ', 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 = explode(' ', shell_exec($cmd), 2);
$res['attachments'] = $out[0]*1024;
$res['database'] = IDF_Views_Admin_getForgeDbSize();
$last_eval = $conf->getVal('downloads_size_check_date', 0);
if (Pluf::f('idf_no_size_check', false) or
(!$force and $last_eval > time()-172800)) {
$res['downloads'] = $conf->getVal('downloads_size', 0);
} else {
$conf->setVal('downloads_size_check_date', time());
$cmd = Pluf::f('idf_exec_cmd_prefix', '').'du -sk '
.escapeshellarg(Pluf::f('upload_path'));
$out = explode(' ', shell_exec($cmd), 2);
$res['downloads'] = $out[0]*1024;
$conf->setVal('downloads_size', $res['downloads']);
}
$last_eval = $conf->getVal('attachments_size_check_date', 0);
if (Pluf::f('idf_no_size_check', false) or
(!$force and $last_eval > time()-172800)) {
$res['attachments'] = $conf->getVal('attachments_size', 0);
} else {
$conf->setVal('attachments_size_check_date', time());
$cmd = Pluf::f('idf_exec_cmd_prefix', '').'du -sk '
.escapeshellarg(Pluf::f('upload_path'));
$out = explode(' ', shell_exec($cmd), 2);
$res['attachments'] = $out[0]*1024;
$conf->setVal('attachments_size', $res['attachments']);
}
$last_eval = $conf->getVal('database_size_check_date', 0);
if (Pluf::f('idf_no_size_check', false) or
(!$force and $last_eval > time()-172800)) {
$res['database'] = $conf->getVal('database_size', 0);
} else {
$conf->setVal('database_size_check_date', time());
$res['database'] = IDF_Views_Admin_getForgeDbSize();
$conf->setVal('database_size', $res['database']);
}
$res['total'] = $res['repositories'] + $res['downloads'] + $res['attachments'] + $res['database'];
return $res;
}

View File

@ -152,6 +152,29 @@ class IDF_Views_Download
if ($request->method == 'POST') {
$fname = $upload->file;
@unlink(Pluf::f('upload_path').'/'.$prj->shortname.'/files/'.$fname);
/**
* [signal]
*
* IDF_Upload::delete
*
* [sender]
*
* IDF_Form_UpdateUpload
*
* [description]
*
* This signal allows an application to perform a set of tasks
* just before the deletion of the corresponding object in the
* database but just after the deletion from the storage.
*
* [parameters]
*
* array('upload' => $upload);
*
*/
$params = array('upload' => $upload);
Pluf_Signal::send('IDF_Upload::delete',
'IDF_Views_Download', $params);
$upload->delete();
$request->user->setMessage(__('The file has been deleted.'));
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Download::index',

View File

@ -475,44 +475,45 @@ class IDF_Views_Project
/**
* Administrate the source control.
*
* There, the login/password of the subversion remote repo can be
* change together with the webhook url.
*/
public $adminSource_precond = array('IDF_Precondition::projectOwner');
public function adminSource($request, $match)
{
$prj = $request->project;
$title = sprintf(__('%s Source'), (string) $prj);
$form = null;
$remote_svn = false;
if ($request->conf->getVal('scm') == 'svn' and
strlen($request->conf->getVal('svn_remote_url')) > 0) {
$remote_svn = true;
$extra = array(
'conf' => $request->conf,
);
if ($request->method == 'POST') {
$form = new IDF_Form_SourceConf($request->POST, $extra);
if ($form->isValid()) {
foreach ($form->cleaned_data as $key=>$val) {
$request->conf->setVal($key, $val);
}
$request->user->setMessage(__('The project source configuration has been saved.'));
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Project::adminSource',
array($prj->shortname));
return new Pluf_HTTP_Response_Redirect($url);
$remote_svn = ($request->conf->getVal('scm') == 'svn' and
strlen($request->conf->getVal('svn_remote_url')) > 0);
$extra = array(
'conf' => $request->conf,
'remote_svn' => $remote_svn,
);
if ($request->method == 'POST') {
$form = new IDF_Form_SourceConf($request->POST, $extra);
if ($form->isValid()) {
foreach ($form->cleaned_data as $key=>$val) {
$request->conf->setVal($key, $val);
}
} else {
$params = array();
foreach (array('svn_username', 'svn_password') as $key) {
$_val = $request->conf->getVal($key, false);
if ($_val !== false) {
$params[$key] = $_val;
}
}
if (count($params) == 0) {
$params = null; //Nothing in the db, so new form.
}
$form = new IDF_Form_SourceConf($params, $extra);
$request->user->setMessage(__('The project source configuration has been saved.'));
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Project::adminSource',
array($prj->shortname));
return new Pluf_HTTP_Response_Redirect($url);
}
} else {
$params = array();
foreach (array('svn_username', 'svn_password', 'webhook_url') as $key) {
$_val = $request->conf->getVal($key, false);
if ($_val !== false) {
$params[$key] = $_val;
}
}
if (count($params) == 0) {
$params = null; //Nothing in the db, so new form.
}
$form = new IDF_Form_SourceConf($params, $extra);
}
$scm = $request->conf->getVal('scm', 'git');
$options = array(
@ -530,6 +531,7 @@ class IDF_Views_Project
'repository_size' => $prj->getRepositorySize(),
'page_title' => $title,
'form' => $form,
'hookkey' => $prj->getPostCommitHookKey(),
),
$request);
}

104
src/IDF/Webhook.php 100644
View File

@ -0,0 +1,104 @@
<?php
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
# ***** BEGIN LICENSE BLOCK *****
# This file is part of InDefero, an open source project management application.
# Copyright (C) 2008, 2009, 2010 Céondo Ltd and contributors.
#
# InDefero is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# InDefero is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# ***** END LICENSE BLOCK ***** */
/**
* Management of the webhooks.
*
* The class provides the tools to perform the POST request with
* authentication for the webhooks.
*
*/
class IDF_Webhook
{
/**
* Perform the POST request given the webhook payload.
*
* @param array Payload
* @return bool Success or error
*/
public static function postNotification($payload)
{
$data = json_encode($payload['to_send']);
$sign = hash_hmac('md5', $data, $payload['authkey']);
$params = array('http' => array(
'method' => 'POST',
'content' => $data,
'user_agent' => 'Indefero Hook Sender (http://www.indefero.net)',
'max_redirects' => 0,
'timeout' => 15,
'header'=> 'Post-Commit-Hook-Hmac: '.$sign."\r\n"
.'Content-Type: application/json'."\r\n",
)
);
$url = $payload['url'];
$ctx = stream_context_create($params);
$fp = @fopen($url, 'rb', false, $ctx);
if (!$fp) {
return false;
}
$meta = stream_get_meta_data($fp);
@fclose($fp);
if (!isset($meta['wrapper_data'][0]) or $meta['timed_out']) {
return false;
}
if (0 === strpos($meta['wrapper_data'][0], 'HTTP/1.1 2') or
0 === strpos($meta['wrapper_data'][0], 'HTTP/1.1 3')) {
return true;
}
return false;
}
/**
* Process the webhook.
*
*/
public static function process($sender, &$params)
{
$item = $params['item'];
if ($item->type != 'new_commit') {
// We do nothing.
return;
}
if (isset($params['res']['IDF_Webhook::process']) and
$params['res']['IDF_Webhook::process'] == true) {
// Already processed.
return;
}
if ($item->payload['url'] == '') {
// We do nothing.
return;
}
// We have either to retry or to push for the first time.
$res = self::postNotification($item->payload);
if ($res) {
$params['res']['IDF_Webhook::process'] = true;
} elseif ($item->trials >= 9) {
// We are at trial 10, give up
$params['res']['IDF_Webhook::process'] = true;
} else {
// Need to try again
$params['res']['IDF_Webhook::process'] = false;
}
}
}

View File

@ -260,4 +260,9 @@ $cfg['allowed_scm'] = array('git' => 'IDF_Scm_Git',
# $cfg['hg_path'] = 'hg';
# $cfg['git_path'] = 'git';
# If you do not want to have calculations of the repositories, attachments
# and downloads size, set it to true. You can set to false some
# times to times to check the size.
# $cfg['idf_no_size_check'] = false;
return $cfg;

View File

@ -66,7 +66,8 @@ $ctl[] = array('regex' => '#^/logout/$#',
$ctl[] = array('regex' => '#^/help/$#',
'base' => $base,
'model' => 'IDF_Views',
'method' => 'faq');
'method' => 'faq',
'name' => 'idf_faq');
$ctl[] = array('regex' => '#^/p/([\-\w]+)/$#',
'base' => $base,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -5,21 +5,24 @@
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Project-Id-Version: Indefero\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-11-06 20:31+0100\n"
"PO-Revision-Date: 2009-12-24 12:53+0200\n"
"POT-Creation-Date: 2010-04-19 09:26+0200\n"
"PO-Revision-Date: 2010-06-17 18:48+0200\n"
"Last-Translator: Denis Kot <denis.kot@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language-Team: Indefero Russian <denis.kot@gmail.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2&& n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Poedit-Language: Russian\n"
"X-Poedit-Country: RUSSIAN FEDERATION\n"
"X-Poedit-SourceCharset: utf-8\n"
#: IDF/Commit.php:54
#: IDF/Conf.php:54
#: IDF/Issue.php:52
#: IDF/Queue.php:92
#: IDF/Review.php:65
#: IDF/Tag.php:52
#: IDF/Upload.php:49
@ -68,12 +71,12 @@ msgstr "история изменений"
msgid "creation date"
msgstr "дата создания"
#: IDF/Commit.php:222
#: IDF/Commit.php:225
#, php-format
msgid "Commit&nbsp;%s, by %s"
msgstr ""
#: IDF/Commit.php:282
#: IDF/Commit.php:285
#, php-format
msgid "New Commit %s - %s (%s)"
msgstr ""
@ -175,6 +178,7 @@ msgstr "Владелец:"
#: IDF/gettexttemplates/idf/downloads/download-created-email.txt.php:7
#: IDF/gettexttemplates/idf/downloads/view.html.php:16
#: IDF/gettexttemplates/idf/downloads/delete.html.php:11
#: IDF/gettexttemplates/idf/wiki/deletepage.html.php:10
#: IDF/gettexttemplates/idf/wiki/wiki-updated-email.txt.php:9
#: IDF/gettexttemplates/idf/wiki/wiki-updated-email.txt.php:12
#: IDF/gettexttemplates/idf/wiki/view.html.php:15
@ -186,12 +190,12 @@ msgstr "Ярлыки:"
#: IDF/IssueComment.php:171
#, php-format
msgid "Comment on <a href=\"%s\" class=\"%s\">issue&nbsp;%d</a>, by %s"
msgstr ""
msgstr "Комментарий на <a href=\"%s\" class=\"%s\">проблему&nbsp;%d</a>, %s"
#: IDF/IssueComment.php:182
#, php-format
msgid "%s: Comment on issue %d - %s"
msgstr ""
msgstr "%s: Комментарий на проблему %d - %s"
#: IDF/IssueFile.php:64
msgid "file name"
@ -258,14 +262,14 @@ msgstr ""
#: IDF/Issue.php:206
#, php-format
msgid "%s: Issue %d created - %s"
msgstr ""
msgstr "%s: Проблема %d создана - %s"
#: IDF/Issue.php:267
#: IDF/Issue.php:270
#, php-format
msgid "Issue %s - %s (%s)"
msgstr "Проблема %s - %s (%s)"
#: IDF/Issue.php:304
#: IDF/Issue.php:316
#, php-format
msgid "Updated Issue %s - %s (%s)"
msgstr "Обновить проблему %s - %s (%s)"
@ -289,7 +293,7 @@ msgstr "краткое имя"
#: IDF/Project.php:70
msgid "Used in the url to access the project, must be short with only letters and numbers."
msgstr ""
msgstr "Используется в адресе для доступа к проекту. Только буквы и цифры."
#: IDF/Project.php:78
msgid "short description"
@ -306,7 +310,7 @@ msgstr "описания"
#: IDF/Project.php:87
msgid "The description can be extended using the markdown syntax."
msgstr ""
msgstr "Описание может быть расширенно использованием markdown синтакса."
#: IDF/Project.php:93
msgid "private"
@ -319,11 +323,11 @@ msgstr "Проект \"%s\" не найден."
#: IDF/Tag.php:59
msgid "tag class"
msgstr ""
msgstr "пометить класс"
#: IDF/Tag.php:60
msgid "The class of the tag."
msgstr ""
msgstr "Класс метки."
#: IDF/Tag.php:73
msgid "lcname"
@ -339,7 +343,7 @@ msgstr "файл"
#: IDF/Upload.php:71
msgid "The path is relative to the upload path."
msgstr ""
msgstr "Путь относителен пути загрузки."
#: IDF/Upload.php:78
msgid "file size in bytes"
@ -372,7 +376,6 @@ msgstr "Новый файл - %s (%s)"
#: IDF/Views.php:44
#: IDF/gettexttemplates/idf/faq.html.php:35
#: IDF/gettexttemplates/idf/faq-api.html.php:4
#: IDF/gettexttemplates/idf/user/public.html.php:6
#: IDF/gettexttemplates/idf/index.html.php:3
#: IDF/gettexttemplates/idf/gadmin/base.html.php:9
#: IDF/Views/Admin.php:57
@ -393,23 +396,23 @@ msgstr "Подтвердите создание аккаунта"
msgid "Welcome! You can now participate in the life of your project of choice."
msgstr "Добро пожаловать! Теперь Вы можете учавствовать в жизни выбранного проекта."
#: IDF/Views.php:189
#: IDF/Views.php:214
#: IDF/Views.php:255
#: IDF/Views.php:191
#: IDF/Views.php:215
#: IDF/Views.php:256
msgid "Password Recovery"
msgstr "Восттановление пароля"
#: IDF/Views.php:234
#: IDF/Views.php:235
msgid "Welcome back! Next time, you can use your broswer options to remember the password."
msgstr "Рады Вас снова видеть! Следющий раз Вы можете использовать функцию сохранения пароля в Вашем браузере."
#: IDF/Views.php:276
#: IDF/Views.php:277
msgid "Here to Help You!"
msgstr "Здесь чтобы помогать Вам!"
#: IDF/Views.php:292
#: IDF/Views.php:293
msgid "InDefero API (Application Programming Interface)"
msgstr ""
msgstr "InDefero API (Application Programming Interface)"
#: IDF/WikiPage.php:62
msgid "title"
@ -533,7 +536,7 @@ msgid "No, I am a new here."
msgstr "Нет, я новенький здесь."
#: IDF/gettexttemplates/idf/login_form.html.php:7
#: IDF/Views/Admin.php:287
#: IDF/Views/Admin.php:322
msgid "Yes"
msgstr "Да"
@ -558,6 +561,8 @@ msgstr "Вложение к тикету <a href=\"%%url%%\">%%issue.id%%</a>"
#: IDF/gettexttemplates/idf/issues/view.html.php:7
#: IDF/gettexttemplates/idf/downloads/view.html.php:4
#: IDF/gettexttemplates/idf/downloads/delete.html.php:5
#: IDF/gettexttemplates/idf/wiki/deletepage.html.php:4
#: IDF/gettexttemplates/idf/wiki/deletepage.html.php:5
#: IDF/gettexttemplates/idf/wiki/view.html.php:8
#: IDF/gettexttemplates/idf/wiki/view.html.php:9
#: IDF/gettexttemplates/idf/wiki/delete.html.php:7
@ -587,6 +592,7 @@ msgstr "Скачать этот файл"
#: IDF/gettexttemplates/idf/issues/attachment.html.php:7
#: IDF/gettexttemplates/idf/issues/view.html.php:18
#: IDF/gettexttemplates/idf/review/view.html.php:25
#: IDF/gettexttemplates/idf/wiki/deletepage.html.php:8
#: IDF/gettexttemplates/idf/wiki/view.html.php:13
#: IDF/gettexttemplates/idf/wiki/delete.html.php:11
msgid "Created:"
@ -667,14 +673,20 @@ msgid "Comments (last first):"
msgstr "Комментарии (последние первыми):"
#: IDF/gettexttemplates/idf/issues/my-issues.html.php:3
#, php-format
#, fuzzy, php-format
msgid "See the <a href=\"%%submit_closed_url%%\">%%nb_submit_closed%% closed</a>."
msgstr "See the <a href=\"%%submit_closed_url%%\">%%nb_submit_closed%% closed</a>."
msgid_plural "See the <a href=\"%%submit_closed_url%%\">%%nb_submit_closed%% closed</a>."
msgstr[0] "See the <a href=\"%%submit_closed_url%%\">%%nb_submit_closed%% closed</a>."
msgstr[1] "See the <a href=\"%%submit_closed_url%%\">%%nb_submit_closed%% closed</a>."
msgstr[2] "See the <a href=\"%%submit_closed_url%%\">%%nb_submit_closed%% closed</a>."
#: IDF/gettexttemplates/idf/issues/my-issues.html.php:4
#, php-format
#, fuzzy, php-format
msgid "See the <a href=\"%%owner_closed_url%%\">%%nb_owner_closed%% closed</a>."
msgstr "See the <a href=\"%%owner_closed_url%%\">%%nb_owner_closed%% closed</a>."
msgid_plural "See the <a href=\"%%owner_closed_url%%\">%%nb_owner_closed%% closed</a>."
msgstr[0] "See the <a href=\"%%owner_closed_url%%\">%%nb_owner_closed%% closed</a>."
msgstr[1] "See the <a href=\"%%owner_closed_url%%\">%%nb_owner_closed%% closed</a>."
msgstr[2] "See the <a href=\"%%owner_closed_url%%\">%%nb_owner_closed%% closed</a>."
#: IDF/gettexttemplates/idf/issues/my-issues.html.php:5
#: IDF/gettexttemplates/idf/issues/base.html.php:4
@ -777,10 +789,12 @@ msgstr "Отправить проблему"
#: IDF/gettexttemplates/idf/user/passrecovery-inputkey.html.php:5
#: IDF/gettexttemplates/idf/user/changeemail.html.php:5
#: IDF/gettexttemplates/idf/user/passrecovery-ask.html.php:5
#: IDF/gettexttemplates/idf/wiki/deletepage.html.php:7
#: IDF/gettexttemplates/idf/wiki/update.html.php:7
#: IDF/gettexttemplates/idf/wiki/create.html.php:7
#: IDF/gettexttemplates/idf/wiki/delete.html.php:10
#: IDF/gettexttemplates/idf/gadmin/users/update.html.php:12
#: IDF/gettexttemplates/idf/gadmin/users/create.html.php:5
#: IDF/gettexttemplates/idf/gadmin/projects/update.html.php:16
#: IDF/gettexttemplates/idf/gadmin/projects/delete.html.php:21
#: IDF/gettexttemplates/idf/register/index.html.php:7
@ -826,9 +840,12 @@ msgid "This issue is marked as closed, add a comment only if you think this issu
msgstr ""
#: IDF/gettexttemplates/idf/issues/view.html.php:8
#, php-format
#, fuzzy, php-format
msgid "%%interested%% person"
msgstr "%%interested%% persons"
msgid_plural "%%interested%% persons"
msgstr[0] "%%interested%% persons"
msgstr[1] "%%interested%% persons"
msgstr[2] "%%interested%% persons"
#: IDF/gettexttemplates/idf/issues/view.html.php:13
msgid "The form contains some errors. Please correct them to change the issue."
@ -842,6 +859,7 @@ msgstr "Сохранить изменения"
#: IDF/gettexttemplates/idf/review/view.html.php:26
#: IDF/gettexttemplates/idf/downloads/view.html.php:14
#: IDF/gettexttemplates/idf/downloads/delete.html.php:9
#: IDF/gettexttemplates/idf/wiki/deletepage.html.php:9
#: IDF/gettexttemplates/idf/wiki/view.html.php:14
#: IDF/gettexttemplates/idf/wiki/delete.html.php:12
msgid "Updated:"
@ -1015,14 +1033,20 @@ msgstr ""
#: IDF/gettexttemplates/idf/review/view.html.php:3
#: IDF/gettexttemplates/idf/source/commit.html.php:3
#, php-format
#, fuzzy, php-format
msgid "%%ndiff%% diff"
msgstr "%%ndiff%% diffs"
msgid_plural "%%ndiff%% diffs"
msgstr[0] "%%ndiff%% diffs"
msgstr[1] "%%ndiff%% diffs"
msgstr[2] "%%ndiff%% diffs"
#: IDF/gettexttemplates/idf/review/view.html.php:4
#, php-format
#, fuzzy, php-format
msgid "%%nc%% comment"
msgstr "%%nc%% comments"
msgid_plural "%%nc%% comments"
msgstr[0] "%%nc%% comments"
msgstr[1] "%%nc%% comments"
msgstr[2] "%%nc%% comments"
#: IDF/gettexttemplates/idf/review/view.html.php:5
msgid ""
@ -1203,6 +1227,7 @@ msgid "Remove this file"
msgstr "Удалить этот файл"
#: IDF/gettexttemplates/idf/downloads/view.html.php:10
#: IDF/gettexttemplates/idf/wiki/update.html.php:9
#: IDF/gettexttemplates/idf/wiki/view.html.php:11
#: IDF/gettexttemplates/idf/gadmin/projects/update.html.php:18
msgid "Trash"
@ -1639,14 +1664,24 @@ msgid "Update Your Account"
msgstr "Обновить профиль"
#: IDF/gettexttemplates/idf/user/myaccount.html.php:11
#, fuzzy
msgid "Your Current SSH Keys"
msgstr "Ваш публичный ssh ключ"
#: IDF/gettexttemplates/idf/user/myaccount.html.php:12
#, fuzzy
msgid "Delete this key"
msgstr "Удалить этот файл"
#: IDF/gettexttemplates/idf/user/myaccount.html.php:13
msgid "If possible, use your real name. By using your real name, people will have more trust in your comments and remarks."
msgstr ""
#: IDF/gettexttemplates/idf/user/myaccount.html.php:12
#: IDF/gettexttemplates/idf/user/myaccount.html.php:14
msgid "The extra password is used to access some of the external systems and the API key is used to interact with this website using a program."
msgstr ""
#: IDF/gettexttemplates/idf/user/myaccount.html.php:13
#: IDF/gettexttemplates/idf/user/myaccount.html.php:15
msgid "Show API key and extra password"
msgstr "Показать ключ API и дополнительный пароль"
@ -1761,6 +1796,21 @@ msgid ""
"The development team.\n"
msgstr ""
#: IDF/gettexttemplates/idf/wiki/deletepage.html.php:3
msgid "If you delete this documentation page, it will be removed from the database with all the associated revisions and <strong>you will not be able to recover it</strong>."
msgstr ""
#: IDF/gettexttemplates/idf/wiki/deletepage.html.php:6
#, fuzzy
msgid "Delete Page"
msgstr "Удалить файл"
#: IDF/gettexttemplates/idf/wiki/deletepage.html.php:11
#: IDF/gettexttemplates/idf/wiki/view.html.php:16
#: IDF/gettexttemplates/idf/wiki/delete.html.php:14
msgid "Old Revisions"
msgstr "Старые ревизии"
#: IDF/gettexttemplates/idf/wiki/wiki-updated-email.txt.php:3
msgid "The following documentation page has been updated:"
msgstr ""
@ -1820,6 +1870,13 @@ msgstr "Форма содержит ошибки. Пожалуйста испр
msgid "Update Page"
msgstr "Обновить страницу"
#: IDF/gettexttemplates/idf/wiki/update.html.php:8
#: IDF/gettexttemplates/idf/wiki/update.html.php:10
#: IDF/gettexttemplates/idf/wiki/update.html.php:11
#, fuzzy
msgid "Delete this page"
msgstr "Удалить этот файл"
#: IDF/gettexttemplates/idf/wiki/create.html.php:4
msgid "The form contains some errors. Please correct them to create the page."
msgstr "Форма содержит ошибки. Пожалуйста исправтье их чтобы создать страницу."
@ -1851,11 +1908,6 @@ msgstr ""
msgid "Delete this revision"
msgstr "Удалить эту ревизию"
#: IDF/gettexttemplates/idf/wiki/view.html.php:16
#: IDF/gettexttemplates/idf/wiki/delete.html.php:14
msgid "Old Revisions"
msgstr "Старые ревизии"
#: IDF/gettexttemplates/idf/wiki/delete.html.php:3
#, php-format
msgid ""
@ -2045,7 +2097,7 @@ msgid "Managed Projects:"
msgstr "Ваших проектов:"
#: IDF/gettexttemplates/idf/gadmin/users/base.html.php:3
#: IDF/Views/Admin.php:197
#: IDF/Views/Admin.php:198
msgid "User List"
msgstr "Список пользователей"
@ -2054,6 +2106,12 @@ msgstr "Список пользователей"
msgid "Update User"
msgstr "Обновить пользователя"
#: IDF/gettexttemplates/idf/gadmin/users/base.html.php:5
#: IDF/gettexttemplates/idf/gadmin/users/create.html.php:4
#, fuzzy
msgid "Create User"
msgstr "Создать страницу"
#: IDF/gettexttemplates/idf/gadmin/users/index.html.php:3
#, php-format
msgid "See <a href=\"%%url%%\">not validated users</a>."
@ -2084,6 +2142,34 @@ msgstr ""
msgid "The form contains some errors. Please correct them to update the user."
msgstr "Форма содержит ошибки. Пожалуйста исправте их чтобы обновить пользователя."
#: IDF/gettexttemplates/idf/gadmin/users/create.html.php:3
#, fuzzy
msgid "The form contains some errors. Please correct them to create the user."
msgstr "Форма содержит ошибки. Пожалуйста исправтье их чтобы создать страницу."
#: IDF/gettexttemplates/idf/gadmin/users/create.html.php:6
#, fuzzy
msgid "The user password will be sent by email to the user."
msgstr "Пароли должны совпадать."
#: IDF/gettexttemplates/idf/gadmin/users/createuser-email.txt.php:3
#, php-format
msgid ""
"Hello %%user%%,\n"
"\n"
"An account on the forge has been created for you by\n"
"the administrator %%admin%%.\n"
"\n"
"Please find here your details to access the forge:\n"
"\n"
" Address: %%url%%\n"
" Login: %%user.login%%\n"
" Password: %%password%%\n"
"\n"
"Yours faithfully,\n"
"The development team.\n"
msgstr ""
#: IDF/gettexttemplates/idf/gadmin/projects/base.html.php:5
msgid "Change Project Details"
msgstr "Изменить данные о проекте"
@ -2326,30 +2412,30 @@ msgstr ""
msgid "Updated Code Review %s - %s (%s)"
msgstr ""
#: IDF/Scm/Git.php:170
#: IDF/Scm/Git.php:172
#, php-format
msgid "Invalid value for the parameter %1$s: %2$s. Use %3$s."
msgstr ""
#: IDF/Scm/Git.php:221
#: IDF/Scm/Mercurial.php:132
#: IDF/Scm/Git.php:223
#: IDF/Scm/Mercurial.php:135
#, php-format
msgid "Folder %1$s not found in commit %2$s."
msgstr ""
#: IDF/Scm/Git.php:328
#: IDF/Scm/Mercurial.php:149
#: IDF/Scm/Git.php:331
#: IDF/Scm/Mercurial.php:152
#, php-format
msgid "Not a valid tree: %s."
msgstr ""
#: IDF/Plugin/SyncMercurial.php:75
#: IDF/Plugin/SyncSvn.php:78
#: IDF/Plugin/SyncMercurial.php:78
#: IDF/Plugin/SyncSvn.php:81
#, php-format
msgid "The repository %s already exists."
msgstr ""
#: IDF/Plugin/SyncMercurial.php:138
#: IDF/Plugin/SyncMercurial.php:142
#, php-format
msgid "%s does not exist or is not writable."
msgstr ""
@ -2420,14 +2506,16 @@ msgstr ""
#: IDF/Form/Upload.php:148
#: IDF/Form/UserChangeEmail.php:80
#: IDF/Form/ReviewFileComment.php:125
#: IDF/Form/Password.php:61
#: IDF/Form/Password.php:76
#: IDF/Form/TabsConf.php:97
#: IDF/Form/UpdateUpload.php:126
#: IDF/Form/Admin/UserCreate.php:108
#: IDF/Form/Admin/ProjectUpdate.php:77
#: IDF/Form/Admin/ProjectCreate.php:215
#: IDF/Form/Admin/UserUpdate.php:129
#: IDF/Form/Admin/ProjectDelete.php:78
#: IDF/Form/UserAccount.php:119
#: IDF/Form/WikiDelete.php:59
#: IDF/Form/UserAccount.php:118
#: IDF/Form/IssueUpdate.php:232
#: IDF/Form/ReviewCreate.php:187
#: IDF/Form/IssueCreate.php:233
@ -2489,10 +2577,11 @@ msgid "Provide either your login or your email to recover your password."
msgstr ""
#: IDF/Form/Password.php:49
#: IDF/Form/Password.php:64
msgid "Sorry, we cannot find a user with this email address or login. Feel free to try again."
msgstr ""
#: IDF/Form/Password.php:80
#: IDF/Form/Password.php:100
msgid "Password Recovery - InDefero"
msgstr "Восстановение пароля - InDefero"
@ -2502,12 +2591,14 @@ msgid "Your confirmation key"
msgstr "Ваш код подтверждения"
#: IDF/Form/RegisterConfirmation.php:50
#: IDF/Form/Admin/UserCreate.php:37
#: IDF/Form/Admin/UserUpdate.php:36
#: IDF/Form/UserAccount.php:38
msgid "First name"
msgstr "Имя"
#: IDF/Form/RegisterConfirmation.php:59
#: IDF/Form/Admin/UserCreate.php:46
#: IDF/Form/Admin/UserUpdate.php:45
#: IDF/Form/UserAccount.php:47
msgid "Last name"
@ -2547,7 +2638,7 @@ msgstr "Пароли должны совпадать."
#: IDF/Form/RegisterConfirmation.php:137
#: IDF/Form/RegisterInputKey.php:72
#: IDF/Form/PasswordReset.php:105
#: IDF/Form/PasswordReset.php:108
#: IDF/Form/PasswordInputKey.php:76
msgid "Cannot save an invalid form."
msgstr "Немогу сохранить неправильную форму."
@ -2598,10 +2689,68 @@ msgstr "Предопределенные ярлыки для проблемы"
msgid "Each issue may have at most one label with each of these classes"
msgstr ""
#: IDF/Form/Admin/UserCreate.php:56
#, fuzzy
msgid "Login"
msgstr "Логин:"
#: IDF/Form/Admin/UserCreate.php:60
#: IDF/Form/Register.php:45
msgid "The login must be between 3 and 15 characters long and contains only letters and digits."
msgstr ""
#: IDF/Form/Admin/UserCreate.php:69
#: IDF/Form/Admin/UserUpdate.php:55
msgid "Email"
msgstr "Эл. адрес"
#: IDF/Form/Admin/UserCreate.php:71
msgid "Double check the email address as the password is directly sent to the user."
msgstr ""
#: IDF/Form/Admin/UserCreate.php:76
#: IDF/Form/Admin/UserUpdate.php:65
#: IDF/Form/UserAccount.php:64
msgid "Language"
msgstr "Язык"
#: IDF/Form/Admin/UserCreate.php:87
#: IDF/Form/UserAccount.php:97
#, fuzzy
msgid "Add a public SSH key"
msgstr "Ваш публичный ssh ключ"
#: IDF/Form/Admin/UserCreate.php:92
#, fuzzy
msgid "Be careful to provide the public key and not the private key!"
msgstr "Будьте осторожны! Нужен публичный ключь, а не личный!"
#: IDF/Form/Admin/UserCreate.php:159
msgid "Your details to access your forge."
msgstr ""
#: IDF/Form/Admin/UserCreate.php:196
#: IDF/Form/UserAccount.php:270
#, php-format
msgid "The email \"%s\" is already used."
msgstr "Эл. адрес \"%s\" уже используется."
#: IDF/Form/Admin/UserCreate.php:205
#: IDF/Form/Register.php:72
#, php-format
msgid "The login \"%s\" can only contain letters and digits."
msgstr ""
#: IDF/Form/Admin/UserCreate.php:210
#: IDF/Form/Register.php:77
#, php-format
msgid "The login \"%s\" is already used, please find another one."
msgstr ""
#: IDF/Form/Admin/ProjectUpdate.php:42
#: IDF/Form/Admin/ProjectCreate.php:48
#: IDF/Views/Admin.php:66
#: IDF/Views/Admin.php:206
#: IDF/Views/Admin.php:207
msgid "Name"
msgstr "Имя"
@ -2670,15 +2819,6 @@ msgstr "Краткое имя уже используется. Пожалуйс
msgid "Click on the Project Management tab to set the description of your project."
msgstr "Кликните на закладку Управление проектом, чтобы задать описание Вашего проекта."
#: IDF/Form/Admin/UserUpdate.php:55
msgid "Email"
msgstr "Эл. адрес"
#: IDF/Form/Admin/UserUpdate.php:65
#: IDF/Form/UserAccount.php:64
msgid "Language"
msgstr "Язык"
#: IDF/Form/Admin/UserUpdate.php:76
msgid "Password"
msgstr "Пароль"
@ -2696,7 +2836,7 @@ msgid "Confirm password"
msgstr "Подтверждение пароля"
#: IDF/Form/Admin/UserUpdate.php:99
#: IDF/Views/Admin.php:207
#: IDF/Views/Admin.php:208
msgid "Staff"
msgstr ""
@ -2705,7 +2845,7 @@ msgid "If you give staff rights to a user, you really need to trust him."
msgstr ""
#: IDF/Form/Admin/UserUpdate.php:110
#: IDF/Views/Admin.php:209
#: IDF/Views/Admin.php:210
msgid "Active"
msgstr "Активный"
@ -2722,7 +2862,7 @@ msgid "A user with this email already exists, please provide another email addre
msgstr ""
#: IDF/Form/Admin/UserUpdate.php:214
#: IDF/Form/UserAccount.php:243
#: IDF/Form/UserAccount.php:285
msgid "The passwords do not match. Please give them again."
msgstr ""
@ -2742,6 +2882,15 @@ msgstr ""
msgid "Sorry, you really need to backup your data before deletion."
msgstr ""
#: IDF/Form/WikiDelete.php:39
msgid "Yes, I understand that the page and all its revisions will be deleted."
msgstr ""
#: IDF/Form/WikiDelete.php:50
#, fuzzy
msgid "You need to confirm the deletion."
msgstr "Будет выдан запрос для подтвеждения."
#: IDF/Form/UserAccount.php:57
msgid "Your mail"
msgstr "Ваша почта"
@ -2754,27 +2903,30 @@ msgstr "Если Вы измените адрес эл. почты, то на н
msgid "Leave blank if you do not want to change your password."
msgstr "Оставьте поле пустым, если Вы не хотите менять пароль."
#: IDF/Form/UserAccount.php:97
msgid "Your public SSH key"
msgstr "Ваш публичный ssh ключ"
#: IDF/Form/UserAccount.php:102
msgid "Be careful to provide your public key and not your private key!"
msgstr "Будьте осторожны! Нужен публичный ключь, а не личный!"
#: IDF/Form/UserAccount.php:148
#: IDF/Form/UserAccount.php:147
msgid "Confirm your new email address."
msgstr ""
#: IDF/Form/UserAccount.php:151
#: IDF/Form/UserAccount.php:150
#, php-format
msgid "A validation email has been sent to \"%s\" to validate the email address change."
msgstr ""
#: IDF/Form/UserAccount.php:228
#, php-format
msgid "The email \"%s\" is already used."
msgstr "Эл. адрес \"%s\" уже используется."
#: IDF/Form/UserAccount.php:210
msgid "The format of the key is not valid. It must start with ssh-dss or ssh-rsa, a long string on a single line and at the end a comment."
msgstr ""
#: IDF/Form/UserAccount.php:220
msgid "Please check the key as it does not appears to be a valid key."
msgstr ""
#: IDF/Form/UserAccount.php:230
msgid "You already have uploaded this SSH key."
msgstr ""
#: IDF/Form/IssueUpdate.php:65
#: IDF/Form/ReviewCreate.php:83
@ -2822,12 +2974,16 @@ msgstr ""
msgid "Initial patch to be reviewed."
msgstr ""
#: IDF/Form/PasswordReset.php:86
#: IDF/Form/PasswordReset.php:77
msgid "This account is not active. Please contact the forge administrator to activate it."
msgstr ""
#: IDF/Form/PasswordReset.php:89
#: IDF/Form/PasswordInputKey.php:50
msgid "We are sorry but this validation key is not valid. Maybe you should directly copy/paste it from your validation email."
msgstr ""
#: IDF/Form/PasswordReset.php:97
#: IDF/Form/PasswordReset.php:100
#: IDF/Form/PasswordInputKey.php:61
msgid "Sorry, but this verification key has expired, please restart the password recovery sequence. For security reasons, the verification key is only valid 24h."
msgstr ""
@ -2897,9 +3053,12 @@ msgid "Initial page creation"
msgstr ""
#: IDF/Form/MembersConf.php:104
#, php-format
#, fuzzy, php-format
msgid "The following login is invalid: %s."
msgstr "The following login are invalids: %s."
msgid_plural "The following login are invalids: %s."
msgstr[0] "The following login are invalids: %s."
msgstr[1] "The following login are invalids: %s."
msgstr[2] "The following login are invalids: %s."
#: IDF/Form/WikiConf.php:49
msgid "Predefined documentation page labels"
@ -2925,10 +3084,6 @@ msgstr ""
msgid "Your login"
msgstr "Ваш логин"
#: IDF/Form/Register.php:45
msgid "The login must be between 3 and 15 characters long and contains only letters and digits."
msgstr ""
#: IDF/Form/Register.php:53
msgid "Your email"
msgstr "Ваш эл. адрес"
@ -2941,16 +3096,6 @@ msgstr ""
msgid "I agree to the terms and conditions."
msgstr ""
#: IDF/Form/Register.php:72
#, php-format
msgid "The login \"%s\" can only contain letters and digits."
msgstr ""
#: IDF/Form/Register.php:77
#, php-format
msgid "The login \"%s\" is already used, please find another one."
msgstr ""
#: IDF/Form/Register.php:88
msgid "We know, this is boring, but you need to agree with the terms and conditions."
msgstr ""
@ -2960,7 +3105,7 @@ msgstr ""
msgid "The email \"%s\" is already used. If you need, click on the help link to recover your password."
msgstr ""
#: IDF/Form/Register.php:144
#: IDF/Form/Register.php:150
msgid "Confirm the creation of your account."
msgstr ""
@ -3026,27 +3171,25 @@ msgstr "На Вас не назначена ни одна проблема, ур
msgid "All the issues you submitted are fixed, yeah!"
msgstr ""
#: IDF/Views/User.php:117
msgid "Your personal information have been updated."
msgstr ""
#: IDF/Views/User.php:118
#, fuzzy
msgid "Your personal information has been updated."
msgstr "Проект был обновлен."
#: IDF/Views/User.php:127
msgid "Truncated for security reasons."
msgstr "Обрезан в целях безопасности."
#: IDF/Views/User.php:129
msgid "You have not upload your public SSH key yet."
msgstr ""
#: IDF/Views/User.php:132
#: IDF/Views/User.php:128
msgid "Your Account"
msgstr "Ваш аккаунт"
#: IDF/Views/User.php:158
#: IDF/Views/User.php:151
#, fuzzy
msgid "The SSH key has been deleted."
msgstr "Файл был удален."
#: IDF/Views/User.php:174
msgid "Confirm The Email Change"
msgstr "Подтвердите изменения эл. адреса"
#: IDF/Views/User.php:183
#: IDF/Views/User.php:199
#, php-format
msgid "Your new email address \"%s\" has been validated. Thank you!"
msgstr ""
@ -3116,7 +3259,7 @@ msgstr "Удалить старую ревизию of %s"
#: IDF/Views/Wiki.php:314
#: IDF/Views/Admin.php:93
#: IDF/Views/Admin.php:245
#: IDF/Views/Admin.php:248
#, php-format
msgid "Update %s"
msgstr ""
@ -3126,6 +3269,16 @@ msgstr ""
msgid "The page <a href=\"%s\">%s</a> has been updated."
msgstr ""
#: IDF/Views/Wiki.php:364
#, fuzzy
msgid "The documentation page has been deleted."
msgstr "Файл был удален."
#: IDF/Views/Wiki.php:372
#, fuzzy, php-format
msgid "Delete Page %s"
msgstr "Удалить файл %s"
#: IDF/Views/Admin.php:60
msgid "This table shows the projects in the forge."
msgstr ""
@ -3164,35 +3317,45 @@ msgstr ""
msgid "Not Validated User List"
msgstr ""
#: IDF/Views/Admin.php:200
#: IDF/Views/Admin.php:202
msgid "This table shows the users in the forge."
msgstr ""
#: IDF/Views/Admin.php:205
#: IDF/Views/Admin.php:206
msgid "login"
msgstr "логин"
#: IDF/Views/Admin.php:208
#: IDF/Views/Admin.php:209
msgid "Admin"
msgstr "Админ"
#: IDF/Views/Admin.php:210
#: IDF/Views/Admin.php:211
msgid "Last Login"
msgstr "Последний вход"
#: IDF/Views/Admin.php:215
#: IDF/Views/Admin.php:218
msgid "No users were found."
msgstr "Пользователей не найдено."
#: IDF/Views/Admin.php:252
#: IDF/Views/Admin.php:255
msgid "You do not have the rights to update this user."
msgstr ""
#: IDF/Views/Admin.php:268
#: IDF/Views/Admin.php:271
msgid "The user has been updated."
msgstr "Пользователь был обновлен."
#: IDF/Views/Admin.php:287
#: IDF/Views/Admin.php:302
#, fuzzy, php-format
msgid "The user %s has been created."
msgstr "Пользователь был обновлен."
#: IDF/Views/Admin.php:309
#, fuzzy
msgid "Add User"
msgstr "Обновить пользователя"
#: IDF/Views/Admin.php:322
msgid "No"
msgstr "Нет"
@ -3498,3 +3661,5 @@ msgstr "Аноним"
msgid "Me"
msgstr "Я"
#~ msgid "Truncated for security reasons."
#~ msgstr "Обрезан в целях безопасности."

View File

@ -84,5 +84,16 @@ Pluf_Signal::connect('IDF_Key::preDelete',
Pluf_Signal::connect('gitpostupdate.php::run',
array('IDF_Plugin_SyncGit', 'entry'));
#
# -- Processing of the webhook queue --
Pluf_Signal::connect('queuecron.php::run',
array('IDF_Queue', 'process'));
#
# Processing of a given webhook, the hook can be configured
# directly in the configuration file if a different solution
# is required.
Pluf_Signal::connect('IDF_Queue::processItem',
Pluf::f('idf_hook_process_item',
array('IDF_Webhook', 'process')));
return $m;

View File

@ -1,7 +1,7 @@
{extends "idf/admin/base.html"}
{block docclass}yui-t1{assign $inSource = true}{/block}
{block docclass}yui-t3{assign $inSource = true}{/block}
{block body}
{if $remote_svn and $form.errors}
{if $form.errors}
<div class="px-message-error">
<p>{trans 'The form contains some errors. Please correct them to update the source configuration.'}</p>
{if $form.get_top_errors}
@ -37,13 +37,25 @@
<td>{if $form.f.svn_password.errors}{$form.f.svn_password.fieldErrors}{/if}
{$form.f.svn_password|unsafe}
</td>
</tr>{/if}
<tr>
<th>{$form.f.webhook_url.labelTag}:</th>
<td>{if $form.f.webhook_url.errors}{$form.f.webhook_url.fieldErrors}{/if}
{$form.f.webhook_url|unsafe}<br>
</td>
</tr>
<tr>
<th>{trans 'Post-commit authentication key:'}</th>
<td>{$hookkey}
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>
<input type="submit" value="{trans 'Save Changes'}" name="submit" />
</td>
</tr>{/if}
</tr>
</table>
</form>
{/block}
@ -51,4 +63,31 @@
<div class="issue-submit-info">
<p>{blocktrans}You can find here the current repository configuration of your project.{/blocktrans}</p>
</div>
<br>
<div class="issue-submit-info">
{blocktrans}<p>The webhook URL setting specifies a URL to which a HTTP POST
request is sent after each repository commit. If this field is empty,
notifications are disabled.</p>
<p>Only properly-escaped <strong>HTTP</strong> URLs are supported, for example:</p>
<ul>
<li>http://domain.com/commit</li>
<li>http://domain.com/commit?my%20param</li>
</ul>
<p>In addition, the URL may contain the following "%" notation, which
will be replaced with specific project values for each commit:</p>
<ul>
<li>%p - project name</li>
<li>%r - revision number</li>
</ul>
<p>For example, committing revision 123 to project 'my-project' with
post-commit URL http://mydomain.com/%p/%r would send a request to
http://mydomain.com/my-project/123.</p>{/blocktrans}</div>
{/block}