User:Jeblad/DataTable

From mediawiki.org

Some ideas for chnages…

<?php

$wgExtensionCredits['parserhook'][] = array(
  'name' => 'DataTable',
  'version' => ExtDataTable::$mVersion,
  'author' => '[http://www.mediawiki.org/wiki/User:RV1971 RV1971]',
  'url' => 'http://www.mediawiki.org/wiki/Extension:DataTable',
  'description' => 'store data in a table and retrieve it on other pages'
);

// instance of this extension class
$wgExtDataTable = new ExtDataTable();

// register the extension
$wgExtensionFunctions[] = array( &$wgExtDataTable, 'setup' );

$wgHooks['LanguageGetMagic'][] = array( &$wgExtDataTable, 
					'onLanguageGetMagic' );

$wgHooks['ArticleDelete'][] = array( &$wgExtDataTable, 
				     'onArticleDelete' );

$wgHooks['ArticleSaveComplete'][] = array( &$wgExtDataTable, 
					   'onArticleSaveComplete' );

$wgHooks['NewRevisionFromEditComplete'][] = 
  array( &$wgExtDataTable, 
	 'onNewRevisionFromEditComplete' );

class ExtDataTable
{
  public static $mVersion = '0.5';

  public function setup() {
    global $wgParser;
    
    // parser functions
    $wgParser->setFunctionHook( 'data', array( &$this, 'data' ) );
    
    // tags
    $wgParser->setHook( 'datatable', array( &$this, 'datatable' ) );
  }

  public function onLanguageGetMagic( &$magicWords, $langCode ) {
    $magicWords['data'] = array( 0, 'data' );
    return true;
  }

  // hook to delete data when an article is deleted
  public function onArticleDelete( $article )
    {
      $dtd_page = $article->getID();
      $dbr =& wfGetDB( DB_MASTER );
      
      // Delete all data for this page.
      $dbr->delete( 'datatable_data', array( 'dtd_page' => $dtd_page ) );

      return true;
    }
  
  // hook to save data when an article is saved
  public function onArticleSaveComplete( $article, $user, $text )
  {
    return $this->saveData( $article, $text );
  }

  // hook to save data when an article is imported
  public function onNewRevisionFromEditComplete( $article, $rev, $baseID )
  {
    return $this->saveData( $article, $article->getContent() );
  }

  // display data
  public function datatable( $content, $params, &$parser)
    {
      extract( $params );

      $output = '';

      // parse head tag, if any
      if( preg_match( '+<head>(.*)</head>+s', $content, $matches ) )
	{
	  $head = $matches[1];
	  $content = str_replace( $matches[0], '', $content );
	  
	  if( $class )
	    $class = "class='$class'";
	  
	  $output .= "{| $class\n|-\n$head\n|-\n";
	}

      // parse template tag, if any
      if( preg_match( '+<template>(.*)</template>+s', $content, $matches ) )
	{
	  $inlinetemplate = $matches[1];
	  $content = str_replace( $matches[0], '', $content );
	  
	  for( $i = 1; $i <= 20; $i++ )
	    $param_array[] = '{{{' . $i . '}}}';
	}

      foreach( preg_split( "/[\n\r]+/", $content ) as $row ) 
	{
	  if( $row > '' )
	    { 
	      $record = array();
	      
	      foreach($this->splitRowByPipe( $row ) as $key => $value)
		$record['c' . ($key + 1)] = $value;
	      
	      if( $template )
		{
		  // explicitely name positional parameters
		  // in order to allow equal signs within parameters
		  $count = 0;
		  foreach( $record as $key => $arg )
		    $params[$key] = ++$count . '=' . $arg;
		  
		  
		  $output .= '{{' . $template . '|' 
		    . $this->templateArgString( $record ) . "}}\n";
		}
	      elseif( $inlinetemplate )
		{
		  $output .= str_replace( $param_array, $record, 
					  $inlinetemplate );
		}
	      else
		$output .= '| ' . implode( "\n| ", $record ) . "\n|-\n";
	    }
	}
      
      if( $head )
	$output .= "|-\n|}\n";
      
      if( $display != 'no' )
	return $parser->recursiveTagParse( $output );
    }

