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

This commit is contained in:
Loïc d'Anterroches
2010-09-01 14:51:33 +02:00
17 changed files with 682 additions and 152 deletions

View File

@@ -84,7 +84,7 @@ class IDF_Key extends Pluf_Model
return array('ssh', $m[2], $m[1]);
}
throw new IDF_Exception('invalid or unknown key data detected');
throw new Exception('invalid or unknown key data detected');
}
/**
@@ -123,7 +123,7 @@ class IDF_Key extends Pluf_Model
{
list($type, $keyName, $keyData) = $this->parseContent();
if ($type != 'mtn')
throw new IDF_Exception('key is not a monotone public key');
throw new Exception('key is not a monotone public key');
return sha1($keyName.":".$keyData);
}

View File

@@ -111,7 +111,7 @@ function IDF_Middleware_ContextPreProcessor($request)
$request->project);
$c = array_merge($c, $request->rights);
}
$c['usherConfigured'] = Pluf::f("mtn_usher", null) !== null;
$c['usherConfigured'] = Pluf::f("mtn_usher_conf", null) !== null;
return $c;
}

View File

@@ -0,0 +1,249 @@
<?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) 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 classes is a plugin which allows to synchronise access rights
* between indefero and monotone usher setups.
*/
class IDF_Plugin_SyncMonotone
{
/**
* Entry point of the plugin.
*/
static public function entry($signal, &$params)
{
$plug = new IDF_Plugin_SyncMonotone();
switch ($signal) {
case 'IDF_Project::created':
$plug->processMonotoneCreate($params['project']);
break;
case 'mtnpostpush.php::run':
$plug->processSyncTimeline($params);
break;
}
}
/**
* Four steps to setup a new monotone project:
*
* 1) run mtn db init to initialize a new database underknees
* 'mtn_repositories'
* 2) create a new server key in the same directory
* 3) write monotonerc for access control
* 4) add the database as new local server in the usher configuration
* 5) reload the running usher instance so it acknowledges the new
* server
*
* @param IDF_Project
*/
function processMonotoneCreate($project)
{
if ($project->getConf()->getVal('scm') != 'mtn') {
return;
}
$projecttempl = Pluf::f('mtn_repositories', false);
if ($projecttempl === false) {
throw new IDF_Scm_Exception(
'"mtn_repositories" must be defined in your configuration file.'
);
}
$usher_config = Pluf::f('mtn_usher_conf', false);
if (!$usher_config || !is_writable($usher_config)) {
throw new IDF_Scm_Exception(
'"mtn_usher_conf" does not exist or is not writable.'
);
}
$mtnpostpush = realpath(dirname(__FILE__) . "/../../../scripts/mtn-post-push");
if (!file_exists($mtnpostpush)) {
throw new IDF_Scm_Exception(sprintf(
__('Could not find mtn-post-push script "%s".'), $mtnpostpush
));
}
$shortname = $project->shortname;
$projectpath = sprintf($projecttempl, $shortname);
if (file_exists($projectpath)) {
throw new IDF_Scm_Exception(sprintf(
__('The project path %s already exists.'), $projectpath
));
}
if (!mkdir($projectpath)) {
throw new IDF_Scm_Exception(sprintf(
__('The project path %s could not be created.'), $projectpath
));
}
//
// step 1) create a new database
//
$dbfile = $projectpath.'/database.mtn';
$cmd = sprintf(
Pluf::f('mtn_path', 'mtn').' db init -d %s',
escapeshellarg($dbfile)
);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$output = $return = null;
$ll = exec($cmd, $output, $return);
if ($return != 0) {
throw new IDF_Scm_Exception(sprintf(
__('The database file %s could not be created.'), $dbfile
));
}
//
// step 2) create a server key
//
// try to parse the key's domain part from the remote_url's host
// name, otherwise fall back to the configured Apache server name
$server = $_SERVER['SERVER_NAME'];
$remote_url = Pluf::f('mtn_remote_url');
if (($parsed = parse_url($remote_url)) !== false &&
!empty($parsed['host'])) {
$server = $parsed['host'];
}
$keyname = $shortname.'-server@'.$server;
$cmd = sprintf(
Pluf::f('mtn_path', 'mtn').' au generate_key --confdir=%s %s ""',
escapeshellarg($projectpath),
escapeshellarg($keyname)
);
$cmd = Pluf::f('idf_exec_cmd_prefix', '').$cmd;
$output = $return = null;
$ll = exec($cmd, $output, $return);
if ($return != 0) {
throw new IDF_Scm_Exception(sprintf(
__('The server key %s could not be created.'), $keyname
));
}
//
// step 3) write monotonerc for access control
// FIXME: netsync access control is still missing!
//
$monotonerc = file_get_contents(dirname(__FILE__) . "/SyncMonotone/monotonerc.tpl");
$monotonerc = str_replace(
array("%%MTNPOSTPUSH%%", "%%PROJECT%%"),
array($mtnpostpush, $shortname),
$monotonerc
);
$rcfile = $projectpath.'/monotonerc';
if (!file_put_contents($rcfile, $monotonerc, LOCK_EX)) {
throw new IDF_Scm_Exception(sprintf(
__('Could not write mtn configuration file "%s"'), $rcfile
));
}
//
// step 4) read in and append the usher config with the new server
//
$usher_rc = file_get_contents($usher_config);
$parsed_config = array();
try {
$parsed_config = IDF_Scm_Monotone_BasicIO::parse($usher_rc);
}
catch (Exception $e) {
throw new IDF_Scm_Exception(sprintf(
__('Could not parse usher configuration in "%s": %s'),
$usher_config, $e->getMessage()
));
}
// ensure we haven't configured a server with this name already
foreach ($parsed_config as $stanzas)
{
foreach ($stanzas as $stanza_line)
{
if ($stanza_line['key'] == 'server' &&
$stanza_line['values'][0] == $shortname)
{
throw new IDF_Scm_Exception(sprintf(
__('usher configuration already contains a server '.
'entry named "%s"'),
$shortname
));
}
}
}
$new_server = array(
array('key' => 'server', 'values' => array($shortname)),
array('key' => 'local', 'values' => array(
'--confdir', $projectpath,
'-d', $dbfile
)),
);
$parsed_config[] = $new_server;
$usher_rc = IDF_Scm_Monotone_BasicIO::compile($parsed_config);
// FIXME: more sanity - what happens on failing writes? we do not
// have a backup copy of usher.conf around...
if (!file_put_contents($usher_config, $usher_rc, LOCK_EX)) {
throw new IDF_Scm_Exception(sprintf(
__('Could not write usher configuration file "%s"'), $usher_config
));
}
//
// step 5) reload usher to pick up the new configuration
//
IDF_Scm_Monotone_Usher::reload();
}
/**
* Update the timeline after a push
*
*/
public function processSyncTimeline($params)
{
$pname = $params['project'];
try {
$project = IDF_Project::getOr404($pname);
} catch (Pluf_HTTP_Error404 $e) {
Pluf_Log::event(array(
'IDF_Plugin_SyncMonotone::processSyncTimeline',
'Project not found.',
array($pname, $params)
));
return false; // Project not found
}
Pluf_Log::debug(array(
'IDF_Plugin_SyncMonotone::processSyncTimeline',
'Project found', $pname, $project->id
));
IDF_Scm::syncTimeline($project, true);
Pluf_Log::event(array(
'IDF_Plugin_SyncMonotone::processSyncTimeline',
'sync', array($pname, $project->id)
));
}
}

