<?php
/**
 * DokuWiki Plugin simplemysqlclient (Syntax Component)
 *
 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
 * @author  Heiko Heinz <heiko.heinz@soft2c.de>
 */

// must be run within Dokuwiki
if (! defined ( 'DOKU_INC' ))
	die ();
class syntax_plugin_simplemysqlclient_simplemysqlclient extends DokuWiki_Syntax_Plugin {
	/**
	 *
	 * @return string Syntax mode type
	 */
	public function getType() {
		return 'container';
	}
	/**
	 *
	 * @return string Paragraph type
	 */
	public function getPType() {
		return 'block';
	}
	/**
	 *
	 * @return int Sort order - Low numbers go before high numbers
	 */
	public function getSort() {
		return 260;
	}
	
	/**
	 * Connect lookup pattern to lexer.
	 *
	 * @param string $mode
	 *        	Parser mode
	 */
	public function connectTo($mode) {
		$this->Lexer->addSpecialPattern ( '\{\{simplemysqlclient\}\}', $mode, 'plugin_simplemysqlclient_simplemysqlclient' );
	}
	
	// public function postConnect() {
	// $this->Lexer->addExitPattern('</FIXME>','plugin_simplemysqlclient_simplemysqlclient');
	// }
	
	/**
	 * Handle matches of the simplemysqlclient syntax
	 *
	 * @param string $match
	 *        	The match of the syntax
	 * @param int $state
	 *        	The state of the handler
	 * @param int $pos
	 *        	The position in the document
	 * @param Doku_Handler $handler
	 *        	The handler
	 * @return array Data for the renderer
	 */
	public function handle($match, $state, $pos, Doku_Handler &$handler) {
		$data = array ();
		
		return $data;
	}
	
	/**
	 * Render xhtml output or metadata
	 *
	 * @param string $mode
	 *        	Renderer mode (supported modes: xhtml)
	 * @param Doku_Renderer $renderer
	 *        	The renderer
	 * @param array $data
	 *        	The data from the handler() function
	 * @return bool If rendering was successful.
	 */
	public function render($mode, Doku_Renderer &$renderer, $data) {

		if ($mode != 'xhtml')
			return false;
		
		$renderer->info ['cache'] = false;
		
		if (! $this->isAuthorized ()) {
			echo '<div class="error">' . $this->getLang ( 'missingpermission' ) . '</div>';
			return true;
		}
		
		$button = $this->getLang ( 'submit_button' );
		
		$query='';
		if(isset($_GET ['q']))
		$query = trim ( $_GET ['q'] );
		
		$queryEsc = strtr ( $query, array (
				'<' => '&lt;' 
		) );
		
		$shortcuts = $this->getShortCuts ();
		$templates = $this->getTemplates ();
		
		$emptyresult = $this->getLang ( 'emptyresult' );
		
		echo <<<EOT
<script type="text/javascript"><!--
function simplemysqlclient_load(query)
{
	with ( document.simplemysqlclient.q )
	{
		value = query;
		focus();
	}

	return false;
}
//--></script>
<form action="$_SERVER[PHP_SELF]" method="GET" name="simplemysqlclient" id="simplemysqlclient">
 <input type="hidden" name="do" value="$_REQUEST[do]" />
 <input type="hidden" name="page" value="$_REQUEST[page]" />
 <input type="hidden" name="id" value="$_REQUEST[id]" />
 <div>
  $shortcuts
 </div>
 <div>
  $templates
 </div>
 $history
 <textarea name="q" rows="5" cols="100" style="width: 100%;">$queryEsc</textarea>
 <div>
  <input style="float:right;padding:4px;margin:10px" type="submit" value="$button" />
 </div>
</form>
<script type="text/javascript"><!--
document.simplemysqlclient.q.focus();
document.simplemysqlclient.q.select();  		
//--></script>
EOT;
		if ($query !== '') {
			
			echo <<<EOT
<div style="padding-top: 1em; margin-top: 1em; border-top: 1px solid #888888;">
EOT;
			
			try {
				
				$db = $this->connectToDb ();
				
				if (! $db) {
					echo '<div class="error">' . $this->getLang ( 'errordbconnection' ) . '</div>';
					return true;
				}
				if ($db->multi_query ( $query )) {
					echo '<u>' . $this->getLang ( 'firstresult' ) . '</u><br>';
					do {
						$result = $db->store_result ();
						if ($result instanceof mysqli_result) {
							try {
								$rows = $this->fetch_all( $result);
								if (sizeof ( $rows ) <= 0) {
									echo $this->getLang ( 'emptyresult' );
								}
								$cols = empty ( $first ) ? array () : array_keys ( $first );
								
								echo $this->__renderTable ( $cols, $rows );
							} catch ( Exception $e ) {
								echo '<div class="error">' . $e->getMessage () . '</div>';
							} finally {
								$result->free ();
							}
						} else if (is_bool ( $result )) {
							$err = mysqli_errno ( $db );
							echo $this->getLang ( 'queryexecuted' ) . ($err != 0 ? $this->getLang ( 'no' ) : $this->getLang ( 'yes' ));
						} else
							var_dump ( $result );
						
						if ($db->more_results ()) {
							echo '<br><u>' . $this->getLang ( 'nextresult' ) . '</u><br>';
						}
					} while ( $db->next_result () );
				} else {
					echo $this->getLang ( 'queriesnotexecuted' );
				}
			} catch ( Exception $e ) {
				echo '<div class="error">' . $e->getMessage () . '</div>';
			} finally {
				if ($db) {
					$db->close ();
				}
			}
			
			echo '</div>';
		}
		
		return true;
	}
	
