1<?php
2/**
3* Plugin Chain: Execute a sys command and create output file in a current directory for a next chain command.
4*
5* @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6* @author     Riccardo Pisanu <rkpisanu@yahoo.it>
7* based on the format plugin
8*
9* <chain alias=program_alias output=file_ouput render=[yes/no] >
10* text to feed into the program
11* </chain>
12*
13* program_alias is defined in conf/default.php similat to:
14* $conf['<program-name>']=array('name'           => "<user name>",
15*             '<mode>' => array('iext'           => "<input  extension>",
16				                'oext'           => "<output extension>",
17*                               'pre'            => "<pre>",
18*                               'post'           => "<post>",
19                                'command_wintel' => "<wintel command line>"
20*                               'command'        => "<non wintel command line>" ));
21*
22*
23**/
24
25// must be run within DokuWiki
26if(!defined('DOKU_INC')) die();
27
28if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
29require_once DOKU_PLUGIN.'syntax.php';
30
31
32/**
33* All DokuWiki plugins to extend the parser/rendering mechanism
34* need to inherit from this class
35*/
36class syntax_plugin_chain extends DokuWiki_Syntax_Plugin
37  {
38
39  function getInfo()
40    {
41    return array('author' => 'Riccardo Pisanu',
42                 'email'  => 'rkpisanu@yahoo.it',
43                 'date'   => '2010-01-16',
44                 'name'   => 'Chain Plugin',
45                 'desc'   => 'Execute a sys command and create output file in a current directory for a next chain command.',
46                 'url'    => 'http://www.dokuwiki.org/plugin:chain');
47    }
48
49  function getType()
50    {
51    return 'substition';
52    }
53
54  function getSort()
55    {
56    return 32;
57    }
58
59  function connectTo($mode)
60    {
61    $this->Lexer->addEntryPattern('<chain(?=.*?>.*?</chain>)',$mode,'plugin_chain');
62    }
63
64  function postConnect()
65    {
66    $this->Lexer->addExitPattern('</chain>','plugin_chain');
67    }
68
69  function handle($match, $state, $pos, &$handler)
70    {
71    return array($match, $state, $pos);
72    }
73
74  function render($mode, &$renderer, $data)
75    {
76	//Query not have time limit
77	set_time_limit(0);
78	//No cache pages
79	$renderer->info['cache'] = false;
80    //Refer to global Dokuwiki vars to get directory configuration and namespace:page
81    global $conf;
82    global $ID;
83    //Only xhtml
84    if ($mode!='xhtml') return;
85    //Split data received from handle function
86    @list($match, $state, $pos) = $data;
87    switch ($state)
88      {
89      case DOKU_LEXER_ENTER:
90        break;
91      case DOKU_LEXER_UNMATCHED:
92        //** Split $match in Parameters $matches[0] and Text $matches[1] **
93        $matches = preg_split('/>/u',$match,2);
94        $matches[0] = trim($matches[0]);
95        if ( trim($matches[0]) == '' )
96          {
97	  $matches[0] = NULL;
98          }
99        //** Parsing Parameters **
100        preg_match('/alias=([a-zA-Z_0-9]+)/i',  $matches[0], $match_alias);
101        preg_match('/output=([a-zA-Z_0-9]+)/i', $matches[0], $match_output);
102        preg_match('/render=([a-zA-Z_0-9]+)/i', $matches[0], $match_render);
103        preg_match('/delim=([a-zA-Z_0-9]+)/i',  $matches[0], $match_delim);
104        preg_match('/debug=([a-zA-Z_0-9]+)/i',  $matches[0], $match_debug);
105		//Manage Delimitator
106		switch (trim($match_delim[1]))
107          {
108          case 'tab'  : $match_delim[1] = "\t";     break;
109          case 'mtab' : $match_delim[1] = "[ \t]+"; break;
110          case 'pipe' : $match_delim[1] = "\|";     break;
111          case 'scol' : $match_delim[1] = ";";      break;
112		  default     : $match_delim[1] = ",";
113		  }
114		  //** Begin Application Logic Plugin **
115        //Get alias configuration array conf[$match_alias[1]] in conf/default.php and exit with error message if it not well configured
116        $config = $this->getConf($match_alias[1]);
117        if(!$config || !$config[$mode])
118          {
119		  //_xmlEntities encode html correctly
120  	      $renderer->doc .= $renderer->_xmlEntities($match_alias[1].' ** <chain alias=AliasName > not configured for '.$mode.' output in /lib/plugins/chain/default.php **');
121	      return true;
122          }
123        //Create/Verify Directory to store files
124        $ns_path = $ID;
125        $ns_path = str_replace(":","/",$ns_path); // Work out the namespace and page
126        $dirname = $conf['mediadir'].'/temp/chain'.$program.'/'.$ns_path;
127		//Set relative url_base for image path rendering
128		list($drive_path,$url_base) = split(DOKU_BASE,$dirname);
129        $url_base = DOKU_BASE.$url_base;
130        //If directory not exist then create it and grant permission with .htaccess
131        if(!is_dir($dirname))
132          {
133		  io_mkdir_p($dirname); //Using dokuwiki framework
134		  }
135        //Output Filename
136        $OutFileName = $dirname.'/'.$match_output[1].'.'.$config[$mode]['oext'];
137		$RelFileName = $url_base.'/'.$match_output[1].'.'.$config[$mode]['oext'];
138        //Delete old OutFileName if exist
139        if (is_file($OutFileName)) unlink($OutFileName);
140        //Input Filename with contain all data wrapped pre + text + post
141        $InpFileName = $dirname.'/'.$match_output[1].'.'.$config[$mode]['iext'];
142		$NSPath = $dirname.'/';
143		if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') $NSPath=str_replace('/', '\\', $NSPath);
144        $pre = str_replace(array('@InputFile@','@OutputFile@','@Path@'),
145		           array($InpFileName, $OutFileName, $NSPath),
146		           $config[$mode]['pre']);
147        $post = str_replace(array('@InputFile@','@OutputFile@','@Path@'),
148			    array($InpFileName, $OutFileName, $NSPath),
149		            $config[$mode]['post']);
150        $text = str_replace(array('@InputFile@','@OutputFile@','@Path@'),
151			    array($InpFileName, $OutFileName, $NSPath),
152		            $matches[1]);
153        $wrappeddata = $pre.$text.$post;
154        //Save File Using Dokuwiki Framework
155        io_saveFile($InpFileName, $wrappeddata);
156        // Replace the variable strings
157		if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
158          {
159          //This is a command using Windows!
160          $command=str_replace(array('@InputFile@','@OutputFile@','@Path@'),
161			       array($InpFileName, $OutFileName, $NSPath),
162			       $config[$mode]['command_wintel']);
163		  $command=str_replace('/', '\\', $command);
164		  $command=str_replace('Program Files','progra~1', $command);
165          }
166          else
167          {
168          //This is a command not using Windows!
169          $command=str_replace(array('@InputFile@','@OutputFile@','@Path@'),
170			       array($InpFileName, $OutFileName, $NSPath),
171			       $config[$mode]['command']);
172		  }
173        $output = shell_exec($command);
174        //** Trace vars for debug **
175        if ( trim($match_debug[1]) == 'yes' )
176          {
177		  $DebProgs=1;
178		  $this->DebugWindowsOpen();
179		  $this->DebugWindowsWrite("</table>\n<table border=1 >");
180		  $this->DebugWindowsWrite("<TH colspan=2 >CHAIN DEBUG CONSOLE");
181		  $this->DebugWindowsWrite("<TD colspan=2 > Begin Trace Chain Plugin: ".date('l jS \of F Y h:i:s A'));
182		  $this->DebugWindowsWrite("<TD>".$DebProgs++.") ns_path =<TD>-->$ns_path");
183		  $this->DebugWindowsWrite("<TD>".$DebProgs++.") dirname =<TD>-->$dirname");
184		  $this->DebugWindowsWrite("<TD>".$DebProgs++.") DOKU_BASE =<TD>-->".DOKU_BASE);
185		  $this->DebugWindowsWrite("<TD>".$DebProgs++.") url_base =<TD>-->$url_base");
186          $this->DebugWindowsWrite("<TD>".$DebProgs++.") The pre text is:<TD>-->$pre");
187          $this->DebugWindowsWrite("<TD>".$DebProgs++.") The input text is:<TD>-->$matches[1]");
188          $this->DebugWindowsWrite("<TD>".$DebProgs++.") The post text is:<TD>-->$post");
189          $this->DebugWindowsWrite("<TD>".$DebProgs++.") Parameters:<TD>-->$matches[0]");
190          $this->DebugWindowsWrite("<TD>".$DebProgs++.") alias =<TD>-->$match_alias[1]");
191          $this->DebugWindowsWrite("<TD>".$DebProgs++.") output =<TD>-->$match_output[1]");
192          $this->DebugWindowsWrite("<TD>".$DebProgs++.") render =<TD>-->$match_render[1]");
193          $this->DebugWindowsWrite("<TD>".$DebProgs++.") delim =<TD>-->$match_delim[1]");
194		  $this->DebugWindowsWrite("<TD>".$DebProgs++.") NSPath =<TD>-->$NSPath");
195          $this->DebugWindowsWrite("<TD>".$DebProgs++.") The input file is:<TD>-->$InpFileName");
196          $this->DebugWindowsWrite("<TD>".$DebProgs++.") The output file is:<TD>-->$OutFileName");
197          $this->DebugWindowsWrite("<TD>".$DebProgs++.") The command is:<TD>-->$command");
198          $this->DebugWindowsWrite("<TD>".$DebProgs++.") The output is:<TD>-->$output");
199          $this->DebugWindowsWrite("<TD colspan=2 >End Trace Chain Plugin: ".date('l jS \of F Y h:i:s A'));
200          }
201        //** Rendering **
202        //Save Always File Output for next chain command except png,tbl files
203        if ($config[$mode]['oext'] !== 'png' && $config[$mode]['oext'] !== 'tbl' ) io_saveFile($OutFileName, $output);
204        //if render=yes then render it
205        if ( trim($match_render[1]) == 'yes' )
206          {
207          switch ($config[$mode]['oext'])
208            {
209            case 'out' :
210              $renderer->doc .= "<pre>$output</pre>";
211              break;
212
213            case 'csv' :
214			  if ( trim($match_debug[1]) == 'yes' )
215			    {
216                $renderer->doc .= "<pre>$output</pre>";
217				}
218			  //Convert csv to html table
219			  //Clean sqlplus standard output string session
220              $output = preg_replace("/Session altered\./","",$output);
221			  if (trim($match_delim[1]) == "[ \t]+") //mtab
222			    {
223			    //Clean sqlplus standard output string session
224                $output = preg_replace("/[0-9]+ rows selected\./","",$output);
225                $output = preg_replace("/[-]+\s/","",$output);
226                }
227              //$renderer->doc .= "<pre>$output</pre>";
228			  //Clean any trailing or leading empty lines from the data set
229              $output = preg_replace("/[\r\n]*$/","",$output);
230              $output = preg_replace("/^\s*[\r\n]*/","",$output);
231			  //Replace EOL for all O.S.
232			  $output = preg_replace("/(?<!\\n)\\r+(?!\\n)/",    "\r\n", $output); //replace just CR with CRLF
233              $output = preg_replace("/(?<!\\r)\\n+(?!\\r)/",    "\r\n", $output); //replace just LF with CRLF
234              $output = preg_replace("/(?<!\\r)\\n\\r+(?!\\n)/", "\r\n", $output); //replace misordered LFCR with CRLF
235              //Split output in a array of lines
236	          //End of Line delimiter <end> is optional
237			  if (strpos($output, '<end>'))
238			    {
239			    $lines  = preg_split("/<end>\\r\\n/", $output);
240				}
241                else
242                {
243                $lines  = preg_split("/\\r\\n/", $output);
244                }
245			  //Loop for each line
246              $numlines = 0;
247			  $table ="";
248              foreach ($lines as $line)
249			    {
250				if ($numlines===0)
251				  {
252				  //First line is table header
253				  $line = "<tr><th>".$line."\r\n";
254				  $table .= preg_replace("/$match_delim[1]/","<th>",$line);
255				  }
256				  else
257				  {
258				  //Other lines are data
259				  $line = "<tr><td>".$line."\r\n";
260				  $table .= preg_replace("/$match_delim[1]/","<td>",$line);
261				  }
262                ++$numlines;
263                }
264              $renderer->doc .= "\r\n<table class='inline' >\r\n".$table."</table>";
265              break;
266
267			case 'tbl' :
268			  if ( trim($match_debug[1]) == 'yes' )
269			    {
270                $renderer->doc .= "<pre>$output</pre>";
271				}
272			  //Convert csv to html table
273              //$renderer->doc .= "<pre>$output</pre>";
274			  //Clean any trailing or leading empty lines from the data set
275              $output = preg_replace("/[\r\n]*$/","",$output);
276              $output = preg_replace("/^\s*[\r\n]*/","",$output);
277			  //Replace EOL for all O.S.
278			  $output = preg_replace("/(?<!\\n)\\r+(?!\\n)/",    "\r\n", $output); //replace just CR with CRLF
279              $output = preg_replace("/(?<!\\r)\\n+(?!\\r)/",    "\r\n", $output); //replace just LF with CRLF
280              $output = preg_replace("/(?<!\\r)\\n\\r+(?!\\n)/", "\r\n", $output); //replace misordered LFCR with CRLF
281              //Split output in a array of lines
282	          $lines  = preg_split("/\\r\\n/", $output);
283			  //Loop for each line
284              $numlines = 0;
285			  $table ="";
286              foreach ($lines as $line)
287			    {
288				if ($numlines===0)
289				  {
290				  //First line is table header
291				  $line = "<tr><th>".$line."\r\n";
292				  $table .= preg_replace("/$match_delim[1]/","<th>",$line);
293				  }
294				  else
295				  {
296				  //Other lines are data
297				  $line = "<tr><td>".$line."\r\n";
298				  $line_temp = preg_replace("/$match_delim[1]/","<td>",$line);
299				  $table .= preg_replace("/<td><td>/","<td>",$line_temp);
300				  }
301                ++$numlines;
302                }
303              $renderer->doc .= "\r\n<table class='inline' >\r\n".$table."</table>";
304              break;
305
306            case 'png' :
307			  //To avoid browser cache insert random number in a filename
308			  $random=rand();
309			  $OutFileNameRnd =$dirname.'/'.'tempchain_'.$random.'_'.$match_output[1].'.'.$config[$mode]['oext'];
310              //Copy file
311              copy($OutFileName, $OutFileNameRnd);
312              //Render a file png
313			  $OutFileNamePng = 'temp/chain'.$program.'/'.$ns_path.'/'.'tempchain_'.$random.'_'.$match_output[1].'.'.$config[$mode]['oext'];
314              $OutFileNamePng = str_replace("/",":",$OutFileNamePng); // Work out the namespace and page
315			  $renderer->doc .= '<img src='.DOKU_BASE.'lib/exe/fetch.php?media='.$OutFileNamePng.' alt="Png Image Chain Plugin Error" />';
316			  break;
317            }
318          }
319        break;
320      case DOKU_LEXER_EXIT:
321        break;
322      }
323    return true;
324    }
325
326
327  function DebugWindowsOpen()
328    {
329    $GLOBALS[status_window_open] = 1;
330    ?>
331    <script language="JavaScript">
332    <!--
333    var winOpts = "width=400,height=250,scrollbars,resizable";
334    sw = window.open("", "Status", winOpts);
335    if (sw.document == null)
336	  {
337      sw = window.open("", "Status", winOpts);
338      }
339    sw.document.open();
340    sw.bgcolor = "white";
341    </script>
342    <?php
343    }
344
345  function DebugWindowsWrite($s)
346    {
347    if (!$GLOBALS[status_window_open])
348    return;
349    ?>
350    <script>
351    <!--
352    if (!sw.closed) { sw.document.write("<?php $s=str_replace(array("\n","\\"), array("<BR>","\\\\"), $s); echo "<TR>" . $s; ?>"); }
353    </script>
354    <?php
355    flush();
356    }
357
358  function DebugWindowsClose()
359    {
360    if (!$GLOBALS[status_window_open])
361    return;
362
363    $GLOBALS[status_window_open] = 0;
364    ?>
365    <script language="JavaScript">
366    <!--
367    if (!sw.closed) { sw.close(); }
368    </script>
369    <?php
370    }
371
372
373  }
374
375?>
376