Hi

I've just completed a module that integrates the project module with a Subversion repository. I would like to contribute this code to Drupal but I have no idea how to go about this. The code is as follows:

/*
    $Id: project_svn.module,v 1.7 2005/10/10 22:59:53 axon Exp $

    Copyright (C) 2005 Ricardo Gladwell

    This file is part of Drupal Subversion Project Module.

    Drupal Subversion Project Module 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.

    Drupal Subversion Project Module 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 Drupal Project Module; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

function project_svn_help($section) {
  switch ($section) {
    case 'admin/modules#description':
      return t('Support for Subversion integration with the project module.');
  }
}

function project_svn_settings() {;
  if (!file_exists(variable_get('project_svn_binary', '/usr/bin/svn'))) {
    form_set_error('project_svn_binary', t('Subversion binary does not exist.'));
  }

  // TODO check that permissions are correctly set for repo and config files.
  if(!is_dir(variable_get('project_svn_repo', ''))) {
    form_set_error('project_svn_repo', t('No such path for Subversion repository.'));
  }

  $output .= form_textfield(t('Path to Subversion Binary'), 'project_svn_binary', variable_get('project_svn_binary', '/usr/bin/svn'), 30, 255, t("Path to the local subversion binary."));
  $output .= form_textfield(t('Repository Path'), 'project_svn_repo', variable_get('project_svn_repo', ''), 30, 255, t("Path to the local Subversion repository."));
  $output .= form_textfield(t('mod_authz_svn config file'), 'project_svn_mod_authz_svn', variable_get('project_svn_mod_authz_svn', ''), 30, 255, t("Path to mod_authz_svn config file (optional)."));

  return $output;
}

function project_svn_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {

  switch($op) {

    case('insert') :
      if($node->type == 'project_project') {
        project_svn_create_repo($node);
        mod_authz_svn_add($node);
      }
      break;

    case('update') :
      if($node->type == 'project_project') {
        project_svn_update_repo($node);
        mod_authz_svn_add($node);
      }
      break;

    case('delete') :
      if($node->type == 'project_project') {
        project_svn_delete_repo($node);
        mod_authz_svn_delete($node);
      }
      break;

  }

}

function project_svn_load_repo(&$project) {
  return db_fetch_object(db_query('SELECT * FROM {project_svn_repos} WHERE pid=%d', $project->nid));
}

function project_svn_create_repo(&$project) {
  $url = 'file://'.variable_get('project_svn_repo', '').'/'.$project->uri;
  svn_mkdir($url);
  db_query("INSERT INTO {project_svn_repos} (pid, url) VALUES (%d, '%s')", $project->nid, $url);
}

function project_svn_update_repo(&$project) {
  $repo = project_svn_load_repo($project);
  if(!$repo) {
    project_svn_create_repo($project);
  } else {
    $url = 'file://'.variable_get('project_svn_repo', '').'/'.$project->uri;
    if($url != $repo->url) {
      print_r($repo);
      svn_move($repo->url, $url);
      db_query("UPDATE {project_svn_repos} SET url='%s' WHERE pid = %d", $url, $project->nid);
    }
  }
}

function project_svn_delete_repo(&$project) {
  $repo = project_svn_load_repo($project);
  if($repo) {
    svn_delete($repo->url);
    db_query('DELETE FROM {project_svn_repos} WHERE pid=%d', $project->nid);
  }
}

function mod_authz_svn_add(&$project) {
  $file = variable_get('project_svn_mod_authz_svn', '');
  if(!$file) return;

  if(!file_exists($file)) {
    $config['/'] = array('*' => 'r');
  }
  else {
    $config = parse_ini_file($file, true);
  }
  $user_load = array('uid' => $project->uid);
  $user = user_load($user_load);
  $config['/'.$project->uri] = array($user->name => 'rw');
  write_mod_authz_svn($config, $file);
}

function mod_authz_svn_delete(&$project) {
  $file = variable_get('project_svn_mod_authz_svn', '');
  if(!$file || !file_exists($file)) return;
  $config = parse_ini_file($file, true);
  unset($config['/'.$project->uri]);
  write_mod_authz_svn($config, $file);
}

function svn_mkdir($url) {
  $out = array();
  $ret_var = null;
  $cmd = variable_get('project_svn_binary', '/usr/bin/svn').' mkdir --non-interactive -m \'project_svn adding project\' '.$url.' 2>&1';
  
  exec($cmd, $out, $ret_var);
  if($ret_var > 0) {
    watchdog('project_svn', t('Error creating Subversion project for ').$url.': '.join(' ', $out), WATCHDOG_ERROR);
  }
}

function svn_move($from, $to) {
  $out = array();
  $ret_var = null;
  $cmd = variable_get('project_svn_binary', '/usr/bin/svn').' move --non-interactive -m \'project_svn moving project\' '.$from.' '.$to.' 2>&1';
 
  exec($cmd, $out, $ret_var);
  if($ret_var > 0) {
    watchdog('project_svn', t('Error moving Subversion project from ').$from.': '.join(' ', $out), WATCHDOG_ERROR);
  }
}

function svn_delete($url) {
  $out = array();
  $ret_var = null;
  $cmd = variable_get('project_svn_binary', '/usr/bin/svn').' delete --non-interactive -m \'project_svn deleting project\' '.$url.' 2>&1';

  exec($cmd, $out, $ret_var);
  if($ret_var > 0) {
    watchdog('project_svn', t('Error deleting Subversion project for ').$url.': '.join(' ', $out), WATCHDOG_ERROR);
  }
}

function write_mod_authz_svn(&$config, $file) {
  $handle = fopen($file, 'w'); 
  foreach($config as $uri => $users) {
    fwrite($handle,"[".$uri."]\n");
    foreach($users as $user => $perms) {
      fwrite($handle, $user." = ".$perms."\n");
    }
  }
  fclose($handle);
}

Comments

venkat-rk’s picture

You can either apply for a CVS account or raise an issue (just select the project module from the first drop down list) and submit this code as a patch.

Also see: http://drupal.org/node/10259 for guidelines about contributing to drupal

dado’s picture

rgladwell,

Thanks for this exciting contribution! I have been wondering about using Drupal inside my enterprise as a front-end for a Subversion- code repository. I'mlooking for functionality like sourceforge has, w/ project pages, etc. How much development would we have to do to get to that point?

Also, do you have any form of documentation?

Oh and pls be patient w/ the CVS acct. It can take a while.
dado
http://schtickdisc.org

dancasimiro’s picture

I just upgraded this module to work with Drupal 4.7. Here is the code:

/*
    $Id: project_svn.module,v 1.7 2005/10/10 22:59:53 axon Exp $
    Copyright (C) 2005 Ricardo Gladwell
    This file is part of Drupal Subversion Project Module.
    Drupal Subversion Project Module 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.
    Drupal Subversion Project Module 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 Drupal Project Module; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
  /* Create table in postgres with the following command:
   * CREATE TABLE project_svn_repos (
   *      pid integer PRIMARY KEY,
   *      url varchar(255),
   * );
   */
