<?php
/**
  @author  Todd Augsburger <todd@rollerorgans.com>
  @date    2007.08.07
*/
      
if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/');
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once(DOKU_PLUGIN.'admin.php');
require_once(DOKU_INC.'inc/io.php');
 
/**
 * All DokuWiki plugins to extend the admin function
 * need to inherit from this class
 */
class admin_plugin_darcspatch extends DokuWiki_Admin_Plugin {
  var $log=array();
  /**
   * return some info
   */
  function getInfo(){
    return array(
      'author' => 'Todd Augsburger',
      'email'  => 'todd@rollerorgans.com',
      'date'   => '2007-08-07',
      'name'   => 'darcspatch',
      'desc'   => 'manually apply darcs-style raw patches',
      'url'    => 'http://wiki.splitbrain.org/plugin:darcspatch',
    );
  }

  /**
   * return sort order for position in admin menu
   */
  function getMenuSort() {
    return 2;
  }

  /**
   * handle user request
   */
  function handle() {
  }

  /**
   * output appropriate html
   */
  function html() {
    global $conf;
    $this->setupLocale();

    $conf['htmlok']=1;

    $my_page.='====== '.$this->lang['menu']." ======\n";

    switch ($_REQUEST['darcspatch_do']) {
      case 'apply_patch':
        if(isset($_REQUEST['darcspatch_url']) && ($_REQUEST['darcspatch_url'] != '') && ($_REQUEST['darcspatch_url'] != 'http://')) {
          $my_page.=$this->apply_raw_darcs_patch($_REQUEST['darcspatch_url'],DOKU_INC,isset($_REQUEST['test_only']),isset($_REQUEST['reverse']));
        } else {
        	$my_page.='  '.$this->lang['msg_errurl']."\n\n";
        }
        $my_page.=$this->wiki_admin_page();
        break;
      default:
        $my_page.=$this->wiki_admin_page();
    }

    echo $this->plugin_render($my_page, $format='xhtml') ;
  }

  function wiki_admin_page () {
    global $ID;

    $my_page.='^  '.$this->lang['head']."  ^^^\n";
    $my_page.='| //'.$this->lang['instructions']."//  |||\n";
    $my_page.='| <html><form action="'.wl($ID).'" method="post">'.
      '<input type="hidden" name="id" value="'.$ID.'" />'.
      '<input type="hidden" name="do" value="'.$_REQUEST['do'].'"/>'.
      '<input type="hidden" name="page" value="'.$_REQUEST['page'].'"/>'.
      '<input type="hidden" name="darcspatch_do" value="apply_patch" />'.
      '<input name="darcspatch_url" size="80" value="'.((isset($_REQUEST['darcspatch_url']) && ($_REQUEST['darcspatch_url'] != '')) ?$_REQUEST['darcspatch_url']:'http://').'" /></html>  | <html>'.
      '<input type="checkbox" name="test_only" '.(isset($_REQUEST['test_only'])?'checked':'').'/>'.$this->lang['bt_test'].'<br />'.
      '<input type="checkbox" name="reverse" '.(isset($_REQUEST['reverse'])?'checked':'').'/>'.$this->lang['bt_reverse'].'</html>  |  <html>'.
      '<input type="submit" value="'.$this->lang['bt_apply'].'" class="edit"/>'.
      $patch['name']."</form></html> |\n";
 
    $patchlist = explode("\n",trim($this->getConf('patchlist')));
    $my_page.='^  '.$this->lang['head_stored']."  ^^^\n";
    $my_page.='| //'.str_replace('%1',wl($ID,'do=admin,page=config').'#plugin_settings',$this->lang['setup'])."//  |||\n";
    foreach($patchlist as $patch_url) {
      $patch_url = trim($patch_url);
      if($patch_url == '') continue;
      $my_page.='| '.$patch_url.'  | <html><form action="'.wl($ID).'" method="post">'.
      '<input type="hidden" name="id" value="'.$ID.'" />'.
      '<input type="hidden" name="do" value="'.$_REQUEST['do'].'"/>'.
      '<input type="hidden" name="page" value="'.$_REQUEST['page'].'"/>'.
      '<input type="hidden" name="darcspatch_do" value="apply_patch" />'.
      '<input type="hidden" name="darcspatch_url" value="'.$patch_url.'"/>'.
      '<input type="checkbox" name="test_only" '.(isset($_REQUEST['test_only'])?'checked':'').'/>'.$this->lang['bt_test'].'<br />'.
      '<input type="checkbox" name="reverse" '.(isset($_REQUEST['reverse'])?'checked':'').'/>'.$this->lang['bt_reverse'].'</html>  |  <html>'.
      '<input type="submit" value="'.$this->lang['bt_apply'].'" class="edit"/>'.
      $patch['name']."</form></html> |\n";
   }

    $my_page.='<html><form action="'.wl($ID).'" method="post">'.
      '<input type="hidden" name="id" value="'.$ID.'" />'.
      '<input type="hidden" name="do" value="'.$_REQUEST['do'].'"/>'.
      '<input type="hidden" name="page" value="'.$_REQUEST['page'].'"/>'.
      '<input type="submit" value="'.$this->lang['bt_back'].'" class="edit"/>'.
      '</form></html>';
    return $my_page;
  }

