1<?php 2if(!defined('DOKU_INC')) define('DOKU_INC',realpath(dirname(__FILE__).'/../../../').'/'); 3if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); 4if(!defined('DOKU_MEDIA')) define('DOKU_MEDIA',DOKU_INC.'data/media/'); 5define ('BROKEN_IMAGE', DOKU_URL . 'lib/plugins/ckgdoku/fckeditor/userfiles/blink.jpg?nolink&33x34'); 6require_once(DOKU_PLUGIN.'action.php'); 7define('FCK_ACTION_SUBDIR', realpath(dirname(__FILE__)) . '/'); 8/** 9 * @license GPL 2 (http://www.gnu.org/licenses/gpl.html) 10 */ 11 12class action_plugin_ckgdoku_save extends DokuWiki_Action_Plugin { 13 14 function register(Doku_Event_Handler $controller) { 15 16 $controller->register_hook('DOKUWIKI_STARTED', 'BEFORE', $this, 'ckgdoku_save_preprocess'); 17 } 18 19 function ckgdoku_save_preprocess(Doku_Event $event){ 20 global $ACT; 21 if (!isset($_REQUEST['ckgdoku']) || ! is_array($ACT) || !(isset($ACT['save']) || isset($ACT['preview']))) return; 22 if (isset($_REQUEST["fontdel"]) ) { 23 msg($this->getLang("fontdel"),1); 24 } 25 if (isset($_REQUEST["formatdel"]) ) { 26 msg($this->getLang("formatdel"),1); 27 } 28 29 global $TEXT, $conf; 30 if (!$TEXT) return; 31 $preserve_enc = $this->getConf('preserve_enc'); 32 $deaccent = $conf['deaccent'] == 0 ? false : true; 33 $TEXT = $_REQUEST['fck_wikitext']; 34 35 if(!preg_match('/^\s+(\-|\*)/',$TEXT)) { 36 $TEXT = trim($TEXT); 37 } 38 39 40 $TEXT = preg_replace_callback( 41 '|\{\{data:(.*?);base64|ms', 42 create_function( 43 '$matches', 44 'if(!preg_match("/image/",$matches[1])) { 45 return "{{data:image/jpeg;base64"; 46 } 47 return $matches[0];' 48 ),$TEXT); 49 50 if(strpos($TEXT,'data:image') !== false) { 51 $TEXT = preg_replace_callback( 52 '|\{\{(\s*)data:image\/(\w+;base64,\s*)(.*?)\?nolink&(\s*)\}\}|ms', 53 create_function( 54 '$matches', 55 'list($ext,$base) = explode(";",$matches[2]); 56 if($ext == "jpeg" || $ext == "tiff") $ext = "jpg"; 57 if(function_exists("imagecreatefromstring") && !imagecreatefromstring (base64_decode($matches[3]))) { 58 msg("Clipboard paste: invalid $ext image format"); 59 return "{{" . BROKEN_IMAGE . "}}"; 60 } 61 global $INFO,$conf; 62 $ns = getNS($INFO["id"]); 63 $ns = trim($ns); 64 if(!empty($ns)) { 65 $ns = ":$ns:"; 66 $dir = str_replace(":","/",$ns); 67 } 68 else { // root namespace 69 $dir = "/"; 70 $ns = ":"; 71 } 72 $fn = md5($matches[3]) . ".$ext"; 73 $path = $conf["mediadir"] . $dir . $fn; 74 @io_makeFileDir($path); 75 if(!file_exists($path)) { 76 @file_put_contents($path, base64_decode($matches[3])); 77 } 78 else { 79 msg("file for this image previousely saved",2); 80 } 81 $left = "{{"; 82 $right = "}}"; 83 if($matches[1]) $left .= $matches[1]; 84 if($matches[4]) $right = $matches[4] . $right; 85 86 $retv = "$left" . $ns. $fn . "$right"; 87 return $retv;' 88 ), 89 $TEXT 90 ); 91 } 92 $TEXT = str_replace('%%', "FCKGPERCENTESC", $TEXT); 93 94 if($deaccent || $preserve_enc) { 95 $TEXT = preg_replace_callback('/^(.*?)(\[\[.*?\]\])*(.*?)$/ms', 96 create_function( 97 '$matches', 98 '$matches[1] = preg_replace("/%([A-F0-9]{1,3})/i", "URLENC_PERCENT$1", $matches[1]); 99 $matches[2] = preg_replace("/%([A-F0-9]{1,3})/i", "URLENC_PERCENT$1", $matches[2]); 100 $matches[3] = preg_replace("/%([A-F0-9]{1,3})/i", "URLENC_PERCENT$1", $matches[3]); 101 return $matches[1].$matches[2].$matches[3];' 102 ), 103 $TEXT 104 ); 105 } 106 107 $TEXT = rawurldecode($TEXT); 108 $TEXT = preg_replace('/NOWIKI_%_NOWIKI_%_/', '%%',$TEXT); 109 $TEXT = preg_replace('/URLENC_PERCENT/', '%',$TEXT); 110 $TEXT = preg_replace('/NOWIKI_(.)_/', '$1',$TEXT); 111 112 /* preserve newlines in code blocks */ 113 $TEXT = preg_replace_callback( 114 '/(<code>|<file>)(.*?)(<\/code>|<\/file>)/ms', 115 create_function( 116 '$matches', 117 'return str_replace("\n", "__code_NL__",$matches[0]);' 118 ), 119 $TEXT 120 ); 121 122 $TEXT = preg_replace('/^\s*[\r\n]$/ms',"__n__", $TEXT); 123 $TEXT = preg_replace('/oIWIKIo|cIWIKIc/ms',"", $TEXT); 124 $TEXT = preg_replace('/\r/ms',"", $TEXT); 125 $TEXT = preg_replace('/^\s+(?=\^|\|)/ms',"", $TEXT); 126 $TEXT = preg_replace('/__n__/',"\n", $TEXT); 127 $TEXT = str_replace("__code_NL__","\n", $TEXT); 128 $TEXT = str_replace("FCKGPERCENTESC", '%%', $TEXT); 129 if($this->getConf('complex_tables')) { 130 $TEXT = str_replace('~~COMPLEX_TABLES~~','',$TEXT); 131 } 132 $TEXT .= "\n"; 133 // Removes relics of markup characters left over after acronym markup has been removed 134 //$TEXT = preg_replace('/([\*\/_]{2})\s+\\1\s*([A-Z]+)\s*\\1+/ms',"$2",$TEXT); 135 136 $pos = strpos($TEXT, 'MULTI_PLUGIN_OPEN'); 137 if($pos !== false) { 138 $TEXT = preg_replace_callback( 139 '|MULTI_PLUGIN_OPEN.*?MULTI_PLUGIN_CLOSE|ms', 140 create_function( 141 '$matches', 142 'return preg_replace("/\\\\\\\\/ms","\n",$matches[0]);' 143 ), 144 $TEXT 145 ); 146 147 $TEXT = preg_replace_callback( 148 '|MULTI_PLUGIN_OPEN.*?MULTI_PLUGIN_CLOSE|ms', 149 create_function( 150 '$matches', 151 'return preg_replace("/^\s+/ms","",$matches[0]);' 152 ), 153 $TEXT 154 ); 155 $TEXT = str_replace("~~MULTI_PLUGIN_OPEN~~","~~MULTI_PLUGIN_OPEN~~\n",$TEXT); 156 } 157 158 if(strpos($TEXT,'L_PARgr') !== false) { 159 $TEXT = preg_replace_callback( 160 '|\(\((.*?)\)\)|ms', 161 create_function( 162 '$matches', 163 'return "((" . trim($matches[1]) . "))"; ' 164 ), 165 $TEXT 166 ); 167 $TEXT = str_replace('L_PARgr', '(',$TEXT); 168 $TEXT = str_replace('R_PARgr', ')',$TEXT); 169 } 170 171 $this->replace_entities(); 172 /*Remove urls from linkonly images inserted after second and additional saves, resulting in multiple urls corrupting HTML output */ 173 $TEXT = preg_replace("/\{\{http:\/\/.*?fetch.php\?media=(.*?linkonly.*?)\}\}/",'{{' . "$1" .'}}',$TEXT); 174 $TEXT = str_replace('< nowiki >', '%%<nowiki>%%',$TEXT); 175 176 177 $TEXT = preg_replace_callback( 178 '#\[\[(.*?)\]\]#ms', 179 function($matches){ 180 global $ID, $conf; 181 $qs = ""; 182 if(preg_match("/\[\[http/",$matches[0])) return $matches[0]; //not an internal link 183 if(preg_match("#\[\[.*?\|\{\{.*?\}\}\]\]#", $matches[0],$matches_1)) { 184 if(!$this->getConf('rel_links')) { 185 return $matches[0]; 186 } 187 $link = explode('?',$matches[1]); 188 list($link_id,$linktext) = explode('|', $link[0]); 189 $current_id = $this->abs2rel($link_id,$ID); 190 return preg_replace("#$link_id#",$current_id, $matches[0]); 191 } 192 193 $link = explode('?',$matches[1]); 194 list($link_id,$linktext) = explode('|', $link[0]); 195 if($this->getConf('rel_links')) 196 $current_id = $this->abs2rel($link_id,$ID); 197 else $current_id = $link_id; 198 //like in _getLinkTitle in xhtml.php 199 if(useHeading('content')) { 200 $tmp_linktext = p_get_first_heading($link_id); 201 if(trim($linktext) == trim($tmp_linktext)) { 202 $linktext = ""; 203 } 204 } 205 $tmp_ar = explode(':',$link_id); 206 $tmp_id = array_pop($tmp_ar); 207 if(trim($linktext,'.: ' ) == trim($tmp_id,'.: ')) $linktext = ""; 208 209 $current_id = $current_id.'|'.$linktext; 210 return '[[' . $current_id .']]'; 211 }, 212 $TEXT 213 ); 214 215 if($this->getConf('rel_links')) { 216 $TEXT = preg_replace_callback( 217 '#\{\{(\s*)(.*?)(\s*)\}\}#ms', 218 function($matches) { 219 global $ID; 220 $link = explode('?',$matches[2]); 221 list($link_id,$linktext) = explode('|', $link[0]); 222 $rel = $this->abs2rel($link_id,$ID); 223 if(!empty($link[1])) $rel .= '?' . $link[1]; 224 if(!empty($linktext)) $rel = $rel.'|'.$linktext; 225 return '{{' .$matches[1] . $rel . $matches[3] .'}}'; 226 }, 227 $TEXT 228 ); 229 } 230 231/* 11 Dec 2013 see comment below 232Remove discarded font syntax 233*/ 234 $TEXT = preg_replace_callback( 235 '|_REMOVE_FONTS_START_(.*?)_REMOVE_FONTS_END_|ms', 236 create_function( 237 '$matches', 238 '$matches[1] = preg_replace("/<font.*?>/ms","",$matches[1]); 239 return preg_replace("/<\/font>/ms","",$matches[1]);' 240 ), 241 $TEXT 242 ); 243 244 /* 2456 April 2013 246Removed newlines and spaces from beginnings and ends of text enclosed by font tags. Too subtle for javascript. 247 */ 248 $TEXT = preg_replace_callback( 249 '|(<font.*?>)(.*?)(?=</font>)|ms', 250 create_function( 251 '$matches', 252 '$matches[2]=preg_replace("/^\s+/ms","",$matches[2]); 253 $matches[2]=preg_replace("/\s+$/ms","",$matches[2]); 254 return $matches[1]. $matches[2];' 255 ), 256 $TEXT 257 ); 258 $TEXT = preg_replace('/__QUOTE__/ms',">",$TEXT); 259 $TEXT = preg_replace('/[\t\x20]+$/ms',"",$TEXT); 260 $TEXT = preg_replace('/\n{4,}/ms',"\n\n",$TEXT); 261 $TEXT = preg_replace('/\n{3,}/ms',"\n\n",$TEXT); 262 263 /*first pass for blockquotes*/ 264 $TEXT = preg_replace_callback( 265 "#^>+(.*?)\\\\\\\\#ms", 266 function($matches) { 267 return str_replace('\\',"",$matches[0]); 268 }, 269 $TEXT 270 ); 271 272 /* remove extra line-feeds following in-table code blocks 273 make sure cell-ending pipe not mistaken for a following link divider 274 */ 275 $TEXT = preg_replace_callback( 276 '#(/code|/file)\>.*?\n\|#ms', 277 function($matches) { 278 $matches[0] = preg_replace("/([\S\s\w\:])\\\\\\\\(\w)/ms","$1@#@$2",$matches[0]); //retain backslashes inside code blocks 279 $matches[0] = preg_replace("/(\w+)\\\\\\\\(\w)/ms","$1@#@",$matches[0]); 280 $matches[0] = preg_replace("/\\\\(\w+)/ms","@!@$1",$matches[0]); 281 $matches[0] = preg_replace("/(\w+)\\\\/ms","@!@$1",$matches[0]); 282 $matches[0] = str_replace("\\", "",$matches[0]); 283 $matches[0] = str_replace("@!@",'\\',$matches[0]); 284 return str_replace("@#@", "\\\\",$matches[0]); 285 }, 286 $TEXT 287 ); 288 289 290 /* reformat table cell after removing extra line-feeds, above */ 291 $TEXT = preg_replace_callback( 292 '#\|[\s\n]+(\<file.*?\>)(.*?)(\<\/file>\s*.*?)\n?\|#ms', 293 function($matches) { 294 $matches[3] = preg_replace('/\n+/',"",$matches[3] ); 295 $matches[3] = preg_replace('/\s+$/',"",$matches[3] ) . '|'; 296 return '|' . $matches[1] . $matches[2] . str_replace("\\ ","",$matches[3]); 297 }, 298 $TEXT 299 ); 300 301 return; 302 303 } 304 305 306function replace_entities() { 307global $TEXT; 308global $ents; 309 $serialized = FCK_ACTION_SUBDIR . 'ent.ser'; 310 $ents = unserialize(file_get_contents($serialized)); 311 312 $TEXT = preg_replace_callback( 313 '|(&(\w+);)|', 314 create_function( 315 '$matches', 316 'global $ents; return $ents[$matches[2]];' 317 ), 318 $TEXT 319 ); 320 321} 322 323 324function write_debug($data) { 325return; 326 if (!$handle = fopen('save.txt', 'a')) { 327 return; 328 } 329 330 // Write $somecontent to our opened file. 331 fwrite($handle, "save.php: $data\n"); 332 fclose($handle); 333 334} 335/* @auth Sergey Kotov */ 336//linkPath is the link in the page 337//pagePath is absolute path of the page (ns1:ns2:....:page or :ns1:ns2:....:page) 338function abs2rel($linkPath,$pagePath){ 339 if ($linkPath[0]==='.'){ 340 // It's already relative 341 return $linkPath; 342 } 343 $aLink=explode(':',$linkPath); 344 $nLink=count($aLink); 345 if ($nLink<2){ 346 return $linkPath; 347 } 348 $aPage=explode(':',$pagePath); 349 if(empty($aLink[0])) { 350 // If linkPath is started by ':' 351 // Make canonical absolute path ns1:ns2:.....:pageLink (strip leading :) 352 array_shift($aLink); 353 if (--$nLink<2) { 354 return $linkPath; 355 } 356 } 357 358 if(empty($aPage[0])) { 359 // If pagePath is started by ':' 360 // Make canonical absolute path ns1:ns2:.....:page (strip leading :) 361 array_shift($aPage); 362 } 363 $nPage=count($aPage); 364 $nslEqual=0; // count of equal namespaces from left to right 365 // Minimal length of these two arrays, page name is not included 366 $nMin=($nLink<$nPage ? $nLink : $nPage)-1 ; 367 for ($i=0;$i<$nMin;++$i){ 368 if ($aLink[$i]===$aPage[$i]){ 369 ++$nslEqual; 370 } 371 else { 372 break; 373 } 374 } 375 if ($nslEqual==0){ 376 // Link and page from different root namespaces 377 return $linkPath; 378 } 379 // Truncate equal lef namespaces 380 $aPageDiff=array_slice($aPage,$nslEqual); 381 $nPageDiff=count($aPageDiff); 382 $aLinkDiff=array_slice($aLink,$nslEqual); 383 384 // Now we have to go up to nPageDiff-1 levels 385 $aResult=array(); 386 if ($nPageDiff>1){ 387 $aResult=array_fill(0,$nPageDiff-1,'..'); 388 } 389 else if($nPageDiff == 1) { 390 $aResult[] = '.'; 391 } 392 $aResult=array_merge($aResult,$aLinkDiff); 393 return implode(':', $aResult); 394} 395 396 397} //end of action class 398 399?> 400