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