Extension:DataInvoker
From MediaWiki.org
|
Release status: experimental |
|||
|---|---|---|---|
| Implementation | Parser function, Data extraction | ||
| Description | Allows retrieving data from a database | ||
| Author(s) | Xudong Yang (WyvernoidTalk) | ||
| Last Version | 0.3b (Jul 30 2008) | ||
| MediaWiki | 1.12 (older/newer versions not tested, but should work) | ||
| License | See Code section below | ||
| Download | See Code section below | ||
| Example | DotA Allstars Wiki | ||
|
|||
|
|||
|
check usage (experimental) |
|||
DataInvoker is an extension that introduces new parser functions which allow retrieving data from a database.
Contents |
[edit] Installation
Please cut and paste the code found below and place it in $IP/extensions/DataInvoker/DataInvoker.php. Note: $IP stands for the root directory of your MediaWiki installation, the same directory that holds LocalSettings.php.
To install this extension, add the following to LocalSettings.php:
require_once("$IP/extensions/DataInvoker/DataInvoker.php");
[edit] Configuration parameters
Note: These parameters should be put in LocalSettings.php.
There are currently 4 configuration parameters for this extension. They are:
- $wgDataInvokerHostName - String that indicates the host name of the database connection. Default value is
localhost. - $wgDataInvokerUserName - String that specifies the username of the database connection. Default value is
dbuser. - $wgDataInvokerPassword - String that specifies the password of the database connection. Default value is
dbpass. - $wgDataInvokerDatabase - String that specifies the name of the database being connected to. Default value is
dbname.
[edit] Reference
DataInvoker currently introduces 4 new parser functions allowing data retrieving.
[edit] Usage
DataInvoker retrieves data by doing a query to the database and caching the result temporarily, then reading data from the cache and outputing them. doquery and rawquery do the database query, retrieve a row, and store the result into a temporary cache with a handle. Then getdata can be used to read data from the cached results by a key (column name) and a certain handle. When necessary, use escapestr to escape a string for safe SQL execution with rawquery.
[edit] #doquery:
Does a query to the database and stores the result in a temporary cache.
{{ #doquery: table_name | column_name | match_column | match_value | order_by | order | row_number | handle_name }}
Other than the first parameter (table_name), all parameters are optional.
- table_name
- The name(s) of the table to retrieve data from. If there are multiple tables, use commas (,) to separate them - although retrieving data from multiple tables isn't fully tested yet and might not work.
- column_name
- The name(s) of the columns from which the fields are wanted in the result output. If there are multiple columns, use commas (,) to separate them. If all columns of a table is wanted, use an asterisk (*). Default value is *.
- match_column and match_value
- Indicates that the match_column column of the retrieved result rows must be match_value. If omitted (or empty), no matching pattern is required for the retrieved rows.
- order_by
- Sorts the retrieved rows by the order_by column(s). If there are multiple columns to sort by, use commas (,) to separate them - although sorting by multiple rows isn't fully tested yet and might not work. If omitted (or empty), no sorting is done to the retrieved rows.
- order
- Specifies the way the rows are ordered by order_by. Possible values are ASC (in ascending order), or DESC (in descending order). Default value is ASC.
- row_number
- When multiple rows are retrieved and they all match the matching pattern defined by match_column and match_value (if any), only the row_numberth row will be output. Default value is 1. Must be a positive integer.
- handle_name
- The name of the handle by which to store the result to the cache. This is a classifier of the result which can be referred to by #getdata: later. When this is omitted, the result is stored with the handle _unnamedHandleN, when the stored result is the Nth (counting from 0) without a specified handle name.
[edit] #rawquery:
Does a raw SQL query to the database and stores the result in a temporary cache.
{{ #rawquery: query_string | row_number | handle_name }}
The second and third parameter can be omitted.
- query_string
- The raw SQL query string. Must start with the characters
SELECT, orselect, orsEleCT, etc. - row_number
- When multiple rows are retrieved, only the row_numberth row will be output. Default value is 1. Must be a positive integer.
- handle_name
- The name of the handle by which to store the result to the cache. This is a classifier of the result which can be referred to by #getdata: later. When this is omitted, the result is stored with the handle _unnamedHandleN, when the stored result is the Nth (counting from 0) retrieved result without a specified handle name.
[edit] #getdata:
Reads the data cache and outputs the value.
{{ #getdata: column_name | handle_name | suppress_error }}
- column_name
- Required. The name of the column to retrieve data from.
- handle_name
- Optional. The handle of the cached result. When omitted, it is defaulted to the last stored result.
- suppress_error
- Optional, Boolean. Whether to suppress the errors in case one occurs. Default value is false.
[edit] #escapestr:
Escapes the given string by adding slashes (\) in front of apostrophes ('). This can be useful when used in conjunction with #rawquery: (#doquery: automatically escapes the strings).
{{ #escapestr: string }}
- string
- Required. The string to escape.
[edit] About
[edit] Acknowledgments
Thanks go to those guys at the #mediawiki IRC channel who helped me with this. They include Werdna, MinuteElectron, Duesentrieb, and Dantman. Thanks again!
[edit] Under Development
This extension is still under development; it might be a little unstable since it hasn't undergone strict testing, and it is still too simple at the current stage. It lacks more functionality and I'm aware of the situation; you are welcome to give suggestions in the talk page.
[edit] Trivia
Invoker is a hero in the game DotA Allstars, who has the game's most unique skillset and requires a lot of skills to play.
[edit] Changelog
- v0.3b
- New parameter for getdata suppress_error
- New exception class DIError
- v0.3a Added a new parser function escapestr
- v0.3
- New parser functions doquery and rawquery
- Changed the way retrieving data works; now getdata no longer directly does a query, but retrieve data from the cached query results from either doquery or rawquery.
[edit] Code
<?php /* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ $wgExtensionCredits['parserhook'][] = array( 'name' => 'DataInvoker', 'version' => '0.3b', 'description' => 'Allows retrieving data from a database', 'author' => 'Xudong Yang', 'url' => 'http://www.mediawiki.org/wiki/Extension:DataInvoker', ); $wgExtensionFunctions[] = 'wfDataInvoker'; $wgHooks['LanguageGetMagic'][] = 'wfDataInvokerLanguageGetMagic'; function wfDataInvoker ( ) { global $wgParser, $wgExtDataInvoker; global $wgDataInvokerHostName, $wgDataInvokerUserName, $wgDataInvokerPassword, $wgDataInvokerDatabase; $wgExtDataInvoker = new ExtDataInvoker ( ); if(!$wgDataInvokerHostName) $wgDataInvokerHostName = 'localhost'; if(!$wgDataInvokerUserName) $wgDataInvokerUserName = 'dbuser'; if(!$wgDataInvokerPassword) $wgDataInvokerPassword = 'dbpass'; if(!$wgDataInvokerDatabase) $wgDataInvokerDatabase = 'dbname'; $wgParser->setFunctionHook('doquery', array(&$wgExtDataInvoker,'doQuery')); $wgParser->setFunctionHook('rawquery', array(&$wgExtDataInvoker,'rawQuery')); $wgParser->setFunctionHook('getdata', array(&$wgExtDataInvoker,'getData')); $wgParser->setFunctionHook('escapestr', array(&$wgExtDataInvoker,'escapeStr')); $wgParser->mDataInvokerUnnamedHandleCount = 0; } function wfDataInvokerLanguageGetMagic( &$magicWords, $langCode = "en" ) { switch ( $langCode ) { default: $magicWords['doquery'] = array ( 0, 'doquery' ); $magicWords['rawquery'] = array ( 0, 'rawquery' ); $magicWords['getdata'] = array ( 0, 'getdata' ); $magicWords['escapestr'] = array ( 0, 'escapestr' ); } return true; } class DIError extends Exception { public function __construct($msg){ $this->message = '<strong class="error">DataInvoker Error: ' . $msg . '</strong>'; } } class ExtDataInvoker { var $DBConn; /******** PRIVATE FUNCTIONS ********/ function initConnection() { global $wgDataInvokerHostName, $wgDataInvokerUserName, $wgDataInvokerPassword, $wgDataInvokerDatabase; $this->DBConn = Database::newFromParams($wgDataInvokerHostName, $wgDataInvokerUserName, $wgDataInvokerPassword, $wgDataInvokerDatabase); } function tableName($name) { if ( $name{0} != '`' ) { # Standard quoting $name = "`$name`"; } return $name; } function replode(&$args, $funcname, $glue=',', $delimiter=',') { $arglist = explode($delimiter, $args); foreach ( $arglist as &$arg ) eval('$arg = '.$funcname.'($arg);'); unset($arg); $args = implode($glue, $arglist); } function fetchRetRow ($queryresult, $rownum) { if($rownum < 1) throw new DIError('ROWNUM cannot be non-positive'); for($i = 0; ($i < $rownum) && ($queryrow = $this->DBConn->fetchRow($queryresult)); $i++); // Fetch the $rownum th row // Only keep string-indexed keys foreach( $queryrow as $key => $value ) if(is_int($key)) unset($queryrow[$key]); return $queryrow; } function safeSQL($string){ if(get_magic_quotes_gpc()) { $string = stripslashes($string); } $badWords = "(delete)|(update)|(union)|(insert)|(drop)|(http)|(--)"; $string = eregi_replace($badWords, "", $string); //$string = mysql_real_escape_string($string); return $string; } function cacheResult (&$parser, $querystr, &$handlename, $rownum) { $queryresult = $this->DBConn->query($querystr, 'ExtDataInvoker::cacheResult'); if(!$handlename) { $handlename = "_unnamedHandle".($parser->mDataInvokerUnnamedHandleCount++); } elseif(substr($handlename,0,1)=="_") { throw new DIError('HANDLE_NAME cannot begin with underscore'); } $parser->mDataInvokerDataCache[$handlename] = $this->fetchRetRow($queryresult, $rownum); $parser->mDataInvokerLastHandleName = $handlename; $this->DBConn->freeResult( $queryresult ); } /******** PARSER FUNCTIONS ********/ function doQuery (&$parser, $table='', $column='*', $matchcolumn='', $matchvalue='', $orderby='', $order='ASC', $rownum = 1, $handlename='', $debug=false) { try { $this->initConnection(); // Parse the parameters $this->replode($table, '$this->tableName'); # if($column != '*') $this->replode($column, '$this->DBConn->addQuotes'); if(!$column) $column = '*'; $matchcolumn = mysql_real_escape_string($matchcolumn); $matchvalue = mysql_real_escape_string($matchvalue); # $this->replode($orderby, '$this->DBConn->addQuotes'); if($order != 'ASC') $order = 'DESC'; // Form the query string $querystr = "SELECT $column FROM $table"; if($matchcolumn && $matchvalue) $querystr .= " WHERE $matchcolumn = '$matchvalue'"; if($orderby) $querystr .= " ORDER BY $orderby $order"; // Retrieve and cache the data $this->cacheResult($parser, $querystr, $handlename, $rownum); $ret = ''; if($debug) $ret.="doQuery: $querystr ; handleName: $handlename"; } catch( DBError $e ) { return "<strong class='error'>".$e->getMessage()."</strong>"; } catch( DIError $e ) { return $e->getMessage(); } return $ret; } function rawQuery (&$parser, $querystr='', $rownum = 1, $handlename='', $debug=false) { try { if(strtoupper(substr($querystr, 0, 6)) != 'SELECT') throw new DIError('Raw query must begin with SELECT'); $this->initConnection(); $querystr = $this->safeSQL($querystr); // Retrieve and cache the data $this->cacheResult($parser, $querystr, $handlename, $rownum); $ret = ''; if($debug) $ret.="rawQuery: $querystr; handleName: $handlename"; } catch( DBError $e ) { return "<strong class='error'>".$e->getMessage()."</strong>"; } catch( DIError $e ) { return $e->getMessage(); } return $ret; } function getData (&$parser, $key='', $handlename='', $suppresserror=false) { try { if(!$handlename) $handlename = $parser->mDataInvokerLastHandleName; if(!isset($parser->mDataInvokerDataCache[$handlename])) throw new DIError ("getData error: No such handle found ($handlename)"); if(!isset($parser->mDataInvokerDataCache[$handlename][$key])) throw new DIError ("getData error: No such key in handle ($key)"); $ret = $parser->mDataInvokerDataCache[$handlename][$key]; } catch ( DIError $e ) { if($suppresserror) return ''; else return $e->getMessage(); } return $ret; } function escapeStr (&$parser, $str='') { try { return mysql_real_escape_string($str); } catch (Exception $e) { return $str; } } }