function project_svn_help($section) {
  switch ($section) {
    case 'admin/modules#description':
      return t('Support for Subversion integration with the project module.');
  }
}
function project_svn_settings()
{
  $project_svn_binary = variable_get('project_svn_binary', '/usr/bin/svn');
  $project_svn_repo = variable_get('project_svn_repo', '');

  if (!file_exists($project_svn_binary)) {
    form_set_error('project_svn_binary',
		   t('Subversion binary does not exist.'));
  }
  
  // TODO check that permissions are correctly set for repo and config files.
  if(!is_dir($project_svn_repo)) {
    form_set_error('project_svn_repo',
		   t('No such path for Subversion repository.'));
  }

  $form['project_svn_binary'] =
    array('#type' => 'textarea',
	  '#title'=> 'Path to Subversion Binary',
	  '#cols' => 60,
	  '#rows' => 1,
	  '#description' => t("Path to the local subversion binary."),
	  '#default_value' => $project_svn_binary,
	  '#required' => 1,
	  );

  $form['project_svn_repo'] =
    array('#type' => 'textarea',
	  '#title'=> t('Repository Path'),
	  '#cols' => 60,
	  '#rows' => 1,
	  '#description' => t("Path to the local Subversion repository."),
	  '#default_value' => $project_svn_repo,
	  '#required' => 1,
	  );

  $form['project_svn_mod_authz_svn'] =
    array('#type' => 'textarea',
	  '#title'=> t('mod_authz_svn config file'),
	  '#cols' => 60,
	  '#rows' => 1,
	  '#description' => t("Path to the mod_authz_svn config file"),
	  '#default_value' => variable_get('project_svn_mod_authz_svn', ''),
	  );
				  
  return $form;
}

function project_svn_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
  if ($node->type == 'project_project') {
    switch($op) {
    case 'fields':
      return array('repo');
      break;
    case('insert') :
        project_svn_create_repo($node);
        mod_authz_svn_add($node);
	break;
    case('update') :
      project_svn_update_repo($node);
      mod_authz_svn_add($node);
      break;
    case('delete') :
      project_svn_delete_repo($node);
      mod_authz_svn_delete($node);
      break;
    case('load') :
      $repo = project_svn_load_repo($node);
      return array('repo' => $repo->url,);
      break;
    case 'view':
      $node->body .= theme('project_svn_repo', $node->repo);
      break;
    }
  }
}

function theme_project_svn_repo($repo)
{
  $output  = '<div class="project_svn_repo">';
  $output .= '<h3>Repository</h3>';
  $output .= '<p>' . $repo . '</p>';
  $output .= '</div>';
  return $output;
}

function project_svn_load_repo(&$project) {
  return db_fetch_object(db_query('SELECT * FROM {project_svn_repos} WHERE pid=%d', $project->vid));
}
function project_svn_create_repo(&$project) {
  $url = 'file://'.variable_get('project_svn_repo', '').'/'.$project->uri;
  svn_mkdir($url);
  db_query("INSERT INTO {project_svn_repos} (pid, url) VALUES (%d, '%s')", $project->vid, $url);
}
function project_svn_update_repo(&$project) {
  $repo = project_svn_load_repo($project);
  if(!$repo) {
    project_svn_create_repo($project);
  } else {
    $url = 'file://'.variable_get('project_svn_repo', '').'/'.$project->uri;
    if($url != $repo->url) {
      print_r($repo);
      svn_move($repo->url, $url);
      db_query("UPDATE {project_svn_repos} SET url='%s' WHERE pid = %d", $url, $project->vid);
    }
  }
}
function project_svn_delete_repo(&$project) {
  $repo = project_svn_load_repo($project);
  if($repo) {
    svn_delete($repo->url);
    db_query('DELETE FROM {project_svn_repos} WHERE pid=%d', $project->vid);
  }
}
function mod_authz_svn_add(&$project) {
  $file = variable_get('project_svn_mod_authz_svn', '');
  if(!$file) return;
  if(!file_exists($file)) {
    $config['/'] = array('*' => 'r');
  }
  else {
    $config = parse_ini_file($file, true);
  }
  $user_load = array('uid' => $project->uid);
  $user = user_load($user_load);
  $config['/'.$project->uri] = array($user->name => 'rw');
  write_mod_authz_svn($config, $file);
}
function mod_authz_svn_delete(&$project) {
  $file = variable_get('project_svn_mod_authz_svn', '');
  if(!$file || !file_exists($file)) return;
  $config = parse_ini_file($file, true);
  unset($config['/'.$project->uri]);
  write_mod_authz_svn($config, $file);
}
function svn_mkdir($url) {
  $out = array();
  $ret_var = null;
  $cmd = variable_get('project_svn_binary', '/usr/bin/svn').' mkdir --non-interactive -m \'project_svn adding project\' '.$url.' 2>&1';
  exec($cmd, $out, $ret_var);
  if($ret_var > 0) {
    watchdog('project_svn', t('Error creating Subversion project for ').$url.': '.join(' ', $out), WATCHDOG_ERROR);
  }
}
function svn_move($from, $to) {
  $out = array();
  $ret_var = null;
  $cmd = variable_get('project_svn_binary', '/usr/bin/svn').' move --non-interactive -m \'project_svn moving project\' '.$from.' '.$to.' 2>&1';
  exec($cmd, $out, $ret_var);
  if($ret_var > 0) {
    watchdog('project_svn', t('Error moving Subversion project from ').$from.': '.join(' ', $out), WATCHDOG_ERROR);
  }
}
function svn_delete($url) {
  $out = array();
  $ret_var = null;
  $cmd = variable_get('project_svn_binary', '/usr/bin/svn').' delete --non-interactive -m \'project_svn deleting project\' '.$url.' 2>&1';
  exec($cmd, $out, $ret_var);
  if($ret_var > 0) {
    watchdog('project_svn', t('Error deleting Subversion project for ').$url.': '.join(' ', $out), WATCHDOG_ERROR);
  }
}
function write_mod_authz_svn(&$config, $file) {
  $handle = fopen($file, 'w');
  foreach($config as $uri => $users) {
    fwrite($handle,"[".$uri."]\n");
    foreach($users as $user => $perms) {
      fwrite($handle, $user." = ".$perms."\n");
    }
  }
  fclose($handle);
}
dado’s picture

dancasimiro,
Thanks a lot for this. I am excited to try this as a front end for my corporation's internal subversion code repository. Is it up to the task? Any documentation?

dado
http://schtickdisc.org

dado’s picture

I believe that the module tries to validate the setting for where the svn location is (which it gets from the variables table), before it inserts that value in the variables table. So I could not set this value until I commented this validation code out

if(!is_dir($project_svn_repo)) {
form_set_error('project_svn_repo',
t('No such path for Subversion repository.'));
}

For this module you must use

AuthzSVNAccessFile

method of authorization (for Apache).
I found this helpful
http://svnbook.red-bean.com/en/1.1/ch06s04.html
(and search the page for "AuthzSVNAccessFile")

dado
http://schtickdisc.org