View File

@@ -0,0 +1,71 @@
-- ***** 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 *****
--
-- controls the access rights for remote_stdio which is used by IDFs frontend
--
function get_remote_automate_permitted(key_identity, command, options)
local read_only_commands = {
"get_corresponding_path", "get_content_changed", "tags", "branches",
"common_ancestors", "packet_for_fdelta", "packet_for_fdata",
"packets_for_certs", "packet_for_rdata", "get_manifest_of",
"get_revision", "select", "graph", "children", "parents", "roots",
"leaves", "ancestry_difference", "toposort", "erase_ancestors",
"descendents", "ancestors", "heads", "get_file_of", "get_file",
"interface_version", "get_attributes", "content_diff",
"file_merge", "show_conflicts", "certs", "keys"
}
for _,v in ipairs(read_only_commands) do
if (v == command[1]) then
return true
end
end
return false
end
_idf_revs = {}
function note_netsync_start(session_id)
_idf_revs[session_id] = {}
end
function note_netsync_revision_received(new_id, revision, certs, session_id)
table.insert(_idf_revs[session_id], new_id)
end
function note_netsync_end (session_id, ...)
if table.getn(_idf_revs[session_id]) == 0 then
return
end
local pin,pout,pid = spawn_pipe("%%MTNPOSTPUSH%%", "%%PROJECT%%");
if pid == -1 then
print("could execute %%MTNPOSTPUSH%%")
return
end
for _,r in ipairs(_idf_revs[session_id]) do
pin:write(r .. "\n")
end
pin:close()
wait(pid)
end

View File

@@ -22,6 +22,7 @@
# ***** END LICENSE BLOCK ***** */
require_once(dirname(__FILE__) . "/Monotone/Stdio.php");
require_once(dirname(__FILE__) . "/Monotone/BasicIO.php");
/**
* Monotone scm class
@@ -121,12 +122,6 @@ class IDF_Scm_Monotone extends IDF_Scm
$branch = "*";
}
if (count($this->_resolveSelector("h:$branch")) == 0) {
throw new IDF_Scm_Exception(
"Branch $branch is empty"
);
}
return $branch;
}
@@ -143,75 +138,6 @@ class IDF_Scm_Monotone extends IDF_Scm
return preg_split("/\n/", $out, -1, PREG_SPLIT_NO_EMPTY);
}
/**
* Parses monotone's basic_io format
*
* @param string $in
* @return array of arrays
*/
private static function _parseBasicIO($in)
{
$pos = 0;
$stanzas = array();
while ($pos < strlen($in)) {
$stanza = array();
while ($pos < strlen($in)) {
if ($in[$pos] == "\n") break;
$stanzaLine = array('key' => '', 'values' => array(), 'hash' => null);
while ($pos < strlen($in)) {
$ch = $in[$pos];
if ($ch == '"' || $ch == '[') break;
++$pos;
if ($ch == ' ') continue;
$stanzaLine['key'] .= $ch;
}
if ($in[$pos] == '[') {
++$pos; // opening square bracket
$stanzaLine['hash'] = substr($in, $pos, 40);
$pos += 40;
++$pos; // closing square bracket
}
else
{
$valCount = 0;
while ($in[$pos] == '"') {
++$pos; // opening quote
$stanzaLine['values'][$valCount] = '';
while ($pos < strlen($in)) {
$ch = $in[$pos]; $pr = $in[$pos-1];
if ($ch == '"' && $pr != '\\') break;
++$pos;
$stanzaLine['values'][$valCount] .= $ch;
}
++$pos; // closing quote
if ($in[$pos] == ' ') {
++$pos; // space
++$valCount;
}
}
for ($i = 0; $i <= $valCount; $i++) {
$stanzaLine['values'][$i] = str_replace(
array("\\\\", "\\\""),
array("\\", "\""),
$stanzaLine['values'][$i]
);
}
}
$stanza[] = $stanzaLine;
++$pos; // newline
}
$stanzas[] = $stanza;
++$pos; // newline
}
return $stanzas;
}
/**
* Queries the certs for a given revision and returns them in an
* associative array array("branch" => array("branch1", ...), ...)
@@ -226,7 +152,7 @@ class IDF_Scm_Monotone extends IDF_Scm
if (!array_key_exists($rev, $certCache)) {
$out = $this->stdio->exec(array('certs', $rev));
$stanzas = self::_parseBasicIO($out);
$stanzas = IDF_Scm_Monotone_BasicIO::parse($out);
$certs = array();
foreach ($stanzas as $stanza) {
$certname = null;
@@ -290,7 +216,7 @@ class IDF_Scm_Monotone extends IDF_Scm
'get_content_changed', $startrev, $file
));
$stanzas = self::_parseBasicIO($out);
$stanzas = IDF_Scm_Monotone_BasicIO::parse($out);
// FIXME: we only care about the first returned content mark
// everything else seem to be very, very rare cases
@@ -326,7 +252,7 @@ class IDF_Scm_Monotone extends IDF_Scm
$out = $this->stdio->exec(array('tags'));
$tags = array();
$stanzas = self::_parseBasicIO($out);
$stanzas = IDF_Scm_Monotone_BasicIO::parse($out);
foreach ($stanzas as $stanza) {
$tagname = null;
foreach ($stanza as $stanzaline) {
@@ -375,7 +301,7 @@ class IDF_Scm_Monotone extends IDF_Scm
));
$files = array();
$stanzas = self::_parseBasicIO($out);
$stanzas = IDF_Scm_Monotone_BasicIO::parse($out);
$folder = $folder == '/' || empty($folder) ? '' : $folder.'/';
foreach ($stanzas as $stanza) {
@@ -525,7 +451,7 @@ class IDF_Scm_Monotone extends IDF_Scm
));
$files = array();
$stanzas = self::_parseBasicIO($out);
$stanzas = IDF_Scm_Monotone_BasicIO::parse($out);
foreach ($stanzas as $stanza) {
if ($stanza[0]['key'] == 'format_version')
@@ -666,7 +592,7 @@ class IDF_Scm_Monotone extends IDF_Scm
));
$newAndPatchedFiles = 0;
$stanzas = self::_parseBasicIO($out);
$stanzas = IDF_Scm_Monotone_BasicIO::parse($out);
foreach ($stanzas as $stanza) {
if ($stanza[0]['key'] == 'patch' || $stanza[0]['key'] == 'add_file')

View File

@@ -0,0 +1,162 @@
<?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) 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 ***** */
/**
* Utility class to parse and compile basic_io stanzas
*
* @author Thomas Keller <me@thomaskeller.biz>
*/
class IDF_Scm_Monotone_BasicIO
{
/**
* Parses monotone's basic_io format
*
* @param string $in
* @return array of arrays
*/
public static function parse($in)
{
$pos = 0;
$stanzas = array();
while ($pos < strlen($in)) {
$stanza = array();
while ($pos < strlen($in)) {
if ($in[$pos] == "\n") break;
$stanzaLine = array('key' => '', 'values' => array(), 'hash' => null);
while ($pos < strlen($in)) {
$ch = $in[$pos];
if ($ch == '"' || $ch == '[') break;
++$pos;
if ($ch == ' ') continue;
$stanzaLine['key'] .= $ch;
}
if ($in[$pos] == '[') {
unset($stanzaLine['values']);
++$pos; // opening square bracket
$stanzaLine['hash'] = substr($in, $pos, 40);
$pos += 40;
++$pos; // closing square bracket
}
else
{
unset($stanzaLine['hash']);
$valCount = 0;
while ($in[$pos] == '"') {
++$pos; // opening quote
$stanzaLine['values'][$valCount] = '';
while ($pos < strlen($in)) {
$ch = $in[$pos]; $pr = $in[$pos-1];
if ($ch == '"' && $pr != '\\') break;
++$pos;
$stanzaLine['values'][$valCount] .= $ch;
}
++$pos; // closing quote
if ($in[$pos] == ' ') {
++$pos; // space
++$valCount;
}
}
for ($i = 0; $i <= $valCount; $i++) {
$stanzaLine['values'][$i] = str_replace(
array("\\\\", "\\\""),
array("\\", "\""),
$stanzaLine['values'][$i]
);
}
}
$stanza[] = $stanzaLine;
++$pos; // newline
}
$stanzas[] = $stanza;
++$pos; // newline
}
return $stanzas;
}
/**
* Compiles monotone's basicio format
*
* @param array $in Array of arrays
* @return string
*/
public static function compile($in)
{
$out = "";
$first = true;
foreach ((array)$in as $sx => $stanza) {
if ($first)
$first = false;
else
$out .= "\n";
$maxkeylength = 0;
foreach ((array)$stanza as $lx => $line) {
if (!array_key_exists('key', $line)) {
throw new IDF_Scm_Exception(
'"key" not found in basicio stanza '.$sx.', line '.$lx
);
}
$maxkeylength = max($maxkeylength, strlen($line['key']));
}
foreach ((array)$stanza as $lx => $line) {
$out .= str_pad($line['key'], $maxkeylength, ' ', STR_PAD_LEFT);
if (array_key_exists('hash', $line)) {
$out .= ' ['.$line['hash'].']';
} else
if (array_key_exists('values', $line)) {
if (!is_array($line['values']) || count($line['values']) == 0) {
throw new IDF_Scm_Exception(
'"values" must be an array of a size >= 1 '.
'in basicio stanza '.$sx.', line '.$lx
);
}
foreach ($line['values'] as $value) {
$out .= ' "'.str_replace(
array("\\", "\""),
array("\\\\", "\\\""),
$value).'"';
}
}
else
{
throw new IDF_Scm_Exception(
'neither "hash" nor "values" found in basicio '.
'stanza '.$sx.', line '.$lx
);
}
$out .= "\n";
}
}
return $out;
}
}

View File

@@ -146,8 +146,9 @@ class IDF_Scm_Monotone_Stdio
return false;
$read = array($this->pipes[1], $this->pipes[2]);
$write = $except = null;
$streamsChanged = stream_select(
$read, $write = null, $except = null, 0, 20000
$read, $write, $except, 0, 20000
);
if ($streamsChanged === false) {

View File

@@ -21,6 +21,8 @@
#
# ***** END LICENSE BLOCK ***** */
require_once(dirname(__FILE__) . "/BasicIO.php");
/**
* Connects with the admininistrative interface of usher,
* the monotone proxy. This class contains only static methods because
@@ -190,40 +192,60 @@ class IDF_Scm_Monotone_Usher
private static function _triggerCommand($cmd)
{
$uc = Pluf::f('mtn_usher');
if (empty($uc['host'])) {
$uc = Pluf::f('mtn_usher_conf', false);
if (!$uc || !is_readable($uc)) {
throw new IDF_Scm_Exception(
'"mtn_usher_conf" is not configured or not readable'
);
}
$parsed_config =
IDF_Scm_Monotone_BasicIO::parse(file_get_contents($uc));
$host = $port = $user = $pass = null;
foreach ($parsed_config as $stanza) {
foreach ($stanza as $line) {
if ($line['key'] == 'adminaddr') {
list($host, $port) = explode(":", @$line['values'][0]);
break;
}
if ($line['key'] == 'userpass') {
$user = @$line['values'][0];
$pass = @$line['values'][1];
}
}
}
if (empty($host)) {
throw new IDF_Scm_Exception('usher host is empty');
}
if (!preg_match('/^\d+$/', $uc['port']) ||
$uc['port'] == 0)
if (!preg_match('/^\d+$/', $port))
{
throw new IDF_Scm_Exception('usher port is invalid');
}
if (empty($uc['user'])) {
if (empty($user)) {
throw new IDF_Scm_Exception('usher user is empty');
}
if (empty($uc['pass'])) {
if (empty($pass)) {
throw new IDF_Scm_Exception('usher pass is empty');
}
$sock = @fsockopen($uc['host'], $uc['port'], $errno, $errstr);
$sock = @fsockopen($host, $port, $errno, $errstr);
if (!$sock) {
throw new IDF_Scm_Exception(
"could not connect to usher: $errstr ($errno)"
);
}
fwrite($sock, 'USERPASS '.$uc['user'].' '.$uc['pass'].'\n');
fwrite($sock, 'USERPASS '.$user.' '.$pass."\n");
if (feof($sock)) {
throw new IDF_Scm_Exception(
'usher closed the connection - probably wrong admin '.
'username or password'
'usher closed the connection - this should not happen'
);
}
fwrite($sock, $cmd.'\n');
fwrite($sock, $cmd."\n");
$out = '';
while (!feof($sock)) {
$out .= fgets($sock);
@@ -232,7 +254,7 @@ class IDF_Scm_Monotone_Usher
$out = rtrim($out);
if ($out == 'unknown command') {
throw new IDF_Scm_Exception("unknown command: $cmd");
throw new IDF_Scm_Exception('unknown command: '.$cmd);
}
return $out;

View File

@@ -181,7 +181,7 @@ class IDF_Views_Source
$request_file));
return new Pluf_HTTP_Response_Redirect($url, 301);
}
if (!$scm->isValidRevision($commit, $request_file)) {
if (!$scm->isValidRevision($commit)) {
// Redirect to the first branch
return new Pluf_HTTP_Response_Redirect($fburl);
}

View File

@@ -73,14 +73,16 @@ $cfg['git_write_remote_url'] = 'git@localhost:%s.git';
$cfg['svn_repositories'] = 'file:///home/svn/repositories/%s';
$cfg['svn_remote_url'] = 'http://localhost/svn/%s';
# Path to the monotone binary
# Path to the monotone binary (you need mtn 0.99 or newer)
$cfg['mtn_path'] = 'mtn';
# Additional options for the started monotone process
$cfg['mtn_opts'] = array('--no-workspace', '--norc');
$cfg['mtn_opts'] = array('--no-workspace', '--no-standard-rcfiles', '--key=');
#
# You can setup monotone for use with indefero in two ways:
# You can setup monotone for use with indefero in several ways. The
# two most-used should be:
#
# 1) One database for everything:
#
# Set 'mtn_repositories' below to a fixed database path, such as
# '/home/mtn/repositories/all_projects.mtn'
#
@@ -94,26 +96,39 @@ $cfg['mtn_opts'] = array('--no-workspace', '--norc');
# the database
#
# 2) One database for every project with 'usher':
# Set 'mtn_remote_url' below to a string which matches your setup.
#
# Download and configure 'usher'
# (mtn clone mtn://monotone.ca?net.venge.monotone.contrib.usher)
# which acts as proxy in front of all single project databases.
# Create a basic configuration file for it and add a secret admin
# username and password. Finally, point the below variable
# 'mtn_usher_conf' to this configuration file.
#
# Then set 'mtn_remote_url' below to a string which matches your setup.
# Again, the '%s' placeholder will be expanded to the project's
# short name. Note that 'mtn_remote_url' is used as internal
# URI (to access the data for indefero) as well as external URI
# (for end users) at the same time.
# (for end users) at the same time. 'mtn_repositories' should then
# point to a directory where all project-related files (databases,
# keys, configurations) are kept, as these are automatically created
# on project creation by IDF.
#
# Then download and configure 'usher'
# (mtn clone mtn://monotone.ca?net.venge.monotone.contrib.usher)
# which acts as proxy in front of all single project databases.
# Usher's server names should be mapped to the project's short names,
# so you end up with something like this for every project:
# Example: 'mtn_repositories' is configured to be '/var/monotone/%s'
#
# server "project"
# local "-d" "/home/mtn/repositories/project.mtn" "*"
# - IDF tries to create /var/monotone/<projectname> as root directory
# - The database is placed in as /var/monotone/<projectname>/database.mtn
# - The server key is put into /var/monotone/<projectname>/keys and
# is named "<projectname>-server@<host>", where host is the host part
# of 'mtn_remote_url'
#
# Alternatively if you assign every project a unique DNS such as
# 'project.my-hosting.biz', you can also configure it like this:
# therefor /var/monotone MUST be read/writable for the www user and all
# files which are created underknees MUST be read/writable by the user
# who is executing the usher instance! The best way to achieve this is with
# default (POSIX) ACLs on /var/monotone.
#
# host "project.my-hosting.biz"
# local "-d" "/home/mtn/repositories/project.mtn" "*"
#
# You could also choose to setup usher by hand, i.e. with individual
# databases, in this case leave 'mtn_usher_conf' below commented out.
#
# Pro: - read and write access can be granted per project
# - no database locking issues
@@ -143,19 +158,11 @@ $cfg['mtn_remote_url'] = 'mtn://my-host.biz/%s';
$cfg['mtn_db_access'] = 'remote';
#
# If configured, this allows basic control of a running usher process
# via the forge administration
#
# 'host' and 'port' must be set to the specific bits from usher's
# configured 'adminaddr', 'user' and 'pass' must match the values set for
# the configured 'userpass' combination
#
#$cfg['mtn_usher'] = array(
# 'host' => 'localhost',
# 'port' => 12345,
# 'user' => 'admin',
# 'pass' => 'admin',
#);
# via the forge administration. The variable must point to the full (writable)
# path of the usher configuration file which gets updated when new projects
# are added
#
#$cfg['mtn_usher_conf'] = '/path/to/usher.conf';
# Mercurial repositories path
#$cfg['mercurial_repositories'] = '/home/mercurial/repositories/%s';

View File

@@ -386,7 +386,7 @@ $ctl[] = array('regex' => '#^/admin/users/(\d+)/$#',
'model' => 'IDF_Views_Admin',
'method' => 'userUpdate');
if (Pluf::f("mtn_usher", null) !== null)
if (Pluf::f("mtn_usher_conf", null) !== null)
{
$ctl[] = array('regex' => '#^/admin/usher/$#',
'base' => $base,

View File

@@ -49,51 +49,58 @@ Pluf_Signal::connect('Pluf_Template_Compiler::construct_template_tags_modifiers'
# -- Standard plugins, they will run only if configured --
#
# Subversion synchronization
Pluf_Signal::connect('IDF_Project::membershipsUpdated',
Pluf_Signal::connect('IDF_Project::membershipsUpdated',
array('IDF_Plugin_SyncSvn', 'entry'));
Pluf_Signal::connect('IDF_Project::created',
Pluf_Signal::connect('IDF_Project::created',
array('IDF_Plugin_SyncSvn', 'entry'));
Pluf_Signal::connect('Pluf_User::passwordUpdated',
Pluf_Signal::connect('Pluf_User::passwordUpdated',
array('IDF_Plugin_SyncSvn', 'entry'));
Pluf_Signal::connect('IDF_Project::preDelete',
Pluf_Signal::connect('IDF_Project::preDelete',
array('IDF_Plugin_SyncSvn', 'entry'));
Pluf_Signal::connect('svnpostcommit.php::run',
Pluf_Signal::connect('svnpostcommit.php::run',
array('IDF_Plugin_SyncSvn', 'entry'));
#
# Mercurial synchronization
Pluf_Signal::connect('IDF_Project::membershipsUpdated',
Pluf_Signal::connect('IDF_Project::membershipsUpdated',
array('IDF_Plugin_SyncMercurial', 'entry'));
Pluf_Signal::connect('IDF_Project::created',
Pluf_Signal::connect('IDF_Project::created',
array('IDF_Plugin_SyncMercurial', 'entry'));
Pluf_Signal::connect('Pluf_User::passwordUpdated',
Pluf_Signal::connect('Pluf_User::passwordUpdated',
array('IDF_Plugin_SyncMercurial', 'entry'));
Pluf_Signal::connect('hgchangegroup.php::run',
Pluf_Signal::connect('hgchangegroup.php::run',
array('IDF_Plugin_SyncMercurial', 'entry'));
#
# Git synchronization
Pluf_Signal::connect('IDF_Project::membershipsUpdated',
Pluf_Signal::connect('IDF_Project::membershipsUpdated',
array('IDF_Plugin_SyncGit', 'entry'));
Pluf_Signal::connect('IDF_Key::postSave',
Pluf_Signal::connect('IDF_Key::postSave',
array('IDF_Plugin_SyncGit', 'entry'));
Pluf_Signal::connect('IDF_Project::created',
Pluf_Signal::connect('IDF_Project::created',
array('IDF_Plugin_SyncGit', 'entry'));
Pluf_Signal::connect('IDF_Key::preDelete',
Pluf_Signal::connect('IDF_Key::preDelete',
array('IDF_Plugin_SyncGit', 'entry'));
Pluf_Signal::connect('gitpostupdate.php::run',
Pluf_Signal::connect('gitpostupdate.php::run',
array('IDF_Plugin_SyncGit', 'entry'));
#
# monotone synchronization
Pluf_Signal::connect('IDF_Project::created',
array('IDF_Plugin_SyncMonotone', 'entry'));
Pluf_Signal::connect('phppostpush.php::run',
array('IDF_Plugin_SyncMonotone', 'entry'));
#
# -- Processing of the webhook queue --
Pluf_Signal::connect('queuecron.php::run',
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
# 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',
Pluf_Signal::connect('IDF_Queue::processItem',
Pluf::f('idf_hook_process_item',
array('IDF_Webhook', 'process')));
return $m;

View File

@@ -14,7 +14,7 @@
<td>{$server.name}</td>
<td>{$server.status}</td>
<td>
{if preg_match("/ACTIVE|RUNNING|SLEEPING/", $server.status)}
{if preg_match("/ACTIVE|WAITING|RUNNING|SLEEPING/", $server.status)}
<a href="{url 'IDF_Views_Admin::usherServerControl', array($server.name, 'stop')}">
{trans 'stop'}</a>
{elseif $server.status == "STOPPED"}

View File

@@ -12,12 +12,12 @@
{foreach $changes as $change}
{aurl 'url', 'IDF_Views_Source::commit', array($project.shortname, $change.scm_id)}
<tr class="log">
<td><a href="{$url}">{$change.creation_dtime|dateago:"wihtout"}</a></td>
<td><a href="{$url}">{$change.creation_dtime|dateago:"without"}</a></td>
<td>{issuetext $change.summary, $request}{if $change.fullmessage}<br /><br />{issuetext $change.fullmessage, $request, true, false, true, true, true}{/if}</td>
</tr>
<tr class="extra">
<td colspan="2">
<div class="helptext right">{trans 'Commit'}&nbsp;<a href="{$url}" class="mono">{$change.scm_id}</a>,
<div class="helptext right">{trans 'Commit'}&nbsp;<a href="{$url}" class="mono">{$change.scm_id}</a>,
{trans 'by'} {showuser $change.get_author(), $request, $change.origauthor}
</div>