API: sinumberingroup now gives correct size of 'user' group, and omits size of implic...
[lhc/web/wiklou.git] / includes / api / ApiFormatXml.php
1 <?php
2 /**
3 * API for MediaWiki 1.8+
4 *
5 * Created on Sep 19, 2006
6 *
7 * Copyright © 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 * http://www.gnu.org/copyleft/gpl.html
23 *
24 * @file
25 */
26
27 if ( !defined( 'MEDIAWIKI' ) ) {
28 // Eclipse helper - will be ignored in production
29 require_once( 'ApiFormatBase.php' );
30 }
31
32 /**
33 * API XML output formatter
34 * @ingroup API
35 */
36 class ApiFormatXml extends ApiFormatBase {
37
38 private $mRootElemName = 'api';
39 private $mDoubleQuote = false;
40 private $mXslt = null;
41
42 public function __construct( $main, $format ) {
43 parent::__construct( $main, $format );
44 }
45
46 public function getMimeType() {
47 return 'text/xml';
48 }
49
50 public function getNeedsRawData() {
51 return true;
52 }
53
54 public function setRootElement( $rootElemName ) {
55 $this->mRootElemName = $rootElemName;
56 }
57
58 public function execute() {
59 $params = $this->extractRequestParams();
60 $this->mDoubleQuote = $params['xmldoublequote'];
61 $this->mXslt = $params['xslt'];
62
63 $this->printText( '<?xml version="1.0"?>' );
64 if ( !is_null( $this->mXslt ) ) {
65 $this->addXslt();
66 }
67 $this->printText(
68 self::recXmlPrint( $this->mRootElemName,
69 $this->getResultData(),
70 $this->getIsHtml() ? - 2 : null,
71 $this->mDoubleQuote
72 )
73 );
74 }
75
76 /**
77 * This method takes an array and converts it to XML.
78 * There are several noteworthy cases:
79 *
80 * If array contains a key '_element', then the code assumes that ALL other keys are not important and replaces them with the value['_element'].
81 * Example: name='root', value = array( '_element'=>'page', 'x', 'y', 'z') creates <root> <page>x</page> <page>y</page> <page>z</page> </root>
82 *
83 * 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.
84 * Example: name='root', value = array( '*'=>'text', 'lang'=>'en', 'id'=>10) creates <root lang='en' id='10'>text</root>
85 *
86 * If neither key is found, all keys become element names, and values become element content.
87 * The method is recursive, so the same rules apply to any sub-arrays.
88 */
89 public static function recXmlPrint( $elemName, $elemValue, $indent, $doublequote = false ) {
90 $retval = '';
91 if ( !is_null( $indent ) ) {
92 $indent += 2;
93 $indstr = "\n" . str_repeat( ' ', $indent );
94 } else {
95 $indstr = '';
96 }
97 $elemName = str_replace( ' ', '_', $elemName );
98
99 switch ( gettype( $elemValue ) ) {
100 case 'array':
101 if ( isset( $elemValue['*'] ) ) {
102 $subElemContent = $elemValue['*'];
103 if ( $doublequote ) {
104 $subElemContent = Sanitizer::encodeAttribute( $subElemContent );
105 }
106 unset( $elemValue['*'] );
107
108 // Add xml:space="preserve" to the
109 // element so XML parsers will leave
110 // whitespace in the content alone
111 $elemValue['xml:space'] = 'preserve';
112 } else {
113 $subElemContent = null;
114 }
115
116 if ( isset( $elemValue['_element'] ) ) {
117 $subElemIndName = $elemValue['_element'];
118 unset( $elemValue['_element'] );
119 } else {
120 $subElemIndName = null;
121 }
122
123 $indElements = array();
124 $subElements = array();
125 foreach ( $elemValue as $subElemId => & $subElemValue ) {
126 if ( is_string( $subElemValue ) && $doublequote ) {
127 $subElemValue = Sanitizer::encodeAttribute( $subElemValue );
128 }
129
130 if ( gettype( $subElemId ) === 'integer' ) {
131 $indElements[] = $subElemValue;
132 unset( $elemValue[$subElemId] );
133 } elseif ( is_array( $subElemValue ) ) {
134 $subElements[$subElemId] = $subElemValue;
135 unset ( $elemValue[$subElemId] );
136 }
137 }
138
139 if ( is_null( $subElemIndName ) && count( $indElements ) ) {
140 ApiBase::dieDebug( __METHOD__, "($elemName, ...) has integer keys without _element value. Use ApiResult::setIndexedTagName()." );
141 }
142
143 if ( count( $subElements ) && count( $indElements ) && !is_null( $subElemContent ) ) {
144 ApiBase::dieDebug( __METHOD__, "($elemName, ...) has content and subelements" );
145 }
146
147 if ( !is_null( $subElemContent ) ) {
148 $retval .= $indstr . Xml::element( $elemName, $elemValue, $subElemContent );
149 } elseif ( !count( $indElements ) && !count( $subElements ) ) {
150 $retval .= $indstr . Xml::element( $elemName, $elemValue );
151 } else {
152 $retval .= $indstr . Xml::element( $elemName, $elemValue, null );
153
154 foreach ( $subElements as $subElemId => & $subElemValue ) {
155 $retval .= self::recXmlPrint( $subElemId, $subElemValue, $indent );
156 }
157
158 foreach ( $indElements as $subElemId => & $subElemValue ) {
159 $retval .= self::recXmlPrint( $subElemIndName, $subElemValue, $indent );
160 }
161
162 $retval .= $indstr . Xml::closeElement( $elemName );
163 }
164 break;
165 case 'object':
166 // ignore
167 break;
168 default:
169 $retval .= $indstr . Xml::element( $elemName, null, $elemValue );
170 break;
171 }
172 return $retval;
173 }
174
175 function addXslt() {
176 $nt = Title::newFromText( $this->mXslt );
177 if ( is_null( $nt ) || !$nt->exists() ) {
178 $this->setWarning( 'Invalid or non-existent stylesheet specified' );
179 return;
180 }
181 if ( $nt->getNamespace() != NS_MEDIAWIKI ) {
182 $this->setWarning( 'Stylesheet should be in the MediaWiki namespace.' );
183 return;
184 }
185 if ( substr( $nt->getText(), - 4 ) !== '.xsl' ) {
186 $this->setWarning( 'Stylesheet should have .xsl extension.' );
187 return;
188 }
189 $this->printText( '<?xml-stylesheet href="' . $nt->escapeLocalURL( 'action=raw' ) . '" type="text/xsl" ?>' );
190 }
191
192 public function getAllowedParams() {
193 return array(
194 'xmldoublequote' => false,
195 'xslt' => null,
196 );
197 }
198
199 public function getParamDescription() {
200 return array(
201 'xmldoublequote' => 'If specified, double quotes all attributes and content',
202 'xslt' => 'If specified, adds <xslt> as stylesheet',
203 );
204 }
205
206 public function getDescription() {
207 return 'Output data in XML format' . parent::getDescription();
208 }
209
210 public function getVersion() {
211 return __CLASS__ . ': $Id$';
212 }
213 }