$plug = new IDF_Plugin_SyncMonotone();␊ |
switch ($signal) {␊ |
case 'IDF_Project::created':␊ |
$plug->processMonotoneCreate($params['project']);␊ |
$plug->processProjectCreate($params['project']);␊ |
break;␊ |
case 'IDF_Project::preDelete':␊ |
$plug->processProjectDelete($params['project']);␊ |
break;␊ |
case 'IDF_Key::postSave':␊ |
$plug->processKeyCreate($params['key']);␊ |
break;␊ |
case 'IDF_Key::preDelete':␊ |
$plug->processKeyDelete($params['key']);␊ |
break;␊ |
case 'mtnpostpush.php::run':␊ |
$plug->processSyncTimeline($params);␊ |
$plug->processSyncTimeline($params['project']);␊ |
break;␊ |
}␊ |
}␊ |
␊ |
/**␊ |
* Four steps to setup a new monotone project:␊ |
* Initial 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␊ |
* 3) create a new client key for IDF and store it in the project conf␊ |
* 4) write monotonerc␊ |
* 5) add the database as new local server in the usher configuration␊ |
* 6) reload the running usher instance so it acknowledges the new server␊ |
* 7) create read-/write-permissions for the project and add all public␊ |
* keys to the project␊ |
*␊ |
* @param IDF_Project␊ |
*/␊ |
function processMonotoneCreate($project)␊ |
function processProjectCreate($project)␊ |
{␊ |
if ($project->getConf()->getVal('scm') != 'mtn') {␊ |
return;␊ |
|
__('Could not find mtn-post-push script "%s".'), $mtnpostpush␊ |
));␊ |
}␊ |
␊ |
␊ |
$shortname = $project->shortname;␊ |
$projectpath = sprintf($projecttempl, $shortname);␊ |
if (file_exists($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␊ |
));␊ |
}␊ |
$cmd = sprintf('db init -d %s', escapeshellarg($dbfile));␊ |
self::_mtn_exec($cmd);␊ |
␊ |
//␊ |
// step 2) create a server key␊ |
|
$server = $parsed['host'];␊ |
}␊ |
␊ |
$keyname = $shortname.'-server@'.$server;␊ |
$cmd = sprintf(␊ |
Pluf::f('mtn_path', 'mtn').' au generate_key --confdir=%s %s ""',␊ |
$serverkey = $shortname.'-server@'.$server;␊ |
$cmd = sprintf('au generate_key --confdir=%s %s ""',␊ |
escapeshellarg($projectpath),␊ |
escapeshellarg($keyname)␊ |
escapeshellarg($serverkey)␊ |
);␊ |
self::_mtn_exec($cmd);␊ |
␊ |
$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) create a client key, and save it in IDF␊ |
//␊ |
$clientkey_hash = '';␊ |
$monotonerc_tpl = 'monotonerc-noauth.tpl';␊ |
␊ |
if (Pluf::f('mtn_remote_auth', true)) {␊ |
$monotonerc_tpl = 'monotonerc-auth.tpl';␊ |
$keydir = Pluf::f('tmp_folder').'/mtn-client-keys';␊ |
if (!file_exists($keydir)) {␊ |
if (!mkdir($keydir)) {␊ |
throw new IDF_Scm_Exception(sprintf(␊ |
__('The key directory %s could not be created.'), $keydir␊ |
));␊ |
}␊ |
}␊ |
␊ |
$clientkey_name = $shortname.'-client@'.$server;␊ |
$cmd = sprintf('au generate_key --keydir=%s %s ""',␊ |
escapeshellarg($keydir),␊ |
escapeshellarg($clientkey_name)␊ |
);␊ |
$keyinfo = self::_mtn_exec($cmd);␊ |
␊ |
$parsed_keyinfo = array();␊ |
try {␊ |
$parsed_keyinfo = IDF_Scm_Monotone_BasicIO::parse($keyinfo);␊ |
}␊ |
catch (Exception $e) {␊ |
echo $e->getTraceAsString(); exit;␊ |
throw new IDF_Scm_Exception(sprintf(␊ |
__('Could not parse key information: %s'), $e->getMessage()␊ |
));␊ |
}␊ |
␊ |
$clientkey_hash = $parsed_keyinfo[0][1]['hash'];␊ |
$clientkey_file = $keydir . '/' . $clientkey_name . '.' . $clientkey_hash;␊ |
$clientkey_data = file_get_contents($clientkey_file);␊ |
␊ |
$project->getConf()->setVal('mtn_client_key_name', $clientkey_name);␊ |
$project->getConf()->setVal('mtn_client_key_hash', $clientkey_hash);␊ |
$project->getConf()->setVal('mtn_client_key_data', $clientkey_data);␊ |
␊ |
// add the public client key to the server␊ |
$cmd = sprintf('au get_public_key --keydir=%s %s',␊ |
escapeshellarg($keydir),␊ |
escapeshellarg($clientkey_hash)␊ |
);␊ |
$clientkey_pubdata = self::_mtn_exec($cmd);␊ |
␊ |
$cmd = sprintf('au put_public_key --confdir=%s %s',␊ |
escapeshellarg($projectpath),␊ |
escapeshellarg($clientkey_pubdata)␊ |
);␊ |
$keyinfo = self::_mtn_exec($cmd);␊ |
}␊ |
␊ |
//␊ |
// step 3) write monotonerc for access control␊ |
// FIXME: netsync access control is still missing!␊ |
// ␊ |
$monotonerc = file_get_contents(dirname(__FILE__) . "/SyncMonotone/monotonerc.tpl");␊ |
// step 4) write monotonerc␊ |
//␊ |
$monotonerc = file_get_contents(␊ |
dirname(__FILE__).'/SyncMonotone/'.$monotonerc_tpl␊ |
);␊ |
$monotonerc = str_replace(␊ |
array("%%MTNPOSTPUSH%%", "%%PROJECT%%"),␊ |
array($mtnpostpush, $shortname),␊ |
array('%%MTNPOSTPUSH%%', '%%PROJECT%%', '%%MTNCLIENTKEY%%'),␊ |
array($mtnpostpush, $shortname, $clientkey_hash),␊ |
$monotonerc␊ |
);␊ |
␊ |
$rcfile = $projectpath.'/monotonerc';␊ |
␊ |
if (!file_put_contents($rcfile, $monotonerc, LOCK_EX)) {␊ |
if (file_put_contents($rcfile, $monotonerc, LOCK_EX) === false) {␊ |
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␊ |
// step 5) read in and append the usher config with the new server␊ |
//␊ |
$usher_rc = file_get_contents($usher_config);␊ |
$parsed_config = array();␊ |
|
}␊ |
␊ |
// ensure we haven't configured a server with this name already␊ |
foreach ($parsed_config as $stanzas)␊ |
{␊ |
foreach ($stanzas as $stanza_line)␊ |
{␊ |
foreach ($parsed_config as $stanzas) {␊ |
foreach ($stanzas as $stanza_line) {␊ |
if ($stanza_line['key'] == 'server' &&␊ |
$stanza_line['values'][0] == $shortname)␊ |
{␊ |
$stanza_line['values'][0] == $shortname) {␊ |
throw new IDF_Scm_Exception(sprintf(␊ |
__('usher configuration already contains a server '.␊ |
'entry named "%s"'),␊ |
|
␊ |
// 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)) {␊ |
if (file_put_contents($usher_config, $usher_rc, LOCK_EX) === false) {␊ |
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␊ |
// step 6) reload usher to pick up the new configuration␊ |
//␊ |
IDF_Scm_Monotone_Usher::reload();␊ |
␊ |
//␊ |
// step 7) add public monotone keys for the project to␊ |
// read-permissions, write-permissions and the database␊ |
//␊ |
$mtn = IDF_Scm_Monotone::factory($project);␊ |
$stdio = $mtn->getStdio();␊ |
␊ |
$auth_ids = self::getAuthorizedUserIds($project);␊ |
$key_ids = array();␊ |
foreach ($auth_ids as $auth_id) {␊ |
$sql = new Pluf_SQL('user=%s', array($auth_id));␊ |
$keys = Pluf::factory('IDF_Key')->getList(array('filter' => $sql->gen()));␊ |
foreach ($keys as $key) {␊ |
if ($key->getType() != 'mtn')␊ |
continue;␊ |
$stdio->exec(array('put_public_key', $key->content));␊ |
$key_ids[] = $key->getMtnId();␊ |
}␊ |
}␊ |
␊ |
$write_permissions = implode("\n", $key_ids);␊ |
$rcfile = $projectpath.'/write-permissions';␊ |
if (file_put_contents($rcfile, $write_permissions, LOCK_EX) === false) {␊ |
throw new IDF_Scm_Exception(sprintf(␊ |
__('Could not write write-permissions file "%s"'), $rcfile␊ |
));␊ |
}␊ |
␊ |
if ($project->private) {␊ |
$stanza = array(␊ |
array('key' => 'pattern', 'values' => array('*')),␊ |
);␊ |
foreach ($key_ids as $key_id)␊ |
{␊ |
$stanza[] = array('key' => 'allow', 'values' => array($key_id));␊ |
}␊ |
}␊ |
else {␊ |
$stanza = array(␊ |
array('key' => 'pattern', 'values' => array('*')),␊ |
array('key' => 'allow', 'values' => array('*')),␊ |
);␊ |
}␊ |
$read_permissions = IDF_Scm_Monotone_BasicIO::compile(array($stanza));␊ |
$rcfile = $projectpath.'/read-permissions';␊ |
if (file_put_contents($rcfile, $read_permissions, LOCK_EX) === false) {␊ |
throw new IDF_Scm_Exception(sprintf(␊ |
__('Could not write read-permissions file "%s"'), $rcfile␊ |
));␊ |
}␊ |
}␊ |
␊ |
/**␊ |
* Clean up after a mtn project was deleted␊ |
*␊ |
* @param IDF_Project␊ |
*/␊ |
public function processProjectDelete($project)␊ |
{␊ |
if ($project->getConf()->getVal('scm') != 'mtn') {␊ |
return;␊ |
}␊ |
␊ |
$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.'␊ |
);␊ |
}␊ |
␊ |
$shortname = $project->shortname;␊ |
IDF_Scm_Monotone_Usher::killServer($shortname);␊ |
␊ |
$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.'␊ |
);␊ |
}␊ |
␊ |
$projectpath = sprintf($projecttempl, $shortname);␊ |
if (file_exists($projectpath)) {␊ |
if (!self::_delete_recursive($projectpath)) {␊ |
throw new IDF_Scm_Exception(sprintf(␊ |
__('One or more paths underknees %s could not be deleted.'), $projectpath␊ |
));␊ |
}␊ |
}␊ |
␊ |
if (Pluf::f('mtn_remote_auth', true)) {␊ |
$keydir = Pluf::f('tmp_folder').'/mtn-client-keys';␊ |
$keyname = $project->getConf()->getVal('mtn_client_key_name', false);␊ |
$keyhash = $project->getConf()->getVal('mtn_client_key_hash', false);␊ |
if ($keyname && $keyhash &&␊ |
file_exists($keydir .'/'. $keyname . '.' . $keyhash)) {␊ |
if (!@unlink($keydir .'/'. $keyname . '.' . $keyhash)) {␊ |
throw new IDF_Scm_Exception(sprintf(␊ |
__('Could not delete client private key %s'), $keyname␊ |
));␊ |
}␊ |
}␊ |
}␊ |
␊ |
$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()␊ |
));␊ |
}␊ |
␊ |
foreach ($parsed_config as $idx => $stanzas) {␊ |
foreach ($stanzas as $stanza_line) {␊ |
if ($stanza_line['key'] == 'server' &&␊ |
$stanza_line['values'][0] == $shortname) {␊ |
unset($parsed_config[$idx]);␊ |
break;␊ |
}␊ |
}␊ |
}␊ |
␊ |
$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) === false) {␊ |
throw new IDF_Scm_Exception(sprintf(␊ |
__('Could not write usher configuration file "%s"'), $usher_config␊ |
));␊ |
}␊ |
␊ |
IDF_Scm_Monotone_Usher::reload();␊ |
}␊ |
␊ |
/**␊ |
* Adds the (monotone) key to all monotone projects of this forge␊ |
* where the user of the key has write access to␊ |
*/␊ |
public function processKeyCreate($key)␊ |
{␊ |
if ($key->getType() != '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.'␊ |
);␊ |
}␊ |
␊ |
foreach (Pluf::factory('IDF_Project')->getList() as $project) {␊ |
$conf = new IDF_Conf();␊ |
$conf->setProject($project);␊ |
$scm = $conf->getVal('scm', 'mtn');␊ |
if ($scm != 'mtn')␊ |
continue;␊ |
␊ |
$shortname = $project->shortname;␊ |
$projectpath = sprintf($projecttempl, $shortname);␊ |
if (!file_exists($projectpath)) {␊ |
throw new IDF_Scm_Exception(sprintf(␊ |
__('The project path %s does not exists.'), $projectpath␊ |
));␊ |
}␊ |
␊ |
$auth_ids = self::getAuthorizedUserIds($project);␊ |
if (!in_array($key->user, $auth_ids))␊ |
continue;␊ |
␊ |
$mtn_key_id = $key->getMtnId();␊ |
␊ |
// if the project is not defined as private, all people have␊ |
// read access already, so we don't need to write anything␊ |
// and we currently do not check if read-permissions really␊ |
// contains␊ |
// pattern "*"␊ |
// allow "*"␊ |
// which is the default for non-private projects␊ |
if ($project->private == true) {␊ |
$read_perms = file_get_contents($projectpath.'/read-permissions');␊ |
$parsed_read_perms = array();␊ |
try {␊ |
$parsed_read_perms = IDF_Scm_Monotone_BasicIO::parse($read_perms);␊ |
}␊ |
catch (Exception $e) {␊ |
throw new IDF_Scm_Exception(sprintf(␊ |
__('Could not parse read-permissions for project "%s": %s'),␊ |
$shortname, $e->getMessage()␊ |
));␊ |
}␊ |
␊ |
$wildcard_section = null;␊ |
foreach ($parsed_read_perms as $stanzas) {␊ |
foreach ($stanzas as $stanza_line) {␊ |
if ($stanza_line['key'] == 'pattern' &&␊ |
$stanza_line['values'][0] == '*') {␊ |
$wildcard_section =& $stanzas;␊ |
break;␊ |
}␊ |
}␊ |
}␊ |
␊ |
if ($wildcard_section == null)␊ |
{␊ |
$wildcard_section = array(␊ |
array('key' => 'pattern', 'values' => array('*'))␊ |
);␊ |
$parsed_read_perms[] =& $wildcard_section;␊ |
}␊ |
␊ |
$key_found = false;␊ |
foreach ($wildcard_section as $line)␊ |
{␊ |
if ($line['key'] == 'allow' && $line['values'][0] == $mtn_key_id) {␊ |
$key_found = true;␊ |
break;␊ |
}␊ |
}␊ |
␊ |
if (!$key_found) {␊ |
$wildcard_section[] = array(␊ |
'key' => 'allow', 'values' => array($mtn_key_id)␊ |
);␊ |
}␊ |
␊ |
$read_perms = IDF_Scm_Monotone_BasicIO::compile($parsed_read_perms);␊ |
␊ |
if (file_put_contents($projectpath.'/read-permissions',␊ |
$read_perms, LOCK_EX) === false) {␊ |
throw new IDF_Scm_Exception(sprintf(␊ |
__('Could not write read-permissions for project "%s"'), $shortname␊ |
));␊ |
}␊ |
}␊ |
␊ |
$write_perms = file_get_contents($projectpath.'/write-permissions');␊ |
$lines = preg_split("/(\n|\r\n)/", $write_perms);␊ |
if (!in_array('*', $lines) && !in_array($mtn_key_id, $lines)) {␊ |
$lines[] = $mtn_key_id;␊ |
}␊ |
if (file_put_contents($projectpath.'/write-permissions',␊ |
implode("\n", $lines), LOCK_EX) === false) {␊ |
throw new IDF_Scm_Exception(sprintf(␊ |
__('Could not write write-permissions file for project "%s"'),␊ |
$shortname␊ |
));␊ |
}␊ |
␊ |
$mtn = IDF_Scm_Monotone::factory($project);␊ |
$stdio = $mtn->getStdio();␊ |
$stdio->exec(array('put_public_key', $key->content));␊ |
}␊ |
}␊ |
␊ |
/**␊ |
* Removes the (monotone) key from all monotone projects of this forge␊ |
* where the user of the key has write access to␊ |
*/␊ |
public function processKeyDelete($key)␊ |
{␊ |
if ($key->getType() != '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.'␊ |
);␊ |
}␊ |
␊ |
foreach (Pluf::factory('IDF_Project')->getList() as $project) {␊ |
$conf = new IDF_Conf();␊ |
$conf->setProject($project);␊ |
$scm = $conf->getVal('scm', 'mtn');␊ |
if ($scm != 'mtn')␊ |
continue;␊ |
␊ |
$shortname = $project->shortname;␊ |
$projectpath = sprintf($projecttempl, $shortname);␊ |
if (!file_exists($projectpath)) {␊ |
throw new IDF_Scm_Exception(sprintf(␊ |
__('The project path %s does not exists.'), $projectpath␊ |
));␊ |
}␊ |
␊ |
$auth_ids = self::getAuthorizedUserIds($project);␊ |
if (!in_array($key->user, $auth_ids))␊ |
continue;␊ |
␊ |
$mtn_key_id = $key->getMtnId();␊ |
␊ |
// if the project is not defined as private, all people have␊ |
// read access already, so we don't need to write anything␊ |
// and we currently do not check if read-permissions really␊ |
// contains␊ |
// pattern "*"␊ |
// allow "*"␊ |
// which is the default for non-private projects␊ |
if ($project->private === true) {␊ |
$read_perms = file_get_contents($projectpath.'/read-permissions');␊ |
$parsed_read_perms = array();␊ |
try {␊ |
$parsed_read_perms = IDF_Scm_Monotone_BasicIO::parse($read_perms);␊ |
}␊ |
catch (Exception $e) {␊ |
throw new IDF_Scm_Exception(sprintf(␊ |
__('Could not parse read-permissions for project "%s": %s'),␊ |
$shortname, $e->getMessage()␊ |
));␊ |
}␊ |
␊ |
// while we add new keys only to an existing wild-card entry␊ |
// we remove dropped keys from all sections since the key␊ |
// should be simply unavailable for all of them␊ |
foreach ($parsed_read_perms as $stanzas) {␊ |
for ($i=0; $i<count($stanzas); ) {␊ |
if ($stanzas[$i]['key'] == 'allow' &&␊ |
$stanzas[$i]['values'][0] == $mtn_key_id) {␊ |
unset($stanzas[$i]);␊ |
continue;␊ |
}␊ |
++$i;␊ |
}␊ |
}␊ |
␊ |
$read_perms = IDF_Scm_Monotone_BasicIO::compile($parsed_read_perms);␊ |
␊ |
if (file_put_contents($projectpath.'/read-permissions',␊ |
$read_perms, LOCK_EX) === false) {␊ |
throw new IDF_Scm_Exception(sprintf(␊ |
__('Could not write read-permissions for project "%s"'), $shortname␊ |
));␊ |
}␊ |
}␊ |
␊ |
$write_perms = file_get_contents($projectpath.'/write-permissions');␊ |
$lines = preg_split("/(\n|\r\n)/", $write_perms);␊ |
for ($i=0; $i<count($lines); ) {␊ |
if ($lines[$i] == $mtn_key_id) {␊ |
unset($lines[$i]);␊ |
continue;␊ |
}␊ |
++$i;␊ |
}␊ |
if (file_put_contents($projectpath.'/write-permissions',␊ |
implode("\n", $lines), LOCK_EX) === false) {␊ |
throw new IDF_Scm_Exception(sprintf(␊ |
__('Could not write write-permissions file for project "%s"'),␊ |
$shortname␊ |
));␊ |
}␊ |
␊ |
$mtn = IDF_Scm_Monotone::factory($project);␊ |
$stdio = $mtn->getStdio();␊ |
// if the public key did not sign any revisions, drop it from␊ |
// the database as well␊ |
if (strlen($stdio->exec(array('select', 'k:' . $mtn_key_id))) == 0) {␊ |
$stdio->exec(array('drop_public_key', $mtn_key_id));␊ |
}␊ |
}␊ |
}␊ |
␊ |
private static function getAuthorizedUserIds($project)␊ |
{␊ |
$mem = $project->getMembershipData();␊ |
$members = array_merge((array)$mem['members'],␊ |
(array)$mem['owners'],␊ |
(array)$mem['authorized']);␊ |
$userids = array();␊ |
foreach ($members as $member) {␊ |
$userids[] = $member->id;␊ |
}␊ |
return $userids;␊ |
}␊ |
␊ |
/**␊ |
* Update the timeline after a push␊ |
*␊ |
*/␊ |
public function processSyncTimeline($params)␊ |
public function processSyncTimeline($project_name)␊ |
{␊ |
$pname = $params['project'];␊ |
try {␊ |
$project = IDF_Project::getOr404($pname);␊ |
$project = IDF_Project::getOr404($project_name);␊ |
} catch (Pluf_HTTP_Error404 $e) {␊ |
Pluf_Log::event(array(␊ |
'IDF_Plugin_SyncMonotone::processSyncTimeline', ␊ |
'IDF_Plugin_SyncMonotone::processSyncTimeline',␊ |
'Project not found.',␊ |
array($pname, $params)␊ |
array($project_name, $params)␊ |
));␊ |
return false; // Project not found␊ |
}␊ |
␊ |
Pluf_Log::debug(array(␊ |
'IDF_Plugin_SyncMonotone::processSyncTimeline', ␊ |
'Project found', $pname, $project->id␊ |
'IDF_Plugin_SyncMonotone::processSyncTimeline',␊ |
'Project found', $project_name, $project->id␊ |
));␊ |
IDF_Scm::syncTimeline($project, true);␊ |
Pluf_Log::event(array(␊ |
'IDF_Plugin_SyncMonotone::processSyncTimeline',␊ |
'sync', array($pname, $project->id)␊ |
'sync', array($project_name, $project->id)␊ |
));␊ |
}␊ |
␊ |
private static function _mtn_exec($cmd)␊ |
{␊ |
$fullcmd = sprintf('%s %s %s',␊ |
Pluf::f('idf_exec_cmd_prefix', ''),␊ |
Pluf::f('mtn_path', 'mtn'),␊ |
$cmd␊ |
);␊ |
␊ |
$output = $return = null;␊ |
exec($fullcmd, $output, $return);␊ |
if ($return != 0) {␊ |
throw new IDF_Scm_Exception(sprintf(␊ |
__('The command "%s" could not be executed.'), $cmd␊ |
));␊ |
}␊ |
return implode("\n", $output);␊ |
}␊ |
␊ |
private static function _delete_recursive($path)␊ |
{␊ |
if (is_file($path)) {␊ |
return @unlink($path);␊ |
}␊ |
␊ |
if (is_dir($path)) {␊ |
$scan = glob(rtrim($path, '/') . '/*');␊ |
$status = 0;␊ |
foreach ($scan as $subpath) {␊ |
$status |= self::_delete_recursive($subpath);␊ |
}␊ |
$status |= rmdir($path);␊ |
return $status;␊ |
}␊ |
}␊ |
}␊ |