<?php
/**
* Plugin Chain: Execute a sys command and create output file in a current directory for a next chain command.
* 
* @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
* @author     Riccardo Pisanu <rkpisanu@yahoo.it>
* based on the format plugin
*
* <chain alias=program_alias output=file_ouput render=[yes/no] > 
* text to feed into the program
* </chain>
*
* program_alias is defined in conf/default.php similat to:
* $conf['<program-name>']=array('name'           => "<user name>",
*             '<mode>' => array('iext'           => "<input  extension>",
				                'oext'           => "<output extension>",
*                               'pre'            => "<pre>",
*                               'post'           => "<post>",
                                'command_wintel' => "<wintel command line>"
*                               'command'        => "<non wintel command line>" ));
* 
*
**/
     
// must be run within DokuWiki
if(!defined('DOKU_INC')) die();
    
if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
require_once DOKU_PLUGIN.'syntax.php';

     
/**
* All DokuWiki plugins to extend the parser/rendering mechanism
* need to inherit from this class
*/
class syntax_plugin_chain extends DokuWiki_Syntax_Plugin 
  {
     
  function getInfo() 
    {
    return array('author' => 'Riccardo Pisanu',
                 'email'  => 'rkpisanu@yahoo.it',
                 'date'   => '2010-01-16',
                 'name'   => 'Chain Plugin',
                 'desc'   => 'Execute a sys command and create output file in a current directory for a next chain command.',
                 'url'    => 'http://www.dokuwiki.org/plugin:chain');
    }
     
  function getType() 
    { 
    return 'substition'; 
    }
        
  function getSort() 
    { 
    return 32; 
    }

  function connectTo($mode) 
    {
    $this->Lexer->addEntryPattern('<chain(?=.*?>.*?</chain>)',$mode,'plugin_chain');
    }

  function postConnect()
    {
    $this->Lexer->addExitPattern('</chain>','plugin_chain');
    }

  function handle($match, $state, $pos, &$handler) 
    {
    return array($match, $state, $pos);
    }

  function render($mode, &$renderer, $data) 
    {
	//Query not have time limit
	set_time_limit(0);
	//No cache pages
	$renderer->info['cache'] = false;
    //Refer to global Dokuwiki vars to get directory configuration and namespace:page
    global $conf;
    global $ID;
    //Only xhtml
    if ($mode!='xhtml') return;
    //Split data received from handle function
    @list($match, $state, $pos) = $data;
    switch ($state) 
      {
      case DOKU_LEXER_ENTER:
        break;
      case DOKU_LEXER_UNMATCHED:
        //** Split $match in Parameters $matches[0] and Text $matches[1] ** 
        $matches = preg_split('/>/u',$match,2);
        $matches[0] = trim($matches[0]);
        if ( trim($matches[0]) == '' ) 
          {
	  $matches[0] = NULL;
          }
        //** Parsing Parameters **
        preg_match('/alias=([a-zA-Z_0-9]+)/i',  $matches[0], $match_alias);
        preg_match('/output=([a-zA-Z_0-9]+)/i', $matches[0], $match_output);
        preg_match('/render=([a-zA-Z_0-9]+)/i', $matches[0], $match_render);
        preg_match('/delim=([a-zA-Z_0-9]+)/i',  $matches[0], $match_delim);
        preg_match('/debug=([a-zA-Z_0-9]+)/i',  $matches[0], $match_debug);
		//Manage Delimitator
		switch (trim($match_delim[1])) 
          {
          case 'tab'  : $match_delim[1] = "\t";     break;
          case 'mtab' : $match_delim[1] = "[ \t]+"; break;
          case 'pipe' : $match_delim[1] = "\|";     break;
          case 'scol' : $match_delim[1] = ";";      break;
		  default     : $match_delim[1] = ",";
		  } 
		  //** Begin Application Logic Plugin **
        //Get alias configuration array conf[$match_alias[1]] in conf/default.php and exit with error message if it not well configured
        $config = $this->getConf($match_alias[1]);
        if(!$config || !$config[$mode]) 
          {
		  //_xmlEntities encode html correctly 
  	      $renderer->doc .= $renderer->_xmlEntities($match_alias[1].' ** <chain alias=AliasName > not configured for '.$mode.' output in /lib/plugins/chain/default.php **');
	      return true;
          }
        //Create/Verify Directory to store files
        $ns_path = $ID;
        $ns_path = str_replace(":","/",$ns_path); // Work out the namespace and page
        $dirname = $conf['mediadir'].'/temp/chain'.$program.'/'.$ns_path;
		//Set relative url_base for image path rendering
		list($drive_path,$url_base) = split(DOKU_BASE,$dirname);
        $url_base = DOKU_BASE.$url_base;	
        //If directory not exist then create it and grant permission with .htaccess		
        if(!is_dir($dirname))
          {		
		  io_mkdir_p($dirname); //Using dokuwiki framework
		  }
        //Output Filename
        $OutFileName = $dirname.'/'.$match_output[1].'.'.$config[$mode]['oext'];
		$RelFileName = $url_base.'/'.$match_output[1].'.'.$config[$mode]['oext'];
        //Delete old OutFileName if exist
        if (is_file($OutFileName)) unlink($OutFileName);
        //Input Filename with contain all data wrapped pre + text + post
        $InpFileName = $dirname.'/'.$match_output[1].'.'.$config[$mode]['iext'];
		$NSPath = $dirname.'/';
		if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') $NSPath=str_replace('/', '\\', $NSPath);
        $pre = str_replace(array('@InputFile@','@OutputFile@','@Path@'),
		           array($InpFileName, $OutFileName, $NSPath),
		           $config[$mode]['pre']);
        $post = str_replace(array('@InputFile@','@OutputFile@','@Path@'),
			    array($InpFileName, $OutFileName, $NSPath),
		            $config[$mode]['post']);
        $text = str_replace(array('@InputFile@','@OutputFile@','@Path@'),
			    array($InpFileName, $OutFileName, $NSPath),
		            $matches[1]);					
        $wrappeddata = $pre.$text.$post;
        //Save File Using Dokuwiki Framework
        io_saveFile($InpFileName, $wrappeddata); 
        // Replace the variable strings
		if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') 
          {
          //This is a command using Windows!
          $command=str_replace(array('@InputFile@','@OutputFile@','@Path@'),
			       array($InpFileName, $OutFileName, $NSPath),
			       $config[$mode]['command_wintel']);
		  $command=str_replace('/', '\\', $command);
		  $command=str_replace('Program Files','progra~1', $command);
          } 
          else 
          {
          //This is a command not using Windows!
          $command=str_replace(array('@InputFile@','@OutputFile@','@Path@'),
			       array($InpFileName, $OutFileName, $NSPath),
			       $config[$mode]['command']);
		  }
        $output = shell_exec($command);
        //** Trace vars for debug **
        if ( trim($match_debug[1]) == 'yes' ) 
          {
		  $DebProgs=1;
		  $this->DebugWindowsOpen();
		  $this->DebugWindowsWrite("</table>\n<table border=1 >");
		  $this->DebugWindowsWrite("<TH colspan=2 >CHAIN DEBUG CONSOLE");
		  $this->DebugWindowsWrite("<TD colspan=2 > Begin Trace Chain Plugin: ".date('l jS \of F Y h:i:s A'));
		  $this->DebugWindowsWrite("<TD>".$DebProgs++.") ns_path =<TD>-->$ns_path");
		  $this->DebugWindowsWrite("<TD>".$DebProgs++.") dirname =<TD>-->$dirname");
		  $this->DebugWindowsWrite("<TD>".$DebProgs++.") DOKU_BASE =<TD>-->".DOKU_BASE);
		  $this->DebugWindowsWrite("<TD>".$DebProgs++.") url_base =<TD>-->$url_base");
          $this->DebugWindowsWrite("<TD>".$DebProgs++.") The pre text is:<TD>-->$pre");
          $this->DebugWindowsWrite("<TD>".$DebProgs++.") The input text is:<TD>-->$matches[1]");
          $this->DebugWindowsWrite("<TD>".$DebProgs++.") The post text is:<TD>-->$post");
          $this->DebugWindowsWrite("<TD>".$DebProgs++.") Parameters:<TD>-->$matches[0]");
          $this->DebugWindowsWrite("<TD>".$DebProgs++.") alias =<TD>-->$match_alias[1]");
          $this->DebugWindowsWrite("<TD>".$DebProgs++.") output =<TD>-->$match_output[1]"); 
          $this->DebugWindowsWrite("<TD>".$DebProgs++.") render =<TD>-->$match_render[1]"); 
          $this->DebugWindowsWrite("<TD>".$DebProgs++.") delim =<TD>-->$match_delim[1]"); 
		  $this->DebugWindowsWrite("<TD>".$DebProgs++.") NSPath =<TD>-->$NSPath");
          $this->DebugWindowsWrite("<TD>".$DebProgs++.") The input file is:<TD>-->$InpFileName");
          $this->DebugWindowsWrite("<TD>".$DebProgs++.") The output file is:<TD>-->$OutFileName");
          $this->DebugWindowsWrite("<TD>".$DebProgs++.") The command is:<TD>-->$command");
          $this->DebugWindowsWrite("<TD>".$DebProgs++.") The output is:<TD>-->$output");
          $this->DebugWindowsWrite("<TD colspan=2 >End Trace Chain Plugin: ".date('l jS \of F Y h:i:s A'));
          } 
        //** Rendering **
        //Save Always File Output for next chain command except png,tbl files
        if ($config[$mode]['oext'] !== 'png' && $config[$mode]['oext'] !== 'tbl' ) io_saveFile($OutFileName, $output); 
        //if render=yes then render it
        if ( trim($match_render[1]) == 'yes' ) 
          {
          switch ($config[$mode]['oext']) 
            {
            case 'out' :        
              $renderer->doc .= "<pre>$output</pre>";
              break;

            case 'csv' :
			  if ( trim($match_debug[1]) == 'yes' ) 
			    {
                $renderer->doc .= "<pre>$output</pre>";
				}
			  //Convert csv to html table
			  //Clean sqlplus standard output string session
              $output = preg_replace("/Session altered\./","",$output);
			  if (trim($match_delim[1]) == "[ \t]+") //mtab
			    {
			    //Clean sqlplus standard output string session		
                $output = preg_replace("/[0-9]+ rows selected\./","",$output);		
                $output = preg_replace("/[-]+\s/","",$output);
                }
              //$renderer->doc .= "<pre>$output</pre>";				
			  //Clean any trailing or leading empty lines from the data set
              $output = preg_replace("/[\r\n]*$/","",$output);
              $output = preg_replace("/^\s*[\r\n]*/","",$output);
			  //Replace EOL for all O.S.
			  $output = preg_replace("/(?<!\\n)\\r+(?!\\n)/",    "\r\n", $output); //replace just CR with CRLF
              $output = preg_replace("/(?<!\\r)\\n+(?!\\r)/",    "\r\n", $output); //replace just LF with CRLF
              $output = preg_replace("/(?<!\\r)\\n\\r+(?!\\n)/", "\r\n", $output); //replace misordered LFCR with CRLF
              //Split output in a array of lines
	          //End of Line delimiter <end> is optional	          
			  if (strpos($output, '<end>'))
			    {
			    $lines  = preg_split("/<end>\\r\\n/", $output);
				}
                else
                {
                $lines  = preg_split("/\\r\\n/", $output);	
                }				
			  //Loop for each line
              $numlines = 0;
			  $table ="";
              foreach ($lines as $line) 
			    {
				if ($numlines===0)
				  {
				  //First line is table header
				  $line = "<tr><th>".$line."\r\n";
				  $table .= preg_replace("/$match_delim[1]/","<th>",$line);
				  } 
				  else
				  {
				  //Other lines are data
				  $line = "<tr><td>".$line."\r\n";
				  $table .= preg_replace("/$match_delim[1]/","<td>",$line);				  
				  } 				  
                ++$numlines; 
                }						
              $renderer->doc .= "\r\n<table class='inline' >\r\n".$table."</table>";
              break;

			case 'tbl' :
			  if ( trim($match_debug[1]) == 'yes' ) 
			    {
                $renderer->doc .= "<pre>$output</pre>";
				}
			  //Convert csv to html table
              //$renderer->doc .= "<pre>$output</pre>";				
			  //Clean any trailing or leading empty lines from the data set
              $output = preg_replace("/[\r\n]*$/","",$output);
              $output = preg_replace("/^\s*[\r\n]*/","",$output);
			  //Replace EOL for all O.S.
			  $output = preg_replace("/(?<!\\n)\\r+(?!\\n)/",    "\r\n", $output); //replace just CR with CRLF
              $output = preg_replace("/(?<!\\r)\\n+(?!\\r)/",    "\r\n", $output); //replace just LF with CRLF
              $output = preg_replace("/(?<!\\r)\\n\\r+(?!\\n)/", "\r\n", $output); //replace misordered LFCR with CRLF
              //Split output in a array of lines
	          $lines  = preg_split("/\\r\\n/", $output);
			  //Loop for each line
              $numlines = 0;
			  $table ="";
              foreach ($lines as $line) 
			    {
				if ($numlines===0)
				  {
				  //First line is table header
				  $line = "<tr><th>".$line."\r\n";
				  $table .= preg_replace("/$match_delim[1]/","<th>",$line);
				  } 
				  else
				  {
				  //Other lines are data
				  $line = "<tr><td>".$line."\r\n";
				  $line_temp = preg_replace("/$match_delim[1]/","<td>",$line);
				  $table .= preg_replace("/<td><td>/","<td>",$line_temp);
				  } 				  
                ++$numlines; 
                }						
              $renderer->doc .= "\r\n<table class='inline' >\r\n".$table."</table>";
              break;

            case 'png' :
			  //To avoid browser cache insert random number in a filename
			  $random=rand();
			  $OutFileNameRnd =$dirname.'/'.'tempchain_'.$random.'_'.$match_output[1].'.'.$config[$mode]['oext'];
              //Copy file		
              copy($OutFileName, $OutFileNameRnd);		
              //Render a file png			  
			  $OutFileNamePng = 'temp/chain'.$program.'/'.$ns_path.'/'.'tempchain_'.$random.'_'.$match_output[1].'.'.$config[$mode]['oext'];
              $OutFileNamePng = str_replace("/",":",$OutFileNamePng); // Work out the namespace and page			   			  
			  $renderer->doc .= '<img src='.DOKU_BASE.'lib/exe/fetch.php?media='.$OutFileNamePng.' alt="Png Image Chain Plugin Error" />';
			  break;
            } 
          }
        break;
      case DOKU_LEXER_EXIT:
        break;
      }
    return true;
    }

	
  function DebugWindowsOpen()
    {
    $GLOBALS[status_window_open] = 1;
    ?>
    <script language="JavaScript">
    <!--
    var winOpts = "width=400,height=250,scrollbars,resizable";
    sw = window.open("", "Status", winOpts);
    if (sw.document == null)  
	  {
      sw = window.open("", "Status", winOpts);
      }
    sw.document.open();
    sw.bgcolor = "white";
    </script>
    <?php
    }

  function DebugWindowsWrite($s)
    {
    if (!$GLOBALS[status_window_open])
    return;
    ?>
    <script>
    <!--
    if (!sw.closed) { sw.document.write("<?php $s=str_replace(array("\n","\\"), array("<BR>","\\\\"), $s); echo "<TR>" . $s; ?>"); }
    </script>
    <?php
    flush();
    }

  function DebugWindowsClose()
    {
    if (!$GLOBALS[status_window_open])
    return;

    $GLOBALS[status_window_open] = 0;
    ?>
    <script language="JavaScript">
    <!--
    if (!sw.closed) { sw.close(); }
    </script>
    <?php
    }

	
  }

?>