  function apply_raw_darcs_patch($patch,$repo,$test_only=false,$reverse=false) {
    $plus = ($reverse ? '-' : '+');
    $minus = ($reverse ? '+' : '-');
    $return_string = '';
    chdir($repo);
    if(($contents = file_get_contents($patch)) !== false) {
      $contents = str_replace("\r",'',$contents);
      $sections = array();
      if(preg_match_all('/(^\[.*?\])\s*\{(.*?)^\}/ms',$contents,$sections) > 0) {
        if($reverse) $sections[1] = array_reverse($sections[1]);
        for($c=0;$c<count($sections[1]);$c++) {
          $section = $sections[2][$c];
          $return_string .= '  '.str_replace("\n","\n  ",$sections[1][$c])."\n";
          $error = false;
          $hunks = preg_split('/^hunk\s/ms',$section);
          array_shift($hunks); // the first will be empty
          $files = array();
          if($reverse) $hunks = array_reverse($hunks);
          foreach($hunks as $hunk) {
            $lines = explode("\n",$hunk);
            if($line = trim(array_shift($lines))) {
              if($pos = strrpos($line,' ')) {
                $fname = substr($line,0,$pos);
                $offset = intval(substr($line,$pos+1));
                $fname = realpath(dirname($fname)).'/'.basename($fname);
                if (isset($files[$fname])) {
                  $target_lines = explode("\n",$files[$fname]);
                  for($i=0;$i<count($target_lines);$i++) {
                    $target_lines[$i] .= "\n";
                  }
                }
                if(($offset > 0) && (isset($files[$fname]) || (($target_lines = @file($fname)) !== false))) {
                  foreach($lines as $line) {
                    if($line[0] == $minus) {
                      if(($offset <= count($target_lines)) && (strcmp(rtrim(substr($line,1)),rtrim($target_lines[$offset-1])) == 0)) {
                        array_splice($target_lines,$offset-1,1);
                      } else {
                        $return_string .= '  '.$this->lang['msg_errmatch'].': '.$fname.' line '.$offset."\n";
                        $return_string .= '  '.rtrim(substr($line,1))."\n";
                        $return_string .= '  '.rtrim($target_lines[$offset-1])."\n";
                        $error = true;
                        break;
                      }
                    }
                    elseif($line[0] == $plus) {
                      if(($offset-1) <= count($target_lines)) {
                        array_splice($target_lines,$offset-1,0,substr($line,1)."\n");
                        $offset++;
                      } else {
                        $return_string .= '  '.$this->lang['msg_errmatch'].': '.$fname.' line '.$offset."\n";
                        $error = true;
                        break;
                      }
                    } elseif(trim($line) == '') {
                    } else {
                      $error = true;
                      $return_string .= '  '.$this->lang['msg_errline'].': '.$line."\n";
                    }
                    if($error) break;
                  }
                  $files[$fname] = implode('',$target_lines);
                } else {
                  $error = true;
                  $return_string .= '  '.$this->lang['msg_erropen'].': '.$fname."\n";
                }
              }
            }
            if($error) break;
          }
          if(!$error) {
            foreach($files as $fname => $contents) {
              if($test_only)
                $return_string .= '  '.$this->lang['msg_test'].': '.$fname."\n";
              elseif(io_saveFile($fname,$contents))
                $return_string .= '  '.$this->lang['msg_success'].': '.$fname."\n";
              else
                $return_string .= '  '.$this->lang['msg_errwrite'].': '.$fname."\n";
            }
          }
          $return_string .= "\n\n";
        }
      }
    }
    return $return_string;
  }
}