  // function save data from an article
  function saveData( $article, $text )
    {
      $dtd_page = $article->getID();
      $dbr =& wfGetDB( DB_MASTER );
      
      // Delete all data for this page.
      $dbr->delete( 'datatable_data', array( 'dtd_page' => $dtd_page ) );

      // Collect data to save
      Parser::extractTagsAndParams( array( 'datatable' ),
				    $text, $datatable_tags );

      foreach( $datatable_tags as $tagdata ) 
	{
	  list( $element, $content, $params, $tag ) = $tagdata;
	  
	  unset( $table );
	  extract( $params );
	  
	  // ignore if no table specified
	  if( !$table )
	    continue;
	  
	  // eliminate internal tags
	  $content = preg_replace( '+<head>(.*)</head>+s', '', $content );
	  $content = preg_replace( '+<template>(.*)</template>+s', '', 
				   $content );
	  
	  foreach( preg_split( "/[\n\r]+/", $content ) as $row ) 
	    {
	      if( $row > '' )
		{ 
		  $record = array( 'dtd_page' => $dtd_page,
				   'dtd_table' => $table );
		  
		  foreach($this->splitRowByPipe( $row ) as $key => $value)
		    $record['c' . ($key + 1)] = $value;
		  
		  $dbr->insert( 'datatable_data', $record );
		}
	    }
	}
      
      return true;
    }
  
  // explicitely name positional parameters
  // in order to allow equal signs within parameters
  function templateArgString( $params )
    {
      $count = 0;
      foreach($params as $key => $arg)
	$params[$key] = ++$count . '=' . $arg;
      
      return implode( '|', $params );
    }

  // split a pipe-separated row into an array
  // such that pipes within template calls are protected
  function splitRowByPipe( $string )
    {
      foreach(preg_split('//', $string, -1, PREG_SPLIT_NO_EMPTY) as $c)
	{
	  switch($c)
	    {
	    case '{' :
	    case '[' :
	      $substring .= $c;
	      $par++;
	      break;
	    case '}' :
	    case ']' :
	      $substring .= $c;
	      $par--;
	      break;
	    case '|' :
	      if( !$par )
		{
		  $string_array[] = $substring;
		  $substring = '';
		}
	      else
		$substring .= $c;
	      break;
	    default :
	      $substring .= $c;
	    }
	}
      
      $string_array[] = $substring;
      
      return $string_array;
    }

  // retrieve data
  function data( &$parser, $table, $columns, $template, $condition, $sort,
		 $p1, $p2, $p3, $p4 )
    {
      $dbr =& wfGetDB( DB_SLAVE );
      $result = '';
      $resarray = array();

      // clean up table
      $match = array();
      if(preg_match( '([_\w]+)', $table, $match)
        $table = $match[0];
      else
        ;// woopsie, report an error

      // clean up the columns
      $c = array();
      foreach(preg_split('\s*,\s*', $columns, -1, PREG_SPLIT_NO_EMPTY) as $str)
      {
        $match = array();
	if(preg_match( '^\s*([_\w]+)\s*$', $str, $match)
          $c[] = $match[1];
        else
          ;// woopsie, report an error
      }
      $columns = implode( ',', $c );
      if( !$columns )
	$columns = '*';

      // clean up the condition
      // probably needs some kind of parser..
      if( $condition )
        $condition = 'and ' . $condition;
      
      if( $sort ) {
        $match = array();
	if(preg_match( '([_\w]+\s+(asc|desc))', $sort, $match)
          $sort = 'order by ' . $match[0];
        else
          ;// woopsie, report an error
      }
      
      $res = $dbr->query( "select distinct $columns from datatable_data where dtd_table = '$table' $condition $sort" , __METHOD__ );
      
      while( $record = $dbr->fetchRow( $res ) )
	{
	  // fetchRow returns an array with both numeric and text keys
	  // we keep only the numeric keys
	  foreach( $record as $key => $value )
	    if( !is_int( $key ) )
	      unset( $record[$key] );

          if( $columns == '*' )
	    {
	      // all columns except page ID and table name
	      unset($record[0]);
	      unset($record[1]);
	    }
	  
	  if( $template )
	    $result .= "{{{$template}|" . $this->templateArgString( $record )
	      . "|p1=$p1|p2=$p2|p3=$p3|p4=$p4}}";
	  else
            $resarray[] = implode( ', ', $record );
	}

      if( !$template )
	$result = implode( '; ', $resarray );
      
      $dbr->freeResult($res);
      
      return $parser->replaceVariables( $result );
    }
}