diff --git a/AUTHORS b/AUTHORS
index 0e45cc9..3402a5f 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -5,4 +5,5 @@ Much appreciated contributors:
Nicolas Lassalle - Subversion support
bohwaz
- Benjamin Jorand - Mercurial support
\ No newline at end of file
+ Benjamin Jorand - Mercurial support
+ Baptiste Michaud - Subversion synchronization
\ No newline at end of file
diff --git a/doc/syncsvn.mdtext b/doc/syncsvn.mdtext
new file mode 100644
index 0000000..140fd23
--- /dev/null
+++ b/doc/syncsvn.mdtext
@@ -0,0 +1,75 @@
+# Plugin SyncSvn by Baptiste Michaud
+
+The SyncSvn plugin allow the direct creation and synchronisation of
+subversion repositories with the InDefero database. This requires
+giving access to the repositories using the DAV_SVN module of Apache2.
+
+## To Contact the Author
+
+ Baptiste Michaud
+ bactisme@gmail.com
+ webplay.fr - frandroid.com - lost-in-translation.fr
+
+## Apache configuration
+
+You will first need to install the DAV_SVN module for Apache. On
+Debian/Ubuntu based systems just run:
+
+ $ sudo apt-get install libapache2-svn
+ $ sudo a2enmod dav_svn
+
+Then, you need to configure dav_svn, this is an example of
+configuration:
+
+
+ DAV svn
+ SVNParentPath /home/svn/repositories
+ AuthzSVNAccessFile /home/svn/dav_svn.authz
+ Satisfy Any
+ Require valid-user
+ AuthType Basic
+ AuthName "Subversion Repository"
+ AuthUserFile /home/svn/dav_svn.passwd
+
+
+Be sure to [read the documentation before](http://svnbook.red-bean.com/en/1.5/svn.serverconfig.httpd.html).
+
+The files `/home/svn/dav_svn.authz`, `/home/svn/dav_svn.passwd` and
+the directory `/home/svn/repositories` must be writable by your
+webserver process. To ensure that, do:
+
+ $ sudo mkdir --parents /home/svn/repositories
+ $ sudo touch /home/svn/dav_svn.authz
+ $ sudo touch /home/svn/dav_svn.passwd
+ $ sudo chown -R www-data:www-data /home/svn
+
+Now, you need to restart apache:
+
+ $ sudo /etc/init.d/apache2 force-reload
+
+## InDefero Configuration
+
+Based on the paths provided in the Apache configuration and if your
+Apache server is serving the domain `www.mydomain.com`, the you need
+to put the following in your configuration file:
+
+ $cfg['svn_repositories'] = 'file:///home/svn/repositories/%s';
+ // We add "trunk" to invite people to checkout the trunk of the
+ // project.
+ $cfg['svn_remote_url'] = 'http://www.mydomain.com/svn/%s/trunk';
+
+ // Synchronisation specific configuration variables
+ $cfg['idf_plugin_syncsvn_authz_file'] = '/home/svn/dav_svn.authz';
+ $cfg['idf_plugin_syncsvn_passwd_file'] = '/home/svn/dav_svn.passwd';
+ $cfg['idf_plugin_syncsvn_svn_path'] = '/home/svn/repositories';
+
+You can have more control over the permissions given to the owners,
+members, extra authorized users and anonymous users if you want with
+the following configuration variables:
+
+* **idf_plugin_syncsvn_access_owners ('rw')**: Access for the project owners.
+* **idf_plugin_syncsvn_access_members ('rw')**: Access for the project members.
+* **idf_plugin_syncsvn_access_extra ('r')**: Access for the extra authorized people in case of a private project.
+* **idf_plugin_syncsvn_access_public ('r')**: Anonymous access.
+* **idf_plugin_syncsvn_access_private ('')**: Anonymous access in the case of a private project.
+
diff --git a/src/IDF/Form/TabsConf.php b/src/IDF/Form/TabsConf.php
index e973b36..3c64de2 100644
--- a/src/IDF/Form/TabsConf.php
+++ b/src/IDF/Form/TabsConf.php
@@ -97,6 +97,7 @@ class IDF_Form_TabsConf extends Pluf_Form
$this->project->private = 0;
}
$this->project->update();
+ $this->project->membershipsUpdated();
}
}
diff --git a/src/IDF/Plugin/SyncSvn.php b/src/IDF/Plugin/SyncSvn.php
index c10e087..54b4ff1 100644
--- a/src/IDF/Plugin/SyncSvn.php
+++ b/src/IDF/Plugin/SyncSvn.php
@@ -24,171 +24,175 @@
require_once 'File/Passwd/Authdigest.php'; // $ pear install File_Passwd
/**
- * This classes is a plugin which allows to synchronise access rights between indefero
- * and a DAV powered SVN repository.
+ * This classes is a plugin which allows to synchronise access rights
+ * between indefero and a DAV powered Subversion repository.
*/
class IDF_Plugin_SyncSvn
{
/**
- * Entry point of the each plugins.
+ * Entry point of the plugin.
*/
- static function entry($signal, $params){
- // if not actif, do nothing
-
- if ($signal == 'IDF_Project::created'){
- $project = $params['project'];
-
- $plug = new IDF_Plugin_SyncSVN();
- //$plug->processSVNCreate($project->shortname);
-
- }else if ($signal == 'IDF_Project::membershipsUpdated'){
- $project = $params['project'];
-
- $plug = new IDF_Plugin_SyncSVN();
- $plug->processSyncAuthz($project);
-
- }else if ($signal == 'IDF_User::passwordUpdated'){
- $plug = new IDF_Plugin_SyncSVN();
+ static public function entry($signal, $params)
+ {
+ // First check for the 3 mandatory config variables.
+ if (!Pluf::f('idf_plugin_syncsvn_authz_file', false) or
+ !Pluf::f('idf_plugin_syncsvn_passwd_file', false) or
+ !Pluf::f('idf_plugin_syncsvn_svn_path'. false)) {
+ return;
+ }
+ $plug = new IDF_Plugin_SyncSvn();
+ switch ($signal) {
+ case 'IDF_Project::created':
+ $plug->processSvnCreate($params['project']);
+ break;
+ case 'IDF_Project::membershipsUpdated':
+ $plug->processSyncAuthz($params['project']);
+ break;
+ case 'Pluf_User::passwordUpdated':
$plug->processSyncPasswd($params['user']);
- }else {
- // do nothing
+ break;
}
}
/**
- * Run svnadmin command to create a usable SVN repository
- * @param Project name
+ * Run svnadmin command to create the corresponding Subversion
+ * repository.
+ *
+ * @param IDF_Project
+ * @return bool Success
*/
- function processSVNCreate($shortname){
-
- $svn_path = Pluf::f('idf_plugin_syncsvn_svn_path');
- $svn_import_path = Pluf::f('idf_plugin_syncsvn_svn_import_path');
- $chown_user = Pluf::f('idf_plugin_syncsvn_svn_import_path');
-
- $c = 0;
- $createsvn = "svnadmin create ".$svn_path."/".$shortname;
- Pluf_Utils::runExternal($createsvn, $c);
-
- if ($svn_import_path != ""){
- //perform initial import
- // TODO
+ function processSvnCreate($project)
+ {
+ $shortname = $project->shortname;
+ if (false===($svn_path=Pluf::f('idf_plugin_syncsvn_svn_path',false))) {
+ throw new Pluf_Exception_SettingError("'idf_plugin_syncsvn_svn_path' must be defined in your configuration file.");
}
-
- if ($chown_user != ""){
- $chown = "chown ".$chown_user." ".$svn_path."/".$shortname." -R";
- Pluf_Utils::runExternal($chown, $c);
+ if (file_exists($svn_path.'/'.$shortname)) {
+ throw new Exception(sprintf(__('The repository %s already exists.'),
+ $svn_path.'/'.$shortname));
}
+ $return = 0;
+ $output = array();
+ $cmd = sprintf('svnadmin create %s',
+ escapeshellarg($svn_path.'/'.$shortname));
+ $ll = exec($cmd, $output, $return);
+ return ($return == 0);
}
/**
- * Synchronise an user's password
- * @param $user Pluf_User
+ * Synchronise an user's password.
+ *
+ * @param Pluf_User
*/
- function processSyncPasswd($user){
+ function processSyncPasswd($user)
+ {
$passwd_file = Pluf::f('idf_plugin_syncsvn_passwd_file');
+ if (!file_exists($passwd_file) or !is_writable($passwd_file)) {
+ return false;
+ }
$ht = new File_Passwd_Authbasic($passwd_file);
$ht->parse();
- $ht->setMode(FILE_PASSWD_SHA); // not anymore a option
- $ht->addUser($user, $this->getSVNPass($user));
+ $ht->setMode(FILE_PASSWD_SHA);
+ if ($ht->userExists($user->login)) {
+ $ht->changePasswd($user->login, $this->getSvnPass($user));
+ } else {
+ $ht->addUser($user->login, $this->getSvnPass($user));
+ }
$ht->save();
+ return true;
}
/**
- * Synchronize the authz file and the passwd file for the project
- * @param $project IDF_Project
+ * Synchronize the authz file and the passwd file for the project.
+ *
+ * @param IDF_Project
*/
- function processSyncAuthz($project){
- //synchronise authz file
+ function processSyncAuthz($project)
+ {
$this->SyncAccess();
- //synchronise pass file for
$this->generateProjectPasswd($project);
}
/**
* Get the repository password for the user
*/
- function getSVNPass($user){
+ function getSvnPass($user){
return substr(sha1($user->password.Pluf::f('secret_key')), 0, 8);
}
/**
* For a particular project: update all passwd information
*/
- function generateProjectPasswd($project){
+ function generateProjectPasswd($project)
+ {
$passwd_file = Pluf::f('idf_plugin_syncsvn_passwd_file');
+ if (!file_exists($passwd_file) or !is_writable($passwd_file)) {
+ return false;
+ }
$ht = new File_Passwd_Authbasic($passwd_file);
-
- $ht->setMode(FILE_PASSWD_SHA); // not anymore a option
+ $ht->setMode(FILE_PASSWD_SHA);
$ht->parse();
-
$mem = $project->getMembershipData();
- $members = $mem['members'];
- $owners = $mem['owners'];
-
- foreach($owners as $v){
- $ht->addUser($v->login, $this->getSVNPass($v));
- }
-
- foreach($members as $v){
- $ht->addUser($v->login, $this->getSVNPass($v));
+ $members = array_merge((array)$mem['members'], (array)$mem['owners'],
+ (array)$mem['authorized']);
+ foreach($members as $user) {
+ if ($ht->userExists($user->login)) {
+ $ht->changePasswd($user->login, $this->getSvnPass($user));
+ } else {
+ $ht->addUser($user->login, $this->getSvnPass($user));
+ }
}
$ht->save();
}
/**
* Generate the dav_svn.authz file
+ *
+ * We rebuild the complete file each time. This is just to be sure
+ * not to bork the rights when trying to just edit part of the
+ * file.
*/
- function SyncAccess(){
+ function SyncAccess()
+ {
$authz_file = Pluf::f('idf_plugin_syncsvn_authz_file');
- $access_owners = Pluf::f('idf_plugin_syncsvn_access_owners');
- $access_members = Pluf::f('idf_plugin_syncsvn_access_members');
- $access_all = Pluf::f('idf_plugin_syncsvn_access_all');
- $access_all_pivate = Pluf::f('idf_plugin_syncsvn_access_all_pivate');
-
- $projects = Pluf::factory('IDF_Project')->getList();
-
- $fcontent = "";
-
- // for each project
- foreach($projects as $project){
-
+ $access_owners = Pluf::f('idf_plugin_syncsvn_access_owners', 'rw');
+ $access_members = Pluf::f('idf_plugin_syncsvn_access_members', 'rw');
+ $access_extra = Pluf::f('idf_plugin_syncsvn_access_extra', 'r');
+ $access_public = Pluf::f('idf_plugin_syncsvn_access_public', 'r');
+ $access_public_priv = Pluf::f('idf_plugin_syncsvn_access_private', '');
+ if (!file_exists($authz_file) or !is_writable($authz_file)) {
+ return false;
+ }
+ $fcontent = '';
+ foreach (Pluf::factory('IDF_Project')->getList() as $project) {
$conf = new IDF_Conf();
$conf->setProject($project);
-
- if ($conf->getVal('scm', "") == "svn"){
-
- $mem = $project->getMembershipData();
- $members = $mem['members'];
- $owners = $mem['owners'];
-
- // [shortname:/]
- $fcontent .= "[".$project->shortname.":/]\n";
-
- // login = rw
- foreach($owners as $v){
- $fcontent .= $v->login." = ".$access_owners."\n";
- }
- // login = rw
- foreach($members as $v){
- $fcontent .= $v->login." = ".$access_members."\n";
- }
-
- // access for all users
- if ($project->private == true){
- $fcontent .= "* = ".$access_all_pivate."\n";
- }else{
- $fcontent .= "* = ".$access_all."\n";
+ if ($conf->getVal('scm') != 'svn' or
+ strlen($conf->getVal('svn_remote_url')) > 0) {
+ continue;
+ }
+ $mem = $project->getMembershipData();
+ // [shortname:/]
+ $fcontent .= '['.$project->shortname.':/]'."\n";
+ foreach ($mem['owners'] as $v) {
+ $fcontent .= $v->login.' = '.$access_owners."\n";
+ }
+ foreach ($mem['members'] as $v) {
+ $fcontent .= $v->login.' = '.$access_members."\n";
+ }
+ // access for all users
+ if ($project->private == true) {
+ foreach ($mem['authorized'] as $v) {
+ $fcontent .= $v->login.' = '.$access_extra."\n";
}
-
- $fcontent .= "\n";
- } //end if SVN
+ $fcontent .= '* = '.$access_public_priv."\n";
+ } else {
+ $fcontent .= '* = '.$access_public."\n";
+ }
+ $fcontent .= "\n";
}
-
file_put_contents($authz_file, $fcontent, LOCK_EX);
-
- return 0;
+ return true;
}
}
-
-?>
diff --git a/src/IDF/relations.php b/src/IDF/relations.php
index 9f38880..652a2d3 100644
--- a/src/IDF/relations.php
+++ b/src/IDF/relations.php
@@ -38,4 +38,15 @@ $m['IDF_Review'] = array('relate_to' => array('IDF_Project', 'Pluf_User', 'IDF_T
$m['IDF_Review_Patch'] = array('relate_to' => array('IDF_Review', 'Pluf_User'));
$m['IDF_Review_FileComment'] = array('relate_to' => array('IDF_Review_Patch', 'Pluf_User'));
+
+# -- Standard plugins, they will run only if configured --
+#
+# Subversion synchronization
+Pluf_Signal::connect('IDF_Project::membershipsUpdated',
+ array('IDF_Plugin_SyncSvn', 'entry'));
+Pluf_Signal::connect('IDF_Project::created',
+ array('IDF_Plugin_SyncSvn', 'entry'));
+Pluf_Signal::connect('Pluf_User::passwordUpdated',
+ array('IDF_Plugin_SyncSvn', 'entry'));
+
return $m;