From 1fc6e9cc869af4e23be070fe76a2bb657e73b659 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Sat, 23 Sep 2006 15:57:16 +0000 Subject: [PATCH] * API: major refactoring * API: added login functionality * API: added several output formats (needs optimization) --- includes/api/ApiBase.php | 113 ++-- includes/api/ApiFormatBase.php | 152 +++++ includes/api/ApiFormatJson.php | 52 ++ includes/api/ApiFormatJson_json.php | 841 +++++++++++++++++++++++++++ includes/api/ApiFormatXml.php | 128 +++++ includes/api/ApiFormatYaml.php | 51 ++ includes/api/ApiFormatYaml_spyc.php | 854 ++++++++++++++++++++++++++++ includes/api/ApiLogin.php | 120 ++++ includes/api/ApiMain.php | 105 +++- includes/api/ApiQuery.php | 63 +- includes/api/ApiQueryContent.php | 12 - 11 files changed, 2384 insertions(+), 107 deletions(-) create mode 100644 includes/api/ApiFormatBase.php create mode 100644 includes/api/ApiFormatJson.php create mode 100644 includes/api/ApiFormatJson_json.php create mode 100644 includes/api/ApiFormatXml.php create mode 100644 includes/api/ApiFormatYaml.php create mode 100644 includes/api/ApiFormatYaml_spyc.php create mode 100644 includes/api/ApiLogin.php diff --git a/includes/api/ApiBase.php b/includes/api/ApiBase.php index e482be45f1..fb9395e695 100644 --- a/includes/api/ApiBase.php +++ b/includes/api/ApiBase.php @@ -66,38 +66,11 @@ abstract class ApiBase { // Main module has GetResult() method overriden // Safety - avoid infinite loop: if ($this->IsMain()) - $this->DieDebug(__METHOD__.' base method was called on main module. '); + $this->DieDebug(__METHOD__ . + ' base method was called on main module. '); return $this->GetMain()->GetResult(); } - /** - * Returns an array of allowed parameters (keys) => default value for that parameter - */ - protected function GetAllowedParams() { - return false; - } - - /** - * Returns the description string for this module - */ - protected function GetDescription() { - return false; - } - - /** - * Returns usage examples for this module. Return null if no examples are available. - */ - protected function GetExamples() { - return false; - } - - /** - * Returns the description string for the given parameter. - */ - protected function GetParamDescription($paramName) { - return ''; - } - /** * Generates help message for this module, or false if there is no description */ @@ -106,28 +79,65 @@ abstract class ApiBase { $msg = $this->GetDescription(); if ($msg !== false) { + + if (is_array($msg)) + $msg = implode("\n", $msg); $msg .= "\n"; // Parameters $params = $this->GetAllowedParams(); if ($params !== false) { + $paramsDescription = $this->GetParamDescription(); $msg .= "Supported Parameters:\n"; foreach (array_keys($params) as $paramName) { - $msg .= sprintf(" %-14s - %s\n", $paramName, $this->GetParamDescription($paramName)); + $desc = isset ($paramsDescription[$paramName]) ? $paramsDescription[$paramName] : ''; + $msg .= sprintf(" %-14s - %s\n", $paramName, $desc); } } // Examples $examples = $this->GetExamples(); if ($examples !== false) { - $msg .= "Examples:\n "; - $msg .= implode("\n ", $examples) . "\n"; + if (is_array($examples)) { + $msg .= "Examples:\n"; + $msg .= implode("\n ", $examples) . "\n"; + } else { + $msg .= "Example: $examples\n"; + } } } return $msg; } + /** + * Returns the description string for this module + */ + protected function GetDescription() { + return false; + } + + /** + * Returns usage examples for this module. Return null if no examples are available. + */ + protected function GetExamples() { + return false; + } + + /** + * Returns an array of allowed parameters (keys) => default value for that parameter + */ + protected function GetAllowedParams() { + return false; + } + + /** + * Returns the description string for the given parameter. + */ + protected function GetParamDescription() { + return false; + } + /** * Using GetAllowedParams(), makes an array of the values provided by the user, * with key being the name of the variable, and value - validated value from user or default. @@ -153,10 +163,12 @@ abstract class ApiBase { $result = $wgRequest->getCheck($param); break; case 'array' : - if (count($dflt) != 3) - $this->DieDebug("In '$param', the default enum must have 3 parts - default, allowmultiple, and array of values " . gettype($dflt)); - $values = $wgRequest->getVal($param, $dflt[GN_ENUM_DFLT]); - $result = $this->ParseMultiValue($param, $values, $dflt[GN_ENUM_ISMULTI], $dflt[GN_ENUM_CHOICES]); + $enumParams = $dflt; + $dflt = isset ($enumParams[GN_ENUM_DFLT]) ? $enumParams[GN_ENUM_DFLT] : null; + $multi = isset ($enumParams[GN_ENUM_ISMULTI]) ? $enumParams[GN_ENUM_ISMULTI] : false; + $choices = isset ($enumParams[GN_ENUM_CHOICES]) ? $enumParams[GN_ENUM_CHOICES] : null; + $value = $wgRequest->getVal($param, $dflt); + $result = $this->ParseMultiValue($param, $value, $multi, $choices); break; default : $this->DieDebug("In '$param', unprocessed type " . gettype($dflt)); @@ -168,15 +180,26 @@ abstract class ApiBase { } /** - * Return an array of values that were given in a "a|b|c" notation, after it validates them against the list allowed values. + * Return an array of values that were given in a "a|b|c" notation, + * after it optionally validates them against the list allowed values. + * + * @param valueName - The name of the parameter (for error reporting) + * @param value - The value being parsed + * @param allowMultiple - Can $value contain more than one value separated by '|'? + * @param allowedValues - An array of values to check against. If null, all values are accepted. + * @return (allowMultiple ? an_array_of_values : a_single_value) */ - protected function ParseMultiValue($valueName, $values, $allowMultiple, $allowedValues) { - $valuesList = explode('|', $values); - if (!$allowMultiple && count($valuesList) != 1) - $this->DieUsage("Only one value is allowed: '" . implode("', '", $allowedValues) . "' for parameter '$valueName'", "multival_$valueName"); - $unknownValues = array_diff($valuesList, $allowedValues); - if ($unknownValues) { - $this->DieUsage("Unrecognised value" . (count($unknownValues) > 1 ? "s '" : " '") . implode("', '", $unknownValues) . "' for parameter '$valueName'", "unknown_$valueName"); + protected function ParseMultiValue($valueName, $value, $allowMultiple, $allowedValues) { + $valuesList = explode('|', $value); + if (!$allowMultiple && count($valuesList) != 1) { + $possibleValues = is_array($allowedValues) ? "of '" . implode("', '", $allowedValues) . "'" : ''; + $this->DieUsage("Only one $possibleValues is allowed for parameter '$valueName'", "multival_$valueName"); + } + if (is_array($allowedValues)) { + $unknownValues = array_diff($valuesList, $allowedValues); + if ($unknownValues) { + $this->DieUsage("Unrecognised value" . (count($unknownValues) > 1 ? "s '" : " '") . implode("', '", $unknownValues) . "' for parameter '$valueName'", "unknown_$valueName"); + } } return $allowMultiple ? $valuesList : $valuesList[0]; @@ -193,4 +216,4 @@ abstract class ApiBase { wfDebugDieBacktrace("Internal error in '{get_class($this)}': $message"); } } -?> +?> \ No newline at end of file diff --git a/includes/api/ApiFormatBase.php b/includes/api/ApiFormatBase.php new file mode 100644 index 0000000000..b6195f5c3a --- /dev/null +++ b/includes/api/ApiFormatBase.php @@ -0,0 +1,152 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * http://www.gnu.org/copyleft/gpl.html + */ + +if (!defined('MEDIAWIKI')) { + // Eclipse helper - will be ignored in production + require_once ("ApiBase.php"); +} + +abstract class ApiFormatBase extends ApiBase { + + private $mIsHtml, $mFormat, $mOriginalFormat; + + /** + * Constructor + */ + public function __construct($main, $format) { + parent :: __construct($main); + + $this->mOriginalFormat = $format; + $this->mIsHtml = (substr($format, -2, 2) === 'fm'); // ends with 'fm' + if ($this->mIsHtml) + $this->mFormat = substr($format, 0, -2); // remove ending 'fm' + else + $this->mFormat = $format; + $this->mFormat = strtoupper($this->mFormat); + } + + /** + * Overriding class returns the mime type that should be sent to the client. + * This method is not called if GetIsHtml() returns true. + * @return string + */ + public abstract function GetMimeType(); + + /** + * Returns true when an HTML filtering printer should be used. + * The default implementation assumes that formats ending with 'fm' + * should be formatted in HTML. + */ + public function GetIsHtml() { + return $this->mIsHtml; + } + + /** + * Initialize the printer function and prepares the output headers, etc. + * This method must be the first outputing method during execution. + * A help screen's header is printed for the HTML-based output + */ + function InitPrinter($isError) { + $isHtml = $this->GetIsHtml(); + $mime = $isHtml ? 'text/html' : $this->GetMimeType(); + header("Content-Type: $mime; charset=utf-8;"); + + if ($isHtml) { +?> + + + MediaWiki API + + +
+ + + This result is being shown in mFormat?> format, + which might not be suitable for your application.
+ See API help for more information.
+
+ +
+GetIsHtml()) {
+?>
+		
+ +GetIsHtml()) + echo $this->FormatHTML($text); + else + echo $text; + } + + /** + * Prety-print various elements in HTML format, such as xml tags and URLs. + * This method also replaces any "<" with < + */ + protected function FormatHTML($text) { + // encode all tags as safe blue strings + $text = ereg_replace('\<([^>]+)\>', '<\1>', $text); + // identify URLs + $text = ereg_replace("[a-zA-Z]+://[^ '()<\n]+", '\\0', $text); + // identify requests to api.php + $text = ereg_replace("\\0', $text); + // make strings inside * bold + $text = ereg_replace("\\*[^<>\n]+\\*", '\\0', $text); + + return $text; + } + + /** + * Returns usage examples for this format. + */ + protected function GetExamples() { + return 'api.php?action=query&meta=siteinfo&si=namespaces&format=' . $this->mOriginalFormat; + } +} +?> \ No newline at end of file diff --git a/includes/api/ApiFormatJson.php b/includes/api/ApiFormatJson.php new file mode 100644 index 0000000000..ec23968509 --- /dev/null +++ b/includes/api/ApiFormatJson.php @@ -0,0 +1,52 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * http://www.gnu.org/copyleft/gpl.html + */ + +if (!defined('MEDIAWIKI')) { + // Eclipse helper - will be ignored in production + require_once ("ApiFormatBase.php"); +} + +class ApiFormatJson extends ApiFormatBase { + + public function __construct($main, $format) { + parent :: __construct($main, $format); + } + + public function GetMimeType() { + return 'application/json'; + } + + public function Execute() { + require_once ('ApiFormatJson_json.php'); + $json = new Services_JSON(); + $this->PrintText( $json->encode( $this->GetResult()->GetData(), true )); + } + + protected function GetDescription() { + return 'Output data in JSON format'; + } +} +?> \ No newline at end of file diff --git a/includes/api/ApiFormatJson_json.php b/includes/api/ApiFormatJson_json.php new file mode 100644 index 0000000000..375de7eb84 --- /dev/null +++ b/includes/api/ApiFormatJson_json.php @@ -0,0 +1,841 @@ + +* @author Matt Knapp +* @author Brett Stimmerman +* @copyright 2005 Michal Migurski +* @version CVS: $Id: JSON.php,v 1.30 2006/03/08 16:10:20 migurski Exp $ +* @license http://www.opensource.org/licenses/bsd-license.php +* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 +*/ + +/** +* Marker constant for Services_JSON::decode(), used to flag stack state +*/ +define('SERVICES_JSON_SLICE', 1); + +/** +* Marker constant for Services_JSON::decode(), used to flag stack state +*/ +define('SERVICES_JSON_IN_STR', 2); + +/** +* Marker constant for Services_JSON::decode(), used to flag stack state +*/ +define('SERVICES_JSON_IN_ARR', 3); + +/** +* Marker constant for Services_JSON::decode(), used to flag stack state +*/ +define('SERVICES_JSON_IN_OBJ', 4); + +/** +* Marker constant for Services_JSON::decode(), used to flag stack state +*/ +define('SERVICES_JSON_IN_CMT', 5); + +/** +* Behavior switch for Services_JSON::decode() +*/ +define('SERVICES_JSON_LOOSE_TYPE', 16); + +/** +* Behavior switch for Services_JSON::decode() +*/ +define('SERVICES_JSON_SUPPRESS_ERRORS', 32); + +/** +* Converts to and from JSON format. +* +* Brief example of use: +* +* +* // create a new instance of Services_JSON +* $json = new Services_JSON(); +* +* // convert a complexe value to JSON notation, and send it to the browser +* $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4))); +* $output = $json->encode($value); +* +* print($output); +* // prints: ["foo","bar",[1,2,"baz"],[3,[4]]] +* +* // accept incoming POST data, assumed to be in JSON notation +* $input = file_get_contents('php://input', 1000000); +* $value = $json->decode($input); +* +*/ +class Services_JSON +{ + /** + * constructs a new JSON instance + * + * @param int $use object behavior flags; combine with boolean-OR + * + * possible values: + * - SERVICES_JSON_LOOSE_TYPE: loose typing. + * "{...}" syntax creates associative arrays + * instead of objects in decode(). + * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. + * Values which can't be encoded (e.g. resources) + * appear as NULL instead of throwing errors. + * By default, a deeply-nested resource will + * bubble up with an error, so all return values + * from encode() should be checked with isError() + */ + function Services_JSON($use = 0) + { + $this->use = $use; + } + + /** + * convert a string from one UTF-16 char to one UTF-8 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf16 UTF-16 character + * @return string UTF-8 character + * @access private + */ + function utf162utf8($utf16) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); + } + + $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); + + switch(true) { + case ((0x7F & $bytes) == $bytes): + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x7F & $bytes); + + case (0x07FF & $bytes) == $bytes: + // return a 2-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xC0 | (($bytes >> 6) & 0x1F)) + . chr(0x80 | ($bytes & 0x3F)); + + case (0xFFFF & $bytes) == $bytes: + // return a 3-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xE0 | (($bytes >> 12) & 0x0F)) + . chr(0x80 | (($bytes >> 6) & 0x3F)) + . chr(0x80 | ($bytes & 0x3F)); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * convert a string from one UTF-8 char to one UTF-16 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf8 UTF-8 character + * @return string UTF-16 character + * @access private + */ + function utf82utf16($utf8) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + } + + switch(strlen($utf8)) { + case 1: + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return $utf8; + + case 2: + // return a UTF-16 character from a 2-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x07 & (ord($utf8{0}) >> 2)) + . chr((0xC0 & (ord($utf8{0}) << 6)) + | (0x3F & ord($utf8{1}))); + + case 3: + // return a UTF-16 character from a 3-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr((0xF0 & (ord($utf8{0}) << 4)) + | (0x0F & (ord($utf8{1}) >> 2))) + . chr((0xC0 & (ord($utf8{1}) << 6)) + | (0x7F & ord($utf8{2}))); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * @param bool $pretty pretty-print output with indents and newlines + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function encode($var, $pretty=false) + { + $this->indent = 0; + $this->pretty = $pretty; + $this->nameValSeparator = $pretty ? ': ' : ':'; + return $this->encode2($var); + } + + /** + * encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access private + */ + function encode2($var) + { + if ($this->pretty) { + $close = "\n" . str_repeat("\t", $this->indent); + $open = $close . "\t"; + $mid = ',' . $open; + } + else { + $open = $close = ''; + $mid = ','; + } + + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT + $ascii = ''; + $strlen_var = strlen($var); + + /* + * Iterate over every character in the string, + * escaping with a slash or encoding to UTF-8 where necessary + */ + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + // double quote, slash, slosh + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + // characters U-00000000 - U-0000007F (same as ASCII) + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + // characters U-00000080 - U-000007FF, mask 110XXXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, ord($var{$c + 1})); + $c += 1; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2})); + $c += 2; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3})); + $c += 3; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFC) == 0xF8): + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4})); + $c += 4; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFE) == 0xFC): + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4}), + ord($var{$c + 5})); + $c += 5; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + } + } + + return '"'.$ascii.'"'; + + case 'array': + /* + * As per JSON spec if any array key is not an integer + * we must treat the the whole array as an object. We + * also try to catch a sparsely populated associative + * array with numeric keys here because some JS engines + * will create an array with empty indexes up to + * max_index which can cause memory issues and because + * the keys, which may be relevant, will be remapped + * otherwise. + * + * As per the ECMA and JSON specification an object may + * have any string as a property. Unfortunately due to + * a hole in the ECMA specification if the key is a + * ECMA reserved word or starts with a digit the + * parameter is only accessible using ECMAScript's + * bracket notation. + */ + + // treat as a JSON object + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + $this->indent++; + $properties = array_map(array($this, 'name_value'), + array_keys($var), + array_values($var)); + $this->indent--; + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . $open . join($mid, $properties) . $close . '}'; + } + + // treat it like a regular array + $this->indent++; + $elements = array_map(array($this, 'encode2'), $var); + $this->indent--; + + foreach($elements as $element) { + if(Services_JSON::isError($element)) { + return $element; + } + } + + return '[' . $open . join($mid, $elements) . $close . ']'; + + case 'object': + $vars = get_object_vars($var); + + $this->indent++; + $properties = array_map(array($this, 'name_value'), + array_keys($vars), + array_values($vars)); + $this->indent--; + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . $open . join($mid, $properties) . $close . '}'; + + default: + return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) + ? 'null' + : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string"); + } + } + + /** + * array-walking function for use in generating JSON-formatted name-value pairs + * + * @param string $name name of key to use + * @param mixed $value reference to an array element to be encoded + * + * @return string JSON-formatted name-value pair, like '"name":value' + * @access private + */ + function name_value($name, $value) + { + $encoded_value = $this->encode2($value); + + if(Services_JSON::isError($encoded_value)) { + return $encoded_value; + } + + return $this->encode2(strval($name)) . $this->nameValSeparator . $encoded_value; + } + + /** + * reduce a string by removing leading and trailing comments and whitespace + * + * @param $str string string value to strip of comments and whitespace + * + * @return string string value stripped of comments and whitespace + * @access private + */ + function reduce_string($str) + { + $str = preg_replace(array( + + // eliminate single line comments in '// ...' form + '#^\s*//(.+)$#m', + + // eliminate multi-line comments in '/* ... */' form, at start of string + '#^\s*/\*(.+)\*/#Us', + + // eliminate multi-line comments in '/* ... */' form, at end of string + '#/\*(.+)\*/\s*$#Us' + + ), '', $str); + + // eliminate extraneous space + return trim($str); + } + + /** + * decodes a JSON string into appropriate variable + * + * @param string $str JSON-formatted string + * + * @return mixed number, boolean, string, array, or object + * corresponding to given JSON input string. + * See argument 1 to Services_JSON() above for object-output behavior. + * Note that decode() always returns strings + * in ASCII or UTF-8 format! + * @access public + */ + function decode($str) + { + $str = $this->reduce_string($str); + + switch (strtolower($str)) { + case 'true': + return true; + + case 'false': + return false; + + case 'null': + return null; + + default: + $m = array(); + + if (is_numeric($str)) { + // Lookie-loo, it's a number + + // This would work on its own, but I'm trying to be + // good about returning integers where appropriate: + // return (float)$str; + + // Return float or int, as appropriate + return ((float)$str == (integer)$str) + ? (integer)$str + : (float)$str; + + } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { + // STRINGS RETURNED IN UTF-8 FORMAT + $delim = substr($str, 0, 1); + $chrs = substr($str, 1, -1); + $utf8 = ''; + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c < $strlen_chrs; ++$c) { + + $substr_chrs_c_2 = substr($chrs, $c, 2); + $ord_chrs_c = ord($chrs{$c}); + + switch (true) { + case $substr_chrs_c_2 == '\b': + $utf8 .= chr(0x08); + ++$c; + break; + case $substr_chrs_c_2 == '\t': + $utf8 .= chr(0x09); + ++$c; + break; + case $substr_chrs_c_2 == '\n': + $utf8 .= chr(0x0A); + ++$c; + break; + case $substr_chrs_c_2 == '\f': + $utf8 .= chr(0x0C); + ++$c; + break; + case $substr_chrs_c_2 == '\r': + $utf8 .= chr(0x0D); + ++$c; + break; + + case $substr_chrs_c_2 == '\\"': + case $substr_chrs_c_2 == '\\\'': + case $substr_chrs_c_2 == '\\\\': + case $substr_chrs_c_2 == '\\/': + if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || + ($delim == "'" && $substr_chrs_c_2 != '\\"')) { + $utf8 .= $chrs{++$c}; + } + break; + + case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): + // single, escaped unicode character + $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) + . chr(hexdec(substr($chrs, ($c + 4), 2))); + $utf8 .= $this->utf162utf8($utf16); + $c += 5; + break; + + case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): + $utf8 .= $chrs{$c}; + break; + + case ($ord_chrs_c & 0xE0) == 0xC0: + // characters U-00000080 - U-000007FF, mask 110XXXXX + //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 2); + ++$c; + break; + + case ($ord_chrs_c & 0xF0) == 0xE0: + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 3); + $c += 2; + break; + + case ($ord_chrs_c & 0xF8) == 0xF0: + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 4); + $c += 3; + break; + + case ($ord_chrs_c & 0xFC) == 0xF8: + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 5); + $c += 4; + break; + + case ($ord_chrs_c & 0xFE) == 0xFC: + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 6); + $c += 5; + break; + + } + + } + + return $utf8; + + } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { + // array, or object notation + + if ($str{0} == '[') { + $stk = array(SERVICES_JSON_IN_ARR); + $arr = array(); + } else { + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = array(); + } else { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = new stdClass(); + } + } + + array_push($stk, array('what' => SERVICES_JSON_SLICE, + 'where' => 0, + 'delim' => false)); + + $chrs = substr($str, 1, -1); + $chrs = $this->reduce_string($chrs); + + if ($chrs == '') { + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } else { + return $obj; + + } + } + + //print("\nparsing {$chrs}\n"); + + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c <= $strlen_chrs; ++$c) { + + $top = end($stk); + $substr_chrs_c_2 = substr($chrs, $c, 2); + + if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { + // found a comma that is not inside a string, array, etc., + // OR we've reached the end of the character list + $slice = substr($chrs, $top['where'], ($c - $top['where'])); + array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); + //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + // we are in an array, so just push an element onto the stack + array_push($arr, $this->decode($slice)); + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + // we are in an object, so figure + // out the property name and set an + // element in an associative array, + // for now + $parts = array(); + + if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + // "name":value pair + $key = $this->decode($parts[1]); + $val = $this->decode($parts[2]); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + // name:value pair, where name is unquoted + $key = $parts[1]; + $val = $this->decode($parts[2]); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } + + } + + } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { + // found a quote, and we are not inside a string + array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); + //print("Found start of string at {$c}\n"); + + } elseif (($chrs{$c} == $top['delim']) && + ($top['what'] == SERVICES_JSON_IN_STR) && + (($chrs{$c - 1} != '\\') || + ($chrs{$c - 1} == '\\' && $chrs{$c - 2} == '\\'))) { + // found a quote, we're in a string, and it's not escaped + array_pop($stk); + //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '[') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-bracket, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); + //print("Found start of array at {$c}\n"); + + } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { + // found a right-bracket, and we're in an array + array_pop($stk); + //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '{') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-brace, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); + //print("Found start of object at {$c}\n"); + + } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { + // found a right-brace, and we're in an object + array_pop($stk); + //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($substr_chrs_c_2 == '/*') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a comment start, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); + $c++; + //print("Found start of comment at {$c}\n"); + + } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { + // found a comment end, and we're in one now + array_pop($stk); + $c++; + + for ($i = $top['where']; $i <= $c; ++$i) + $chrs = substr_replace($chrs, ' ', $i, 1); + + //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } + + } + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + return $obj; + + } + + } + } + } + + /** + * @todo Ultimately, this should just call PEAR::isError() + */ + function isError($data, $code = null) + { + if (class_exists('pear')) { + return PEAR::isError($data, $code); + } elseif (is_object($data) && (get_class($data) == 'services_json_error' || + is_subclass_of($data, 'services_json_error'))) { + return true; + } + + return false; + } +} + +if (class_exists('PEAR_Error')) { + + class Services_JSON_Error extends PEAR_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + parent::PEAR_Error($message, $code, $mode, $options, $userinfo); + } + } + +} else { + + /** + * @todo Ultimately, this class shall be descended from PEAR_Error + */ + class Services_JSON_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + + } + } + +} + +?> diff --git a/includes/api/ApiFormatXml.php b/includes/api/ApiFormatXml.php new file mode 100644 index 0000000000..3c30e75416 --- /dev/null +++ b/includes/api/ApiFormatXml.php @@ -0,0 +1,128 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * http://www.gnu.org/copyleft/gpl.html + */ + +if (!defined('MEDIAWIKI')) { + // Eclipse helper - will be ignored in production + require_once ("ApiFormatBase.php"); +} + +class ApiFormatXml extends ApiFormatBase { + + public function __construct($main, $format) { + parent :: __construct($main, $format); + } + + public function GetMimeType() { + return 'text/xml'; + } + + public function Execute() { + $xmlindent = null; + extract($this->ExtractRequestParams()); + + if ($xmlindent || $this->GetIsHtml()) + $xmlindent = -2; + else + $xmlindent = null; + + $this->PrintText(''); + $this->recXmlPrint('api', $this->GetResult()->GetData(), $xmlindent); + } + + /** + * This method takes an array and converts it into an xml. + * There are several noteworthy cases: + * + * If array contains a key "_element", then the code assumes that ALL other keys are not important and replaces them with the value['_element']. + * Example: name="root", value = array( "_element"=>"page", "x", "y", "z") creates x y z + * + * If any of the array's element key is "*", then the code treats all other key->value pairs as attributes, and the value['*'] as the element's content. + * Example: name="root", value = array( "*"=>"text", "lang"=>"en", "id"=>10) creates text + * + * If neither key is found, all keys become element names, and values become element content. + * The method is recursive, so the same rules apply to any sub-arrays. + */ + function recXmlPrint($elemName, $elemValue, $indent) { + $indstr = ""; + if (!is_null($indent)) { + $indent += 2; + $indstr = "\n" . str_repeat(" ", $indent); + } + + switch (gettype($elemValue)) { + case 'array' : + if (array_key_exists('*', $elemValue)) { + $subElemContent = $elemValue['*']; + unset ($elemValue['*']); + if (gettype($subElemContent) === 'array') { + $this->PrintText($indstr . wfElement($elemName, $elemValue, null)); + $this->recXmlPrint($elemName, $subElemContent, $indent); + $this->PrintText($indstr . ""); + } else { + $this->PrintText($indstr . wfElement($elemName, $elemValue, $subElemContent)); + } + } else { + $this->PrintText($indstr . wfElement($elemName, null, null)); + if (array_key_exists('_element', $elemValue)) { + $subElemName = $elemValue['_element']; + foreach ($elemValue as $subElemId => & $subElemValue) { + if ($subElemId !== '_element') { + $this->recXmlPrint($subElemName, $subElemValue, $indent); + } + } + } else { + foreach ($elemValue as $subElemName => & $subElemValue) { + $this->recXmlPrint($subElemName, $subElemValue, $indent); + } + } + $this->PrintText($indstr . ""); + } + break; + case 'object' : + // ignore + break; + default : + $this->PrintText($indstr . wfElement($elemName, null, $elemValue)); + break; + } + } + protected function GetDescription() { + return 'Output data in XML format'; + } + + protected function GetAllowedParams() { + return array ( + 'xmlindent' => false + ); + } + + protected function GetParamDescription() { + return array ( + 'xmlindent' => 'Enable XML indentation' + ); + } +} +?> \ No newline at end of file diff --git a/includes/api/ApiFormatYaml.php b/includes/api/ApiFormatYaml.php new file mode 100644 index 0000000000..1311c90841 --- /dev/null +++ b/includes/api/ApiFormatYaml.php @@ -0,0 +1,51 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * http://www.gnu.org/copyleft/gpl.html + */ + +if (!defined('MEDIAWIKI')) { + // Eclipse helper - will be ignored in production + require_once ("ApiFormatBase.php"); +} + +class ApiFormatYaml extends ApiFormatBase { + + public function __construct($main, $format) { + parent :: __construct($main, $format); + } + + public function GetMimeType() { + return 'application/yaml'; + } + + public function Execute() { + require_once ('ApiFormatYaml_spyc.php'); + $this->PrintText(Spyc :: YAMLDump($this->GetResult()->GetData())); + } + + protected function GetDescription() { + return 'Output data in YAML format'; + } +} +?> \ No newline at end of file diff --git a/includes/api/ApiFormatYaml_spyc.php b/includes/api/ApiFormatYaml_spyc.php new file mode 100644 index 0000000000..05a39e2361 --- /dev/null +++ b/includes/api/ApiFormatYaml_spyc.php @@ -0,0 +1,854 @@ + + * @link http://spyc.sourceforge.net/ + * @copyright Copyright 2005-2006 Chris Wanstrath + * @license http://www.opensource.org/licenses/mit-license.php MIT License + * @package Spyc + */ + + /** + * A node, used by Spyc for parsing YAML. + * @package Spyc + */ + class YAMLNode { + /**#@+ + * @access public + * @var string + */ + var $parent; + var $id; + /**#@+*/ + /** + * @access public + * @var mixed + */ + var $data; + /** + * @access public + * @var int + */ + var $indent; + /** + * @access public + * @var bool + */ + var $children = false; + + /** + * The constructor assigns the node a unique ID. + * @access public + * @return void + */ + function YAMLNode() { + $this->id = uniqid(''); + } + } + + /** + * The Simple PHP YAML Class. + * + * This class can be used to read a YAML file and convert its contents + * into a PHP array. It currently supports a very limited subsection of + * the YAML spec. + * + * Usage: + * + * $parser = new Spyc; + * $array = $parser->load($file); + * + * @package Spyc + */ + class Spyc { + + /** + * Load YAML into a PHP array statically + * + * The load method, when supplied with a YAML stream (string or file), + * will do its best to convert YAML in a file into a PHP array. Pretty + * simple. + * Usage: + * + * $array = Spyc::YAMLLoad('lucky.yml'); + * print_r($array); + * + * @access public + * @return array + * @param string $input Path of YAML file or string containing YAML + */ + function YAMLLoad($input) { + $spyc = new Spyc; + return $spyc->load($input); + } + + /** + * Dump YAML from PHP array statically + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. Pretty simple. Feel free to + * save the returned string as nothing.yml and pass it around. + * + * Oh, and you can decide how big the indent is and what the wordwrap + * for folding is. Pretty cool -- just pass in 'false' for either if + * you want to use the default. + * + * Indent's default is 2 spaces, wordwrap's default is 40 characters. And + * you can turn off wordwrap by passing in 0. + * + * @access public + * @static + * @return string + * @param array $array PHP array + * @param int $indent Pass in false to use the default, which is 2 + * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) + */ + public static function YAMLDump($array,$indent = false,$wordwrap = false) { + $spyc = new Spyc; + return $spyc->dump($array,$indent,$wordwrap); + } + + /** + * Load YAML into a PHP array from an instantiated object + * + * The load method, when supplied with a YAML stream (string or file path), + * will do its best to convert the YAML into a PHP array. Pretty simple. + * Usage: + * + * $parser = new Spyc; + * $array = $parser->load('lucky.yml'); + * print_r($array); + * + * @access public + * @return array + * @param string $input Path of YAML file or string containing YAML + */ + function load($input) { + // See what type of input we're talking about + // If it's not a file, assume it's a string + if (!empty($input) && (strpos($input, "\n") === false) + && file_exists($input)) { + $yaml = file($input); + } else { + $yaml = explode("\n",$input); + } + // Initiate some objects and values + $base = new YAMLNode; + $base->indent = 0; + $this->_lastIndent = 0; + $this->_lastNode = $base->id; + $this->_inBlock = false; + $this->_isInline = false; + + foreach ($yaml as $linenum => $line) { + $ifchk = trim($line); + + // If the line starts with a tab (instead of a space), throw a fit. + if (preg_match('/^(\t)+(\w+)/', $line)) { + $err = 'ERROR: Line '. ($linenum + 1) .' in your input YAML begins'. + ' with a tab. YAML only recognizes spaces. Please reformat.'; + die($err); + } + + if ($this->_inBlock === false && empty($ifchk)) { + continue; + } elseif ($this->_inBlock == true && empty($ifchk)) { + $last =& $this->_allNodes[$this->_lastNode]; + $last->data[key($last->data)] .= "\n"; + } elseif ($ifchk{0} != '#' && substr($ifchk,0,3) != '---') { + // Create a new node and get its indent + $node = new YAMLNode; + $node->indent = $this->_getIndent($line); + + // Check where the node lies in the hierarchy + if ($this->_lastIndent == $node->indent) { + // If we're in a block, add the text to the parent's data + if ($this->_inBlock === true) { + $parent =& $this->_allNodes[$this->_lastNode]; + $parent->data[key($parent->data)] .= trim($line).$this->_blockEnd; + } else { + // The current node's parent is the same as the previous node's + if (isset($this->_allNodes[$this->_lastNode])) { + $node->parent = $this->_allNodes[$this->_lastNode]->parent; + } + } + } elseif ($this->_lastIndent < $node->indent) { + if ($this->_inBlock === true) { + $parent =& $this->_allNodes[$this->_lastNode]; + $parent->data[key($parent->data)] .= trim($line).$this->_blockEnd; + } elseif ($this->_inBlock === false) { + // The current node's parent is the previous node + $node->parent = $this->_lastNode; + + // If the value of the last node's data was > or | we need to + // start blocking i.e. taking in all lines as a text value until + // we drop our indent. + $parent =& $this->_allNodes[$node->parent]; + $this->_allNodes[$node->parent]->children = true; + if (is_array($parent->data)) { + $chk = $parent->data[key($parent->data)]; + if ($chk === '>') { + $this->_inBlock = true; + $this->_blockEnd = ' '; + $parent->data[key($parent->data)] = + str_replace('>','',$parent->data[key($parent->data)]); + $parent->data[key($parent->data)] .= trim($line).' '; + $this->_allNodes[$node->parent]->children = false; + $this->_lastIndent = $node->indent; + } elseif ($chk === '|') { + $this->_inBlock = true; + $this->_blockEnd = "\n"; + $parent->data[key($parent->data)] = + str_replace('|','',$parent->data[key($parent->data)]); + $parent->data[key($parent->data)] .= trim($line)."\n"; + $this->_allNodes[$node->parent]->children = false; + $this->_lastIndent = $node->indent; + } + } + } + } elseif ($this->_lastIndent > $node->indent) { + // Any block we had going is dead now + if ($this->_inBlock === true) { + $this->_inBlock = false; + if ($this->_blockEnd = "\n") { + $last =& $this->_allNodes[$this->_lastNode]; + $last->data[key($last->data)] = + trim($last->data[key($last->data)]); + } + } + + // We don't know the parent of the node so we have to find it + // foreach ($this->_allNodes as $n) { + foreach ($this->_indentSort[$node->indent] as $n) { + if ($n->indent == $node->indent) { + $node->parent = $n->parent; + } + } + } + + if ($this->_inBlock === false) { + // Set these properties with information from our current node + $this->_lastIndent = $node->indent; + // Set the last node + $this->_lastNode = $node->id; + // Parse the YAML line and return its data + $node->data = $this->_parseLine($line); + // Add the node to the master list + $this->_allNodes[$node->id] = $node; + // Add a reference to the node in an indent array + $this->_indentSort[$node->indent][] =& $this->_allNodes[$node->id]; + // Add a reference to the node in a References array if this node + // has a YAML reference in it. + if ( + ( (is_array($node->data)) && + isset($node->data[key($node->data)]) && + (!is_array($node->data[key($node->data)])) ) + && + ( (preg_match('/^&([^ ]+)/',$node->data[key($node->data)])) + || + (preg_match('/^\*([^ ]+)/',$node->data[key($node->data)])) ) + ) { + $this->_haveRefs[] =& $this->_allNodes[$node->id]; + } elseif ( + ( (is_array($node->data)) && + isset($node->data[key($node->data)]) && + (is_array($node->data[key($node->data)])) ) + ) { + // Incomplete reference making code. Ugly, needs cleaned up. + foreach ($node->data[key($node->data)] as $d) { + if ( !is_array($d) && + ( (preg_match('/^&([^ ]+)/',$d)) + || + (preg_match('/^\*([^ ]+)/',$d)) ) + ) { + $this->_haveRefs[] =& $this->_allNodes[$node->id]; + } + } + } + } + } + } + unset($node); + + // Here we travel through node-space and pick out references (& and *) + $this->_linkReferences(); + + // Build the PHP array out of node-space + $trunk = $this->_buildArray(); + return $trunk; + } + + /** + * Dump PHP array to YAML + * + * The dump method, when supplied with an array, will do its best + * to convert the array into friendly YAML. Pretty simple. Feel free to + * save the returned string as tasteful.yml and pass it around. + * + * Oh, and you can decide how big the indent is and what the wordwrap + * for folding is. Pretty cool -- just pass in 'false' for either if + * you want to use the default. + * + * Indent's default is 2 spaces, wordwrap's default is 40 characters. And + * you can turn off wordwrap by passing in 0. + * + * @access public + * @return string + * @param array $array PHP array + * @param int $indent Pass in false to use the default, which is 2 + * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40) + */ + function dump($array,$indent = false,$wordwrap = false) { + // Dumps to some very clean YAML. We'll have to add some more features + // and options soon. And better support for folding. + + // New features and options. + if ($indent === false or !is_numeric($indent)) { + $this->_dumpIndent = 2; + } else { + $this->_dumpIndent = $indent; + } + + if ($wordwrap === false or !is_numeric($wordwrap)) { + $this->_dumpWordWrap = 40; + } else { + $this->_dumpWordWrap = $wordwrap; + } + + // New YAML document + $string = "---\n"; + + // Start at the base of the array and move through it. + foreach ($array as $key => $value) { + $string .= $this->_yamlize($key,$value,0); + } + return $string; + } + + /**** Private Properties ****/ + + /**#@+ + * @access private + * @var mixed + */ + var $_haveRefs; + var $_allNodes; + var $_lastIndent; + var $_lastNode; + var $_inBlock; + var $_isInline; + var $_dumpIndent; + var $_dumpWordWrap; + /**#@+*/ + + /**** Private Methods ****/ + + /** + * Attempts to convert a key / value array item to YAML + * @access private + * @return string + * @param $key The name of the key + * @param $value The value of the item + * @param $indent The indent of the current node + */ + function _yamlize($key,$value,$indent) { + if (is_array($value)) { + // It has children. What to do? + // Make it the right kind of item + $string = $this->_dumpNode($key,NULL,$indent); + // Add the indent + $indent += $this->_dumpIndent; + // Yamlize the array + $string .= $this->_yamlizeArray($value,$indent); + } elseif (!is_array($value)) { + // It doesn't have children. Yip. + $string = $this->_dumpNode($key,$value,$indent); + } + return $string; + } + + /** + * Attempts to convert an array to YAML + * @access private + * @return string + * @param $array The array you want to convert + * @param $indent The indent of the current level + */ + function _yamlizeArray($array,$indent) { + if (is_array($array)) { + $string = ''; + foreach ($array as $key => $value) { + $string .= $this->_yamlize($key,$value,$indent); + } + return $string; + } else { + return false; + } + } + + /** + * Returns YAML from a key and a value + * @access private + * @return string + * @param $key The name of the key + * @param $value The value of the item + * @param $indent The indent of the current node + */ + function _dumpNode($key,$value,$indent) { + // do some folding here, for blocks + if (strpos($value,"\n")) { + $value = $this->_doLiteralBlock($value,$indent); + } else { + $value = $this->_doFolding($value,$indent); + } + + $spaces = str_repeat(' ',$indent); + + if (is_int($key)) { + // It's a sequence + $string = $spaces.'- '.$value."\n"; + } else { + // It's mapped + $string = $spaces.$key.': '.$value."\n"; + } + return $string; + } + + /** + * Creates a literal block for dumping + * @access private + * @return string + * @param $value + * @param $indent int The value of the indent + */ + function _doLiteralBlock($value,$indent) { + $exploded = explode("\n",$value); + $newValue = '|'; + $indent += $this->_dumpIndent; + $spaces = str_repeat(' ',$indent); + foreach ($exploded as $line) { + $newValue .= "\n" . $spaces . trim($line); + } + return $newValue; + } + + /** + * Folds a string of text, if necessary + * @access private + * @return string + * @param $value The string you wish to fold + */ + function _doFolding($value,$indent) { + // Don't do anything if wordwrap is set to 0 + if ($this->_dumpWordWrap === 0) { + return $value; + } + + if (strlen($value) > $this->_dumpWordWrap) { + $indent += $this->_dumpIndent; + $indent = str_repeat(' ',$indent); + $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent"); + $value = ">\n".$indent.$wrapped; + } + return $value; + } + + /* Methods used in loading */ + + /** + * Finds and returns the indentation of a YAML line + * @access private + * @return int + * @param string $line A line from the YAML file + */ + function _getIndent($line) { + preg_match('/^\s{1,}/',$line,$match); + if (!empty($match[0])) { + $indent = substr_count($match[0],' '); + } else { + $indent = 0; + } + return $indent; + } + + /** + * Parses YAML code and returns an array for a node + * @access private + * @return array + * @param string $line A line from the YAML file + */ + function _parseLine($line) { + $line = trim($line); + + $array = array(); + + if (preg_match('/^-(.*):$/',$line)) { + // It's a mapped sequence + $key = trim(substr(substr($line,1),0,-1)); + $array[$key] = ''; + } elseif ($line[0] == '-' && substr($line,0,3) != '---') { + // It's a list item but not a new stream + if (strlen($line) > 1) { + $value = trim(substr($line,1)); + // Set the type of the value. Int, string, etc + $value = $this->_toType($value); + $array[] = $value; + } else { + $array[] = array(); + } + } elseif (preg_match('/^(.+):/',$line,$key)) { + // It's a key/value pair most likely + // If the key is in double quotes pull it out + if (preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) { + $value = trim(str_replace($matches[1],'',$line)); + $key = $matches[2]; + } else { + // Do some guesswork as to the key and the value + $explode = explode(':',$line); + $key = trim($explode[0]); + array_shift($explode); + $value = trim(implode(':',$explode)); + } + + // Set the type of the value. Int, string, etc + $value = $this->_toType($value); + if (empty($key)) { + $array[] = $value; + } else { + $array[$key] = $value; + } + } + return $array; + } + + /** + * Finds the type of the passed value, returns the value as the new type. + * @access private + * @param string $value + * @return mixed + */ + function _toType($value) { + if (preg_match('/^("(.*)"|\'(.*)\')/',$value,$matches)) { + $value = (string)preg_replace('/(\'\'|\\\\\')/',"'",end($matches)); + $value = preg_replace('/\\\\"/','"',$value); + } elseif (preg_match('/^\\[(.+)\\]$/',$value,$matches)) { + // Inline Sequence + + // Take out strings sequences and mappings + $explode = $this->_inlineEscape($matches[1]); + + // Propogate value array + $value = array(); + foreach ($explode as $v) { + $value[] = $this->_toType($v); + } + } elseif (strpos($value,': ')!==false && !preg_match('/^{(.+)/',$value)) { + // It's a map + $array = explode(': ',$value); + $key = trim($array[0]); + array_shift($array); + $value = trim(implode(': ',$array)); + $value = $this->_toType($value); + $value = array($key => $value); + } elseif (preg_match("/{(.+)}$/",$value,$matches)) { + // Inline Mapping + + // Take out strings sequences and mappings + $explode = $this->_inlineEscape($matches[1]); + + // Propogate value array + $array = array(); + foreach ($explode as $v) { + $array = $array + $this->_toType($v); + } + $value = $array; + } elseif (strtolower($value) == 'null' or $value == '' or $value == '~') { + $value = NULL; + } elseif (ctype_digit($value)) { + $value = (int)$value; + } elseif (in_array(strtolower($value), + array('true', 'on', '+', 'yes', 'y'))) { + $value = TRUE; + } elseif (in_array(strtolower($value), + array('false', 'off', '-', 'no', 'n'))) { + $value = FALSE; + } elseif (is_numeric($value)) { + $value = (float)$value; + } else { + // Just a normal string, right? + $value = trim(preg_replace('/#(.+)$/','',$value)); + } + + return $value; + } + + /** + * Used in inlines to check for more inlines or quoted strings + * @access private + * @return array + */ + function _inlineEscape($inline) { + // There's gotta be a cleaner way to do this... + // While pure sequences seem to be nesting just fine, + // pure mappings and mappings with sequences inside can't go very + // deep. This needs to be fixed. + + // Check for strings + $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/'; + if (preg_match_all($regex,$inline,$strings)) { + $saved_strings[] = $strings[0][0]; + $inline = preg_replace($regex,'YAMLString',$inline); + } + unset($regex); + + // Check for sequences + if (preg_match_all('/\[(.+)\]/U',$inline,$seqs)) { + $inline = preg_replace('/\[(.+)\]/U','YAMLSeq',$inline); + $seqs = $seqs[0]; + } + + // Check for mappings + if (preg_match_all('/{(.+)}/U',$inline,$maps)) { + $inline = preg_replace('/{(.+)}/U','YAMLMap',$inline); + $maps = $maps[0]; + } + + $explode = explode(', ',$inline); + + // Re-add the strings + if (!empty($saved_strings)) { + $i = 0; + foreach ($explode as $key => $value) { + if (strpos($value,'YAMLString')) { + $explode[$key] = str_replace('YAMLString',$saved_strings[$i],$value); + ++$i; + } + } + } + + // Re-add the sequences + if (!empty($seqs)) { + $i = 0; + foreach ($explode as $key => $value) { + if (strpos($value,'YAMLSeq') !== false) { + $explode[$key] = str_replace('YAMLSeq',$seqs[$i],$value); + ++$i; + } + } + } + + // Re-add the mappings + if (!empty($maps)) { + $i = 0; + foreach ($explode as $key => $value) { + if (strpos($value,'YAMLMap') !== false) { + $explode[$key] = str_replace('YAMLMap',$maps[$i],$value); + ++$i; + } + } + } + + return $explode; + } + + /** + * Builds the PHP array from all the YAML nodes we've gathered + * @access private + * @return array + */ + function _buildArray() { + $trunk = array(); + + if (!isset($this->_indentSort[0])) { + return $trunk; + } + + foreach ($this->_indentSort[0] as $n) { + if (empty($n->parent)) { + $this->_nodeArrayizeData($n); + // Check for references and copy the needed data to complete them. + $this->_makeReferences($n); + // Merge our data with the big array we're building + $trunk = $this->_array_kmerge($trunk,$n->data); + } + } + + return $trunk; + } + + /** + * Traverses node-space and sets references (& and *) accordingly + * @access private + * @return bool + */ + function _linkReferences() { + if (is_array($this->_haveRefs)) { + foreach ($this->_haveRefs as $node) { + if (!empty($node->data)) { + $key = key($node->data); + // If it's an array, don't check. + if (is_array($node->data[$key])) { + foreach ($node->data[$key] as $k => $v) { + $this->_linkRef($node,$key,$k,$v); + } + } else { + $this->_linkRef($node,$key); + } + } + } + } + return true; + } + + function _linkRef(&$n,$key,$k = NULL,$v = NULL) { + if (empty($k) && empty($v)) { + // Look for &refs + if (preg_match('/^&([^ ]+)/',$n->data[$key],$matches)) { + // Flag the node so we know it's a reference + $this->_allNodes[$n->id]->ref = substr($matches[0],1); + $this->_allNodes[$n->id]->data[$key] = + substr($n->data[$key],strlen($matches[0])+1); + // Look for *refs + } elseif (preg_match('/^\*([^ ]+)/',$n->data[$key],$matches)) { + $ref = substr($matches[0],1); + // Flag the node as having a reference + $this->_allNodes[$n->id]->refKey = $ref; + } + } elseif (!empty($k) && !empty($v)) { + if (preg_match('/^&([^ ]+)/',$v,$matches)) { + // Flag the node so we know it's a reference + $this->_allNodes[$n->id]->ref = substr($matches[0],1); + $this->_allNodes[$n->id]->data[$key][$k] = + substr($v,strlen($matches[0])+1); + // Look for *refs + } elseif (preg_match('/^\*([^ ]+)/',$v,$matches)) { + $ref = substr($matches[0],1); + // Flag the node as having a reference + $this->_allNodes[$n->id]->refKey = $ref; + } + } + } + + /** + * Finds the children of a node and aids in the building of the PHP array + * @access private + * @param int $nid The id of the node whose children we're gathering + * @return array + */ + function _gatherChildren($nid) { + $return = array(); + $node =& $this->_allNodes[$nid]; + foreach ($this->_allNodes as $z) { + if ($z->parent == $node->id) { + // We found a child + $this->_nodeArrayizeData($z); + // Check for references + $this->_makeReferences($z); + // Merge with the big array we're returning + // The big array being all the data of the children of our parent node + $return = $this->_array_kmerge($return,$z->data); + } + } + return $return; + } + + /** + * Turns a node's data and its children's data into a PHP array + * + * @access private + * @param array $node The node which you want to arrayize + * @return boolean + */ + function _nodeArrayizeData(&$node) { + if (is_array($node->data) && $node->children == true) { + // This node has children, so we need to find them + $childs = $this->_gatherChildren($node->id); + // We've gathered all our children's data and are ready to use it + $key = key($node->data); + $key = empty($key) ? 0 : $key; + // If it's an array, add to it of course + if (is_array($node->data[$key])) { + $node->data[$key] = $this->_array_kmerge($node->data[$key],$childs); + } else { + $node->data[$key] = $childs; + } + } elseif (!is_array($node->data) && $node->children == true) { + // Same as above, find the children of this node + $childs = $this->_gatherChildren($node->id); + $node->data = array(); + $node->data[] = $childs; + } + + // We edited $node by reference, so just return true + return true; + } + + /** + * Traverses node-space and copies references to / from this object. + * @access private + * @param object $z A node whose references we wish to make real + * @return bool + */ + function _makeReferences(&$z) { + // It is a reference + if (isset($z->ref)) { + $key = key($z->data); + // Copy the data to this object for easy retrieval later + $this->ref[$z->ref] =& $z->data[$key]; + // It has a reference + } elseif (isset($z->refKey)) { + if (isset($this->ref[$z->refKey])) { + $key = key($z->data); + // Copy the data from this object to make the node a real reference + $z->data[$key] =& $this->ref[$z->refKey]; + } + } + return true; + } + + + /** + * Merges arrays and maintains numeric keys. + * + * An ever-so-slightly modified version of the array_kmerge() function posted + * to php.net by mail at nospam dot iaindooley dot com on 2004-04-08. + * + * http://us3.php.net/manual/en/function.array-merge.php#41394 + * + * @access private + * @param array $arr1 + * @param array $arr2 + * @return array + */ + function _array_kmerge($arr1,$arr2) { + if(!is_array($arr1)) + $arr1 = array(); + + if(!is_array($arr2)) + $arr2 = array(); + + $keys1 = array_keys($arr1); + $keys2 = array_keys($arr2); + $keys = array_merge($keys1,$keys2); + $vals1 = array_values($arr1); + $vals2 = array_values($arr2); + $vals = array_merge($vals1,$vals2); + $ret = array(); + + foreach($keys as $key) { + list($unused,$val) = each($vals); + // This is the good part! If a key already exists, but it's part of a + // sequence (an int), just keep addin numbers until we find a fresh one. + if (isset($ret[$key]) and is_int($key)) { + while (array_key_exists($key, $ret)) { + $key++; + } + } + $ret[$key] = $val; + } + + return $ret; + } + } +?> \ No newline at end of file diff --git a/includes/api/ApiLogin.php b/includes/api/ApiLogin.php new file mode 100644 index 0000000000..79b4b7418a --- /dev/null +++ b/includes/api/ApiLogin.php @@ -0,0 +1,120 @@ + + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * http://www.gnu.org/copyleft/gpl.html + */ + +if (!defined('MEDIAWIKI')) { + // Eclipse helper - will be ignored in production + require_once ("ApiBase.php"); +} + +class ApiLogin extends ApiBase { + + /** + * Constructor + */ + public function __construct($main, $action) { + parent :: __construct($main); + } + + public function Execute() { + $lgname = $lgpassword = $lgdomain = null; + extract($this->ExtractRequestParams()); + + $params = new FauxRequest(array ( + 'wpName' => $lgname, + 'wpPassword' => $lgpassword, + 'wpDomain' => $lgdomain, + 'wpRemember' => '' + )); + + $result = array(); + + $loginForm = new LoginForm($params); + switch ($loginForm->authenticateUserData()) + { + case (AuthSuccess): + $result['result'] = 'Success'; + $result['lguserid'] = $_SESSION['wsUserID']; + $result['lgusername'] = $_SESSION['wsUserName']; + $result['lgtoken'] = $_SESSION['wsToken']; + break; + + case (AuthNoName): + $result['result'] = 'AuthNoName'; + break; + case (AuthIllegal): + $result['result'] = 'AuthIllegal'; + break; + case (AuthWrongPluginPass): + $result['result'] = 'AuthWrongPluginPass'; + break; + case (AuthNotExists): + $result['result'] = 'AuthNotExists'; + break; + case (AuthWrongPass): + $result['result'] = 'AuthWrongPass'; + break; + case (AuthEmptyPass): + $result['result'] = 'AuthEmptyPass'; + break; + default: + $this->DieDebug( "Unhandled case value" ); + } + + $this->GetResult()->AddMessage('login', null, $result); + } + + /** + * Returns an array of allowed parameters (keys) => default value for that parameter + */ + protected function GetAllowedParams() { + return array ( + 'lgname' => '', + 'lgpassword' => '', + 'lgdomain' => null + ); + } + + /** + * Returns the description string for the given parameter. + */ + protected function GetParamDescription() { + return array ( + 'lgname' => 'User Name', + 'lgpassword' => 'Password', + 'lgdomain' => 'Domain (optional)', + + ); + } + + /** + * Returns the description string for this module + */ + protected function GetDescription() { + return array("*Login Module*", + "This module is used to login and get the authentication tokens."); + } +} +?> \ No newline at end of file diff --git a/includes/api/ApiMain.php b/includes/api/ApiMain.php index ad3cd28f50..d206017593 100644 --- a/includes/api/ApiMain.php +++ b/includes/api/ApiMain.php @@ -29,21 +29,38 @@ if (!defined('MEDIAWIKI')) { require_once ("ApiBase.php"); } +/** +* @desc This exception will be thrown when DieUsage is called to stop module execution. +*/ +class UsageException extends Exception { + var $codestr; + + public function __construct($message, $codestr) { + parent :: __construct($message); + $this->codestr = $codestr; + } + public function __toString() { + return "{$this->codestr}: {$this->message}"; + } +} + class ApiMain extends ApiBase { - private $mModules, $mModuleNames, $mApiStartTime, $mResult; + private $mPrinter, $mModules, $mModuleNames, $mFormats, $mFormatNames, $mApiStartTime, $mResult; /** * Constructor * $apiStartTime - time of the originating call for profiling purposes * $modules - an array of actions (keys) and classes that handle them (values) */ - public function __construct($apiStartTime, $modules) { + public function __construct($apiStartTime, $modules, $formats) { // Special handling for the main module: $parent === $this parent :: __construct($this); $this->mModules = $modules; $this->mModuleNames = array_keys($modules); + $this->mFormats = $formats; + $this->mFormatNames = array_keys($formats); $this->mApiStartTime = $apiStartTime; $this->mResult = new ApiResult($this); } @@ -54,36 +71,57 @@ class ApiMain extends ApiBase { protected function GetAllowedParams() { return array ( - 'format' => 'xmlfm', + 'format' => array ( + GN_ENUM_DFLT => API_DEFAULT_FORMAT, + GN_ENUM_CHOICES => $this->mFormatNames + ), 'action' => array ( GN_ENUM_DFLT => 'help', - GN_ENUM_ISMULTI => false, GN_ENUM_CHOICES => $this->mModuleNames ) ); } + protected function GetParamDescription() { + return array ( + 'format' => 'The format of the output', + 'action' => 'What action you would like to perform' + ); + } + public function Execute() { $action = $format = null; - extract($this->ExtractRequestParams()); + try { + extract($this->ExtractRequestParams()); + + // Create an appropriate printer + $this->mPrinter = new $this->mFormats[$format] ($this, $format); + + // Instantiate and execute module requested by the user + $module = new $this->mModules[$action] ($this, $action); + $module->Execute(); + $this->PrintResult(false); + } catch (UsageException $e) { + // Printer may not be initialized if the ExtractRequestParams() fails for the main module + if (!isset ($this->mPrinter)) + $this->mPrinter = new $this->mFormats[API_DEFAULT_FORMAT] ($this, API_DEFAULT_FORMAT); + $this->PrintResult(true); + } + } - // Instantiate and execute module requested by the user - $module = new $this->mModules[$action] ($this, $action); - $module->Execute(); + /** + * Internal printer + */ + private function PrintResult($isError) { + $this->mPrinter->InitPrinter($isError); + $this->mPrinter->Execute(); + $this->mPrinter->ClosePrinter(); } protected function GetDescription() { return "This API allows programs to access various functions of MediaWiki software."; - } - - protected function GetParamDescription($paramName) { - switch($paramName) { - case 'format': return "The format of the output"; - case 'action': return "What action you would like to perform"; - default: return parent :: GetParamDescription($paramName); - } } - + public function MainDieUsage($description, $errorCode, $httpRespCode = 0) { $this->mResult->Reset(); $this->mResult->addMessage('error', null, $errorCode); @@ -93,8 +131,35 @@ class ApiMain extends ApiBase { header($errorCode, true, $httpRespCode); $this->mResult->addMessage('usage', null, $this->MakeHelpMsg()); - - var_export($this->mResult->GetData()); + + throw new UsageException($description, $errorCode); + } + + /** + * Override the parent to generate help messages for all available modules. + */ + public function MakeHelpMsg() { + + // Use parent to make default message for the main module + $msg = parent :: MakeHelpMsg(); + + $msg .= "\n\n*Modules*\n"; + foreach ($this->mModules as $moduleName => $moduleClass) { + $module = new $this->mModules[$moduleName] ($this, $moduleName); + $msg2 = $module->MakeHelpMsg(); + if ($msg2 !== false) + $msg .= $msg2 . "\n"; + } + + $msg .= "\n*Formats*\n"; + foreach ($this->mFormats as $moduleName => $moduleClass) { + $module = new $this->mFormats[$moduleName] ($this, $moduleName); + $msg2 = $module->MakeHelpMsg(); + if ($msg2 !== false) + $msg .= $msg2 . "\n"; + } + + return $msg; } } -?> +?> \ No newline at end of file diff --git a/includes/api/ApiQuery.php b/includes/api/ApiQuery.php index c10a57db87..b692a983f0 100644 --- a/includes/api/ApiQuery.php +++ b/includes/api/ApiQuery.php @@ -31,29 +31,29 @@ if (!defined('MEDIAWIKI')) { class ApiQuery extends ApiBase { - private static $apiAutoloadClasses_Query = array ( - 'ApiQueryContent' => 'includes/api/ApiQueryContent.php', - - ); - - private static $sQueryModules = array ( + private $mQueryPropModules = array ( 'content' => 'ApiQueryContent' ); + private $mQueryListModules = array ( + 'backlinks' => 'ApiQueryBacklinks' + ); + + private $mSlaveDB = null; /** * Constructor */ public function __construct($main, $action) { parent :: __construct($main); - ApiInitAutoloadClasses($this->apiAutoloadClasses_Query); - $this->mModuleNames = array_keys($this->sQueryModules); - $this->mDb =& wfGetDB( DB_SLAVE ); + $this->mPropModuleNames = array_keys($this->mQueryPropModules); + $this->mListModuleNames = array_keys($this->mQueryListModules); } - + public function GetDB() { - return $this->mDb; + if (!isset ($this->mSlaveDB)) + $this->mSlaveDB = & wfGetDB(DB_SLAVE); + return $this->mSlaveDB; } - public function Execute() { @@ -64,20 +64,35 @@ class ApiQuery extends ApiBase { */ protected function GetAllowedParams() { return array ( - 'what' => 'default', - 'enumparam' => array ( + 'titles' => array ( + GN_ENUM_DFLT => null, + GN_ENUM_ISMULTI => true + ), + 'pageids' => array ( + GN_ENUM_DFLT => 0, + GN_ENUM_ISMULTI => true + ), + 'revids' => array ( + GN_ENUM_DFLT => 0, + GN_ENUM_ISMULTI => true + ), + 'prop' => array ( GN_ENUM_DFLT => null, GN_ENUM_ISMULTI => true, - GN_ENUM_CHOICES => $this->mModuleNames + GN_ENUM_CHOICES => array_keys($this->mPropModuleNames ) - ); + ), 'list' => array ( + GN_ENUM_DFLT => null, + GN_ENUM_ISMULTI => true, + GN_ENUM_CHOICES => array_keys($this->mListModuleNames + ))); } /** * Returns the description string for this module */ protected function GetDescription() { - return 'module a'; + return 'Query Module'; } /** @@ -85,20 +100,8 @@ class ApiQuery extends ApiBase { */ protected function GetExamples() { return array ( - 'http://...' + 'api.php ? action=query & what=content & titles=ArticleA|ArticleB' ); } - - /** - * Returns the description string for the given parameter. - */ - protected function GetParamDescription($paramName) { - switch ($paramName) { - case 'param' : - return 'description'; - default : - return parent :: GetParamDescription($paramName); - } - } } ?> \ No newline at end of file diff --git a/includes/api/ApiQueryContent.php b/includes/api/ApiQueryContent.php index d01bc78cd0..491518e96b 100644 --- a/includes/api/ApiQueryContent.php +++ b/includes/api/ApiQueryContent.php @@ -74,17 +74,5 @@ class ApiQueryContent extends ApiQueryBase { 'http://...' ); } - - /** - * Returns the description string for the given parameter. - */ - protected function GetParamDescription($paramName) { - switch ($paramName) { - case 'param' : - return 'description'; - default : - return parent :: GetParamDescription($paramName); - } - } } ?> \ No newline at end of file -- 2.20.1