1<?php 2/** 3 @author Todd Augsburger <todd@rollerorgans.com> 4 @date 2007.08.07 5*/ 6 7if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../').'/'); 8if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 9require_once(DOKU_PLUGIN.'admin.php'); 10require_once(DOKU_INC.'inc/io.php'); 11 12/** 13 * All DokuWiki plugins to extend the admin function 14 * need to inherit from this class 15 */ 16class admin_plugin_darcspatch extends DokuWiki_Admin_Plugin { 17 var $log=array(); 18 /** 19 * return some info 20 */ 21 function getInfo(){ 22 return array( 23 'author' => 'Todd Augsburger', 24 'email' => 'todd@rollerorgans.com', 25 'date' => '2007-08-07', 26 'name' => 'darcspatch', 27 'desc' => 'manually apply darcs-style raw patches', 28 'url' => 'http://wiki.splitbrain.org/plugin:darcspatch', 29 ); 30 } 31 32 /** 33 * return sort order for position in admin menu 34 */ 35 function getMenuSort() { 36 return 2; 37 } 38 39 /** 40 * handle user request 41 */ 42 function handle() { 43 } 44 45 /** 46 * output appropriate html 47 */ 48 function html() { 49 global $conf; 50 $this->setupLocale(); 51 52 $conf['htmlok']=1; 53 54 $my_page.='====== '.$this->lang['menu']." ======\n"; 55 56 switch ($_REQUEST['darcspatch_do']) { 57 case 'apply_patch': 58 if(isset($_REQUEST['darcspatch_url']) && ($_REQUEST['darcspatch_url'] != '') && ($_REQUEST['darcspatch_url'] != 'http://')) { 59 $my_page.=$this->apply_raw_darcs_patch($_REQUEST['darcspatch_url'],DOKU_INC,isset($_REQUEST['test_only']),isset($_REQUEST['reverse'])); 60 } else { 61 $my_page.=' '.$this->lang['msg_errurl']."\n\n"; 62 } 63 $my_page.=$this->wiki_admin_page(); 64 break; 65 default: 66 $my_page.=$this->wiki_admin_page(); 67 } 68 69 echo $this->plugin_render($my_page, $format='xhtml') ; 70 } 71 72 function wiki_admin_page () { 73 global $ID; 74 75 $my_page.='^ '.$this->lang['head']." ^^^\n"; 76 $my_page.='| //'.$this->lang['instructions']."// |||\n"; 77 $my_page.='| <html><form action="'.wl($ID).'" method="post">'. 78 '<input type="hidden" name="id" value="'.$ID.'" />'. 79 '<input type="hidden" name="do" value="'.$_REQUEST['do'].'"/>'. 80 '<input type="hidden" name="page" value="'.$_REQUEST['page'].'"/>'. 81 '<input type="hidden" name="darcspatch_do" value="apply_patch" />'. 82 '<input name="darcspatch_url" size="80" value="'.((isset($_REQUEST['darcspatch_url']) && ($_REQUEST['darcspatch_url'] != '')) ?$_REQUEST['darcspatch_url']:'http://').'" /></html> | <html>'. 83 '<input type="checkbox" name="test_only" '.(isset($_REQUEST['test_only'])?'checked':'').'/>'.$this->lang['bt_test'].'<br />'. 84 '<input type="checkbox" name="reverse" '.(isset($_REQUEST['reverse'])?'checked':'').'/>'.$this->lang['bt_reverse'].'</html> | <html>'. 85 '<input type="submit" value="'.$this->lang['bt_apply'].'" class="edit"/>'. 86 $patch['name']."</form></html> |\n"; 87 88 $patchlist = explode("\n",trim($this->getConf('patchlist'))); 89 $my_page.='^ '.$this->lang['head_stored']." ^^^\n"; 90 $my_page.='| //'.str_replace('%1',wl($ID,'do=admin,page=config').'#plugin_settings',$this->lang['setup'])."// |||\n"; 91 foreach($patchlist as $patch_url) { 92 $patch_url = trim($patch_url); 93 if($patch_url == '') continue; 94 $my_page.='| '.$patch_url.' | <html><form action="'.wl($ID).'" method="post">'. 95 '<input type="hidden" name="id" value="'.$ID.'" />'. 96 '<input type="hidden" name="do" value="'.$_REQUEST['do'].'"/>'. 97 '<input type="hidden" name="page" value="'.$_REQUEST['page'].'"/>'. 98 '<input type="hidden" name="darcspatch_do" value="apply_patch" />'. 99 '<input type="hidden" name="darcspatch_url" value="'.$patch_url.'"/>'. 100 '<input type="checkbox" name="test_only" '.(isset($_REQUEST['test_only'])?'checked':'').'/>'.$this->lang['bt_test'].'<br />'. 101 '<input type="checkbox" name="reverse" '.(isset($_REQUEST['reverse'])?'checked':'').'/>'.$this->lang['bt_reverse'].'</html> | <html>'. 102 '<input type="submit" value="'.$this->lang['bt_apply'].'" class="edit"/>'. 103 $patch['name']."</form></html> |\n"; 104 } 105 106 $my_page.='<html><form action="'.wl($ID).'" method="post">'. 107 '<input type="hidden" name="id" value="'.$ID.'" />'. 108 '<input type="hidden" name="do" value="'.$_REQUEST['do'].'"/>'. 109 '<input type="hidden" name="page" value="'.$_REQUEST['page'].'"/>'. 110 '<input type="submit" value="'.$this->lang['bt_back'].'" class="edit"/>'. 111 '</form></html>'; 112 return $my_page; 113 } 114 115 function apply_raw_darcs_patch($patch,$repo,$test_only=false,$reverse=false) { 116 $plus = ($reverse ? '-' : '+'); 117 $minus = ($reverse ? '+' : '-'); 118 $return_string = ''; 119 chdir($repo); 120 if(($contents = file_get_contents($patch)) !== false) { 121 $contents = str_replace("\r",'',$contents); 122 $sections = array(); 123 if(preg_match_all('/(^\[.*?\])\s*\{(.*?)^\}/ms',$contents,$sections) > 0) { 124 if($reverse) $sections[1] = array_reverse($sections[1]); 125 for($c=0;$c<count($sections[1]);$c++) { 126 $section = $sections[2][$c]; 127 $return_string .= ' '.str_replace("\n","\n ",$sections[1][$c])."\n"; 128 $error = false; 129 $hunks = preg_split('/^hunk\s/ms',$section); 130 array_shift($hunks); // the first will be empty 131 $files = array(); 132 if($reverse) $hunks = array_reverse($hunks); 133 foreach($hunks as $hunk) { 134 $lines = explode("\n",$hunk); 135 if($line = trim(array_shift($lines))) { 136 if($pos = strrpos($line,' ')) { 137 $fname = substr($line,0,$pos); 138 $offset = intval(substr($line,$pos+1)); 139 $fname = realpath(dirname($fname)).'/'.basename($fname); 140 if (isset($files[$fname])) { 141 $target_lines = explode("\n",$files[$fname]); 142 for($i=0;$i<count($target_lines);$i++) { 143 $target_lines[$i] .= "\n"; 144 } 145 } 146 if(($offset > 0) && (isset($files[$fname]) || (($target_lines = @file($fname)) !== false))) { 147 foreach($lines as $line) { 148 if($line[0] == $minus) { 149 if(($offset <= count($target_lines)) && (strcmp(rtrim(substr($line,1)),rtrim($target_lines[$offset-1])) == 0)) { 150 array_splice($target_lines,$offset-1,1); 151 } else { 152 $return_string .= ' '.$this->lang['msg_errmatch'].': '.$fname.' line '.$offset."\n"; 153 $return_string .= ' '.rtrim(substr($line,1))."\n"; 154 $return_string .= ' '.rtrim($target_lines[$offset-1])."\n"; 155 $error = true; 156 break; 157 } 158 } 159 elseif($line[0] == $plus) { 160 if(($offset-1) <= count($target_lines)) { 161 array_splice($target_lines,$offset-1,0,substr($line,1)."\n"); 162 $offset++; 163 } else { 164 $return_string .= ' '.$this->lang['msg_errmatch'].': '.$fname.' line '.$offset."\n"; 165 $error = true; 166 break; 167 } 168 } elseif(trim($line) == '') { 169 } else { 170 $error = true; 171 $return_string .= ' '.$this->lang['msg_errline'].': '.$line."\n"; 172 } 173 if($error) break; 174 } 175 $files[$fname] = implode('',$target_lines); 176 } else { 177 $error = true; 178 $return_string .= ' '.$this->lang['msg_erropen'].': '.$fname."\n"; 179 } 180 } 181 } 182 if($error) break; 183 } 184 if(!$error) { 185 foreach($files as $fname => $contents) { 186 if($test_only) 187 $return_string .= ' '.$this->lang['msg_test'].': '.$fname."\n"; 188 elseif(io_saveFile($fname,$contents)) 189 $return_string .= ' '.$this->lang['msg_success'].': '.$fname."\n"; 190 else 191 $return_string .= ' '.$this->lang['msg_errwrite'].': '.$fname."\n"; 192 } 193 } 194 $return_string .= "\n\n"; 195 } 196 } 197 } 198 return $return_string; 199 } 200} 201