	private function fetch_all($result)
	{
		if (method_exists('mysqli_result', 'fetch_all')) # Compatibility layer with PHP < 5.3
			$res = mysqli_fetch_all ( $result, MYSQLI_ASSOC );
		else
			for ($res = array(); $tmp = mysqli_fetch_array($result, MYSQLI_ASSOC);) $res[] = $tmp;
	
		return $res;
	}
	private function __renderTable($cols, $rows) {
		
		/*
		 * - collect header row according to listed rows
		 * - transform all listed rows to properly defined HTML table cells
		 */
		$headers = array ();
		$meta = array ();
		$counter = 0;
		
		foreach ( $rows as $nr => $row ) {
			
			// convert all values in current row to table cells
			$i = 0;
			
			foreach ( $row as $column => $value ) {
				
				$def = $meta [$column];
				
				$headers [$column] = $def ['label'] ? $def ['label'] : $column;
				$class = $def ['format'] ? $def ['format'] : 'na';
				$class .= ' col' . ++ $i;
				
				$value = $this->valueFromDB ( $column, $value, $def );
				
				$cell = $this->renderValue ( $column, $value, $def, false, false );
				
				$row [$column] = "<td class=\"$class\">" . $cell . "</td>\n";
			}
			
			// prepend cell for counter
			array_unshift ( $row, '<td class="counter col0 rightalign">' . ++ $counter . "</td>\n" );
			
			// convert set of values into HTML table row
			$classes = array ();
			
			if ($nr == 0)
				$classes [] = 'first';
			if ($nr == count ( $rows ) - 1)
				$classes [] = 'last';
			
			$classes [] = ($nr % 2) ? 'even' : 'odd';
			$classes [] = 'row' . ($nr + 1);
			
			$classes = implode ( ' ', $classes );
			
			$rows [$nr] = '<tr class="' . $classes . '">' . implode ( '', $row ) . "</tr>\n";
		}
		
		// finally convert all HTML table rows into single HTML table body
		$rows = implode ( '', $rows );
		
		/*
		 * compile header row
		 */
		
		// ensure to have row of headers (missing on an empty list of rows)
		if (empty ( $headers ))
			foreach ( $cols as $column ) {
				
				unset ( $def );
				
				if (is_array ( $meta [$column] ))
					$def = $meta [$column];
				else {
					// missing meta information on current "column name"
					// --> might be an alias definition
					// --> extract originally selected column name from that
					
					$pos = strripos ( $column, ' AS ' );
					if ($pos !== false) {
						
						$temp = substr ( $column, $pos + 4 );
						if ($meta [$temp]) {
							// found definition on extracted column name
							
							$def = $meta [$temp];
							$column = $temp;
						}
					}
				}
				
				$headers [$column] = $def ['label'] ? $def ['label'] : $column;
			}
		
		foreach ( $headers as $column => $label ) {
			
			$headers [$column] = "<th class=\"label\">$label</th>\n";
		}
		
		// compile row of header cells
		$headers = implode ( '', $headers );
		
		/*
		 * render list of rows as HTML table
		 */
		
		$width = intval ( $this->options ['width'] ) ? ' width="' . $this->options ['width'] . '"' : '';
		
		$trClass = $this->options ['wikistyle'] ? '' : ' class="data-list"';
		$tableClass = $this->options ['wikistyle'] ? ' class="inline"' : '';
		
		$table = <<<EOT
<div style="overflow-x: scroll;">
   <table width="100%"$tableClass>
    <thead>
     <tr class="row0">
      <th class="counter"></th>
      $headers
     </tr>
    </thead>
    <tbody>
     $rows
    </tbody>
   </table>
</div>
EOT;
		
		return $table;
	}
	protected function valueFromDB($column, $value, $def) {
		switch ($def ['format']) {
			
			case 'image' :
			case 'file' :
				if (is_null ( $value ))
					return null;
				
				if ($value === '||')
					return null;
					
					// parse file for internally used structure
				$a = strpos ( $value, '|' );
				if (! $a)
					// externally provided file --> don't touch
					return (strlen ( $value ) > 0);
				
				$b = strpos ( $value, '|', $a + 1 );
				if (! $b)
					// externally provided file --> don't touch
					return true;
				
				$temp = array (
						'mime' => substr ( $value, 0, $a ),
						'name' => substr ( $value, $a + 1, $b - $a - 1 ),
						'file' => substr ( $value, $b + 1 ) 
				);
				
				if (! preg_match ( '#^[a-z0-9-]+/[+a-z0-9-]+$#i', $temp ['mime'] ))
					// externally provided file --> don't touch
					return true;
				
				if (trim ( $temp ['name'] ) === '')
					// externally provided file --> don't touch
					return true;
				
				return $temp;
			
			case 'date' :
				if ($def ['options'] ['unixts'])
					return $value;
				
				if ((trim ( $value ) === '') || ($value == '0000-00-00'))
					return 0;
				
				return self::parseDBDateTime ( $value, true );
			
			case 'time' :
				return $value;
			
			case 'datetime' :
				if ($def ['options'] ['unixts'])
					return $value;
				
				$value = substr ( $value, 0, 19 );
				if ((trim ( $value ) === '') || ($value == '0000-00-00T00:00:00') || ($value == '0000-00-00 00:00:00'))
					return 0;
				
				return self::parseDBDateTime ( $value, false );
			
			case 'bool' :
				$value = trim ( $value );
				switch ($def ['options'] ['booltype']) {
					
					case 'int' :
						return (intval ( $value ) != 0);
					
					case 'xmark' :
						return (strtolower ( $value [0] ) == 'x');
					
					case 'yesno' :
					default :
						return (strtolower ( $value [0] ) == 'y');
				}
			
			case 'enum' :
				$value = trim ( $value );
				$value = array_search ( $value, $def ['options'] ['selectables'] );
				if ($value === false)
					$value = null;
				else
					$value = intval ( $value );
				
				break;
			
			case 'related' :
				if (is_numeric ( $value ))
					$value = intval ( $value );
				
				break;
			
			case 'monetary' :
			case 'real' :
			/**
			 *
			 * @todo manage decimal point conversions
			 */
			
			case 'url' :
			case 'email' :
			case 'phone' :
			case 'fax' :
			case 'text' :
			case 'integer' :
			// keep value as is ...
		}
		
		return $value;
	}
	protected function isAuthorized() {
		global $INPUT;
		$remoteUser = $INPUT->server->str ( 'REMOTE_USER' );
		
		if (! $remoteUser) {
			return false;
		}
		
		global $USERINFO;
		$groups = $USERINFO ['grps'];
		$allowedUserGroups = $this->getConf ( 'allowedUserGroups' );
		
		$allowedUserGroups = utf8_strtolower ( $allowedUserGroups );
		$members = explode ( ',', $allowedUserGroups );
		$members = array_map ( 'trim', $members );
		$members = array_unique ( $members );
		$members = array_filter ( $members );
		
		// compare cleaned values
		foreach ( $members as $member ) {
			if ($member == 'ALL')
				return true;
			if (in_array ( $member, $groups ))
				return true;
			else {
				if ($member == $remoteUser)
					return true;
			}
		}
		return false;
	}
	protected function renderValue($column, $value, $def, $mayBeSkipped = false, $inEditor = false) {
		if (is_null ( $value ) && ($def ['type'] != 'data'))
			return $mayBeSkipped ? null : '';
		
		switch ($def ['format']) {
			
			case 'text' :
				return trim ( $value );
			
			case 'image' :
			case 'file' :
				if ($value === true)
					return '<span class="info">' . $this->getLang ( 'fileexternalfound' ) . '</span>';
				else if ($value === false)
					return '<span class="info">' . $this->getLang ( 'fileexternalnotfound' ) . '</span>';
				else {
					
					$temp = is_array ( $value ) ? trim ( implode ( '', $value ) ) : '';
					
					if (! $inEditor)
						$url .= '&thumb=150';
					
					if ($def ['format'] === 'image')
						return "<img src=\"$url\" alt=\"" . sprintf ( $this->getLang ( 'fileimagealt' ), $column, $value ['mime'] ) . "\" />";
					
					return "<a href=\"$url\" title=\"" . $this->getLang ( 'filedlhint' ) . '">' . $this->getLang ( 'cmddl' ) . '</a>';
				}
				break;
			
			case 'email' :
				if ((trim ( $value ) === '') && $mayBeSkipped)
					return null;
				
				return DokuWiki_Plugin::email ( $value, $email );
			
			case 'url' :
				if ((trim ( $value ) === '') && $mayBeSkipped)
					return null;
				
				return DokuWiki_Plugin::external_link ( $value );
			
			case 'phone' :
			case 'fax' :
				if ((trim ( $value ) === ''))
					return null;
				
				return $value;
			
			case 'bool' :
				if (! $value)
					return null;
				return $value ? '&#10007;' : '&ndash;';
			
			case 'date' :
				if (! $value)
					return $mayBeSkipped ? null : '';
				
				return strftime ( strtok ( trim ( $this->getConf ( 'dformat' ) ), ' ' ), $value );
			
			case 'datetime' :
				if (! $value)
					return $mayBeSkipped ? null : '';
				
				return strftime ( $this->getConf ( 'dformat' ), $value );
			
			case 'time' :
				if ($value && (substr ( $value, - 3 ) == ':00') && (strlen ( $value ) > 5))
					$value = substr ( $value, 0, - 3 );
				
				return $value;
			
			case 'integer' :
			case 'monetary' :
			case 'real' :
				if (! $value && $mayBeSkipped)
					return null;
				
				return $value;
			
			case 'enum' :
			case 'related' :
				if (is_integer ( $value ))
					$value = $def ['options'] ['selectables'] [$value];
				
				return $value;
			
			default :
				return $value;
		}
	}
	private function getTemplates() {
		$ret = '<u><b>' . $this->getLang ( 'templatesLabel' ) . '</b></u><br>';
		
		$text = $this->getLang ( 'template.1.text' );
		if ($text == '') {
			return '';
		}
		
		for($i = 1; $i <= true; $i ++) {
			$text = $this->getLang ( 'template.' . $i . '.text' );
			if ($text == '') {
				return $ret;
			}
			
			$sql = $this->getLang ( 'template.' . $i . '.sql' );
			$ret = $ret . '<button style="margin:4px" onclick="return simplemysqlclient_load(\'' . $sql . '\')">' . $text . '</button>';
		}
		
		return $ret;
	}
	private function getShortCuts() {
		$ret = '<u><b>' . $this->getLang ( 'shortcutslabel' ) . '</b></u><br>';
		
		$text = $this->getLang ( 'shortcut.1.text' );
		if ($text == '') {
			return '';
		}
		
		for($i = 1; true; $i ++) {
			$text = $this->getLang ( 'shortcut.' . $i . '.text' );
			if ($text == '') {
				return $ret;
			}
			
			$sql = $this->getLang ( 'shortcut.' . $i . '.sql' );
			$sql = str_replace ( '#DATABASENAME#', $this->getConf ( 'dbName' ), $sql );
			$ret = $ret . '<button style="margin:4px" onclick="return simplemysqlclient_load(\'' . $sql . '\')">' . $text . '</button>';
		}
		
		return $ret;
	}
	private function connectToDb() { // DB-Verbindung herstellen - eRent
		$serverName = $this->getConf ( 'serverName' );
		$userName = $this->getConf ( 'userName' );
		$passWord = $this->getConf ( 'passWord' );
		$dbName = $this->getConf ( 'dbName' );
		
		$dbCon = new mysqli ( $serverName, $userName, $passWord, $dbName );
		mysqli_autocommit ( $dbCon, TRUE );
		mysqli_set_charset ( $dbCon, 'utf8' ); // muss auf UTF8 umgesetzt werden
		mysqli_select_db ( $dbCon, $dbName ); // DB-Auswahl
		
		return $dbCon;
	}
}

// vim:ts=4:sw=4:et:
