#!/usr/bin/php
<?php
// written by aclight to work with subversion
function svnhook_precommit($argc, $argv) {
  $stderr = fopen("php://stderr","w");
  // NOTE:  When called by the subversion hook, the $repos argument will NOT
  // have a trailing slash (eg. /usr/local/subversion/projects_svn)
  $repos = $argv[1];    // repository path
  $txn = $argv[2];      // transaction ID
  $svnlook_path='/usr/bin/svnlook';

fwrite($stderr, $txn."\n");
  $author_command = sprintf("%s author -t %s %s", $svnlook_path, $txn, $repos);
  $author_command = escapeshellcmd($author_command);
  $author=trim(shell_exec($author_command));

  $message_command = sprintf("%s -t %s log %s", $svnlook_path, $txn, $repos);
  $message_command = escapeshellcmd($message_command);
  $message = trim(shell_exec($message_command));
  if (strlen($message) == 0) {
    fwrite($stderr, "Error:  Commit was blocked because the log message was blank.  Please try the commit again after adding a log message to the commit.\n");
    exit(1);   // DONT allow blank log message
  }

  $changed_command = sprintf('%s -t %s changed %s', $svnlook_path, $txn, $repos);
  $changed_command = escapeshellcmd($changed_command);
  $changed = shell_exec($changed_command);
  $changed = trim($changed);
  $changed = explode("\n", $changed);
//fwrite($stderr, "Changed:".print_r($txn, TRUE)."\n");
  $args = array();
  $args[0] = $argv[0];
  $args[1] = '/var/www/example.com/public/sites/all/modules/cvslog/xcvs/xcvs-config.php';
  $args[2] = $author;
  $args[3] = '';
  $args[4] = $changed;
  $argc = count($args);
  $argv = $args;

  global $always_allow_user;
  $always_allow_user = 0;

  include_once('/var/www/example.com/public/sites/all/modules/cvslog/xcvs/xsvn-utility.php');
  $changes = array();
  xsvn_process_files($argv[4], $changes, $txn, $repos);

  // inspect the changes to see if the commit should be rejected due to one or more files
  // not having the svn:mime-type property defined or for having svn:mimi-type=text/plain without also
  // having svn:eol-style=native property
  $prop_errors = array();
fwrite($stderr, "FILE PROPERTIES \n");
  foreach ($changes as $change) {
fwrite($stderr, print_r($change, TRUE)."\n");
    if (!empty($change['block_commit'])) {
      $prop_errors[$change['item_path']] = $change['block_commit'];
    }
  }

  // build error message and exit
  if (!empty($prop_errors)) {
    $error_message = <<<EOF
** ERROR: One or more files in this transaction
** do not have the necessary Subversion
** properties set.  All files added to the repository
** that do NOT have the svn:eol-style=native property
** must have the svn:mime-type property set.
** In addition, every file with the property
** svn:mime-type=text/plain must also have the
** svn:eol-style=native property.
**
** These two properties help to ensure the
** proper behavior of text files when they
** are edited on different operating systems.
**
** A list of files with incorrect properties
** and the property that is incorrect is given
** below this error message.
**
** Please see http://www.example.com/svn_error#properties
** for more information.

EOF;

    foreach($prop_errors as $file => $message) {
      $error_message .= "$file   =>   $message\n";
    }
    fwrite($stderr, $error_message."\n");
    exit(1);
  }



  $argv[4] = $changes;

//fwrite($stderr, print_r($argv, TRUE)."\n");

  include_once('/var/www/example.com/public/sites/all/modules/cvslog/xcvs/xcvs-commitinfo.php');

  xcvs_init($argc, $argv);

  include('/var/www/example.com/public/sites/all/modules/cvslog/xcvs/xcvs-taginfo.php');

  // check to make sure that all changes apply to only ONE branch, tag, or trunk, and if not
  // reject the commit
  $targets = array();
  foreach ($changes as $change) {
    if ($change['target_type'] == 'trunk') {
      if (!in_array("trunk", $targets)) {
        $targets[] = 'trunk';
      }
    }
    elseif ($change['target_type'] == 'branch' || $change['target_type'] == 'tag') {
      if (!in_array($change['target_value'], $targets)) {
        $targets[] = $change['target_value'];
      }
    }
  }

  // check for any additions/deletions/changes to tags or branches, and if there are any
  // call the xcvs-taginfo.php script once for each change
  foreach ($changes as $change) {
    if ($change['item_type'] == 'branch' || $change['item_type'] == 'tag') {
      switch ($change['item_type']) {
        case "branch":
          $type = "T";
          break;
        case "tag":
          $type = "N";
          break;
        default:
          continue;
      }

      switch ($change['op']) {
        case "A":
          $op = "add";
          break;
        case "D":
          $op = "del";
          //$type = "?";    // special case--if tag is deleted, cvs would return the type as ?, but I don't think that's necessary so we will return the type still
          break;
        case "U":
          $op = "mov";  // move
          break;
      }
      // rearrange the arguments how xcvs-taginfo.php expects them to be
      $argv[3] = $change['value'];
      $argv[4] = $type;
      $argv[5] = $op;
      $argv[6] = $change['item_path'];
      xcvs_taginfo_init(count($argv), $argv);
    }
  }

  // now, check to make sure that the commit affects only ONE branch, tag, or trunk of a project, UNLESS
  // the subversion user doing the commit is in the always_allow list, in which case allow it
  if (count($targets) > 1) {    // DON'T allow commit unless in always_allow array
    global $always_allow_user;
    if ($always_allow_user == 0) {    // deny commit
      fwrite($stderr, "Error:  Each commit may only have changes that relate to ONE branch, tag, or trunk of a project.  You may need to make your changes via several commits.  Please try again.\n");
      exit(1);
     }
  }
  fwrite($stderr, "Commit would have been successful.\n");    // this message is only shown if the next statement is exit(1), used for debugging
  exit(0);
}

svnhook_precommit($argc, $argv);

