00001 <?php
00002 # Copyright (C) 2003-2008 Brion Vibber <brion@pobox.com>
00003 # http://www.mediawiki.org/
00004 #
00005 # This program is free software; you can redistribute it and/or modify
00006 # it under the terms of the GNU General Public License as published by
00007 # the Free Software Foundation; either version 2 of the License, or
00008 # (at your option) any later version.
00009 #
00010 # This program is distributed in the hope that it will be useful,
00011 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00013 # GNU General Public License for more details.
00014 #
00015 # You should have received a copy of the GNU General Public License along
00016 # with this program; if not, write to the Free Software Foundation, Inc.,
00017 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00018 # http://www.gnu.org/copyleft/gpl.html
00019
00024 class SpecialExport extends SpecialPage {
00025
00026 private $curonly, $doExport, $pageLinkDepth, $templates;
00027 private $images;
00028
00029 public function __construct() {
00030 parent::__construct( 'Export' );
00031 }
00032
00033 public function execute( $par ) {
00034 global $wgOut, $wgRequest, $wgSitename, $wgExportAllowListContributors;
00035 global $wgExportAllowHistory, $wgExportMaxHistory, $wgExportMaxLinkDepth;
00036 global $wgExportFromNamespaces;
00037
00038 $this->setHeaders();
00039 $this->outputHeader();
00040
00041
00042 $this->curonly = true;
00043 $this->doExport = false;
00044 $this->templates = $wgRequest->getCheck( 'templates' );
00045 $this->images = $wgRequest->getCheck( 'images' );
00046 $this->pageLinkDepth = $this->validateLinkDepth(
00047 $wgRequest->getIntOrNull( 'pagelink-depth' ) );
00048
00049 if ( $wgRequest->getCheck( 'addcat' ) ) {
00050 $page = $wgRequest->getText( 'pages' );
00051 $catname = $wgRequest->getText( 'catname' );
00052
00053 if ( $catname !== '' && $catname !== NULL && $catname !== false ) {
00054 $t = Title::makeTitleSafe( NS_MAIN, $catname );
00055 if ( $t ) {
00061 $catpages = $this->getPagesFromCategory( $t );
00062 if ( $catpages ) $page .= "\n" . implode( "\n", $catpages );
00063 }
00064 }
00065 }
00066 else if( $wgRequest->getCheck( 'addns' ) && $wgExportFromNamespaces ) {
00067 $page = $wgRequest->getText( 'pages' );
00068 $nsindex = $wgRequest->getText( 'nsindex' );
00069
00070 if ( $nsindex !== '' && $nsindex !== NULL && $nsindex !== false ) {
00074 $nspages = $this->getPagesFromNamespace( $nsindex );
00075 if ( $nspages ) $page .= "\n" . implode( "\n", $nspages );
00076 }
00077 }
00078 else if( $wgRequest->wasPosted() && $par == '' ) {
00079 $page = $wgRequest->getText( 'pages' );
00080 $this->curonly = $wgRequest->getCheck( 'curonly' );
00081 $rawOffset = $wgRequest->getVal( 'offset' );
00082 if( $rawOffset ) {
00083 $offset = wfTimestamp( TS_MW, $rawOffset );
00084 } else {
00085 $offset = null;
00086 }
00087 $limit = $wgRequest->getInt( 'limit' );
00088 $dir = $wgRequest->getVal( 'dir' );
00089 $history = array(
00090 'dir' => 'asc',
00091 'offset' => false,
00092 'limit' => $wgExportMaxHistory,
00093 );
00094 $historyCheck = $wgRequest->getCheck( 'history' );
00095 if ( $this->curonly ) {
00096 $history = WikiExporter::CURRENT;
00097 } elseif ( !$historyCheck ) {
00098 if ( $limit > 0 && $limit < $wgExportMaxHistory ) {
00099 $history['limit'] = $limit;
00100 }
00101 if ( !is_null( $offset ) ) {
00102 $history['offset'] = $offset;
00103 }
00104 if ( strtolower( $dir ) == 'desc' ) {
00105 $history['dir'] = 'desc';
00106 }
00107 }
00108
00109 if( $page != '' ) $this->doExport = true;
00110 } else {
00111
00112 $page = $wgRequest->getText( 'pages', $par );
00113 $historyCheck = $wgRequest->getCheck( 'history' );
00114 if( $historyCheck ) {
00115 $history = WikiExporter::FULL;
00116 } else {
00117 $history = WikiExporter::CURRENT;
00118 }
00119
00120 if( $page != '' ) $this->doExport = true;
00121 }
00122
00123 if( !$wgExportAllowHistory ) {
00124
00125 $history = WikiExporter::CURRENT;
00126 }
00127
00128 $list_authors = $wgRequest->getCheck( 'listauthors' );
00129 if ( !$this->curonly || !$wgExportAllowListContributors ) $list_authors = false ;
00130
00131 if ( $this->doExport ) {
00132 $wgOut->disable();
00133
00134
00135 wfResetOutputBuffers();
00136 header( "Content-type: application/xml; charset=utf-8" );
00137 if( $wgRequest->getCheck( 'wpDownload' ) ) {
00138
00139 $filename = urlencode( $wgSitename . '-' . wfTimestampNow() . '.xml' );
00140 $wgRequest->response()->header( "Content-disposition: attachment;filename={$filename}" );
00141 }
00142 $this->doExport( $page, $history, $list_authors );
00143 return;
00144 }
00145
00146 $wgOut->addWikiMsg( 'exporttext' );
00147
00148 $form = Xml::openElement( 'form', array( 'method' => 'post',
00149 'action' => $this->getTitle()->getLocalUrl( 'action=submit' ) ) );
00150 $form .= Xml::inputLabel( wfMsg( 'export-addcattext' ) , 'catname', 'catname', 40 ) . ' ';
00151 $form .= Xml::submitButton( wfMsg( 'export-addcat' ), array( 'name' => 'addcat' ) ) . '<br />';
00152
00153 if ( $wgExportFromNamespaces ) {
00154 $form .= Xml::namespaceSelector( '', null, 'nsindex', wfMsg( 'export-addnstext' ) ) . ' ';
00155 $form .= Xml::submitButton( wfMsg( 'export-addns' ), array( 'name' => 'addns' ) ) . '<br />';
00156 }
00157
00158 $form .= Xml::element( 'textarea', array( 'name' => 'pages', 'cols' => 40, 'rows' => 10 ), $page, false );
00159 $form .= '<br />';
00160
00161 if( $wgExportAllowHistory ) {
00162 $form .= Xml::checkLabel( wfMsg( 'exportcuronly' ), 'curonly', 'curonly', true ) . '<br />';
00163 } else {
00164 $wgOut->addHTML( wfMsgExt( 'exportnohistory', 'parse' ) );
00165 }
00166 $form .= Xml::checkLabel( wfMsg( 'export-templates' ), 'templates', 'wpExportTemplates', false ) . '<br />';
00167 if( $wgExportMaxLinkDepth || $this->userCanOverrideExportDepth() ) {
00168 $form .= Xml::inputLabel( wfMsg( 'export-pagelinks' ), 'pagelink-depth', 'pagelink-depth', 20, 0 ) . '<br />';
00169 }
00170
00171
00172 $form .= Xml::checkLabel( wfMsg( 'export-download' ), 'wpDownload', 'wpDownload', true ) . '<br />';
00173
00174 $form .= Xml::submitButton( wfMsg( 'export-submit' ), array( 'accesskey' => 's' ) );
00175 $form .= Xml::closeElement( 'form' );
00176 $wgOut->addHTML( $form );
00177 }
00178
00179 private function userCanOverrideExportDepth() {
00180 global $wgUser;
00181
00182 return $wgUser->isAllowed( 'override-export-depth' );
00183 }
00184
00190 private function doExport( $page, $history, $list_authors ) {
00191 global $wgExportMaxHistory;
00192
00193
00194 $inputPages = array_filter( explode( "\n", $page ), array( $this, 'filterPage' ) );
00195 $pageSet = array_flip( $inputPages );
00196
00197 if( $this->templates ) {
00198 $pageSet = $this->getTemplates( $inputPages, $pageSet );
00199 }
00200
00201 if( $linkDepth = $this->pageLinkDepth ) {
00202 $pageSet = $this->getPageLinks( $inputPages, $pageSet, $linkDepth );
00203 }
00204
00205
00206
00207
00208
00209
00210
00211
00212 $pages = array_keys( $pageSet );
00213
00214
00215 if( $history == WikiExporter::CURRENT ) {
00216 $lb = false;
00217 $db = wfGetDB( DB_SLAVE );
00218 $buffer = WikiExporter::BUFFER;
00219 } else {
00220
00221 $lb = wfGetLBFactory()->newMainLB();
00222 $db = $lb->getConnection( DB_SLAVE );
00223 $buffer = WikiExporter::STREAM;
00224
00225
00226 wfSuppressWarnings();
00227 set_time_limit(0);
00228 wfRestoreWarnings();
00229 }
00230 $exporter = new WikiExporter( $db, $history, $buffer );
00231 $exporter->list_authors = $list_authors;
00232 $exporter->openStream();
00233 foreach( $pages as $page ) {
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246 #Bug 8824: Only export pages the user can read
00247 $title = Title::newFromText( $page );
00248 if( is_null( $title ) ) continue; #TODO: perhaps output an <error> tag or something.
00249 if( !$title->userCanRead() ) continue; #TODO: perhaps output an <error> tag or something.
00250
00251 $exporter->pageByTitle( $title );
00252 }
00253
00254 $exporter->closeStream();
00255 if( $lb ) {
00256 $lb->closeAll();
00257 }
00258 }
00259
00260
00261 private function getPagesFromCategory( $title ) {
00262 global $wgContLang;
00263
00264 $name = $title->getDBkey();
00265
00266 $dbr = wfGetDB( DB_SLAVE );
00267 $res = $dbr->select( array('page', 'categorylinks' ),
00268 array( 'page_namespace', 'page_title' ),
00269 array('cl_from=page_id', 'cl_to' => $name ),
00270 __METHOD__, array('LIMIT' => '5000'));
00271
00272 $pages = array();
00273 while ( $row = $dbr->fetchObject( $res ) ) {
00274 $n = $row->page_title;
00275 if ($row->page_namespace) {
00276 $ns = $wgContLang->getNsText( $row->page_namespace );
00277 $n = $ns . ':' . $n;
00278 }
00279
00280 $pages[] = $n;
00281 }
00282 $dbr->freeResult($res);
00283
00284 return $pages;
00285 }
00286
00287 private function getPagesFromNamespace( $nsindex ) {
00288 global $wgContLang;
00289
00290 $dbr = wfGetDB( DB_SLAVE );
00291 $res = $dbr->select( 'page', array('page_namespace', 'page_title'),
00292 array('page_namespace' => $nsindex),
00293 __METHOD__, array('LIMIT' => '5000') );
00294
00295 $pages = array();
00296 while ( $row = $dbr->fetchObject( $res ) ) {
00297 $n = $row->page_title;
00298 if ($row->page_namespace) {
00299 $ns = $wgContLang->getNsText( $row->page_namespace );
00300 $n = $ns . ':' . $n;
00301 }
00302
00303 $pages[] = $n;
00304 }
00305 $dbr->freeResult($res);
00306
00307 return $pages;
00308 }
00315 private function getTemplates( $inputPages, $pageSet ) {
00316 return $this->getLinks( $inputPages, $pageSet,
00317 'templatelinks',
00318 array( 'tl_namespace AS namespace', 'tl_title AS title' ),
00319 array( 'page_id=tl_from' ) );
00320 }
00321
00325 private function validateLinkDepth( $depth ) {
00326 global $wgExportMaxLinkDepth, $wgExportMaxLinkDepthLimit;
00327 if( $depth < 0 ) {
00328 return 0;
00329 }
00330 if ( !$this->userCanOverrideExportDepth() ) {
00331 if( $depth > $wgExportMaxLinkDepth ) {
00332 return $wgExportMaxLinkDepth;
00333 }
00334 }
00335
00336
00337
00338
00339
00340 return intval( min( $depth, 5 ) );
00341 }
00342
00344 private function getPageLinks( $inputPages, $pageSet, $depth ) {
00345 for( $depth=$depth; $depth>0; --$depth ) {
00346 $pageSet = $this->getLinks( $inputPages, $pageSet, 'pagelinks',
00347 array( 'pl_namespace AS namespace', 'pl_title AS title' ),
00348 array( 'page_id=pl_from' ) );
00349 $inputPages = array_keys( $pageSet );
00350 }
00351 return $pageSet;
00352 }
00353
00360 private function getImages( $inputPages, $pageSet ) {
00361 return $this->getLinks( $inputPages, $pageSet,
00362 'imagelinks',
00363 array( NS_FILE . ' AS namespace', 'il_to AS title' ),
00364 array( 'page_id=il_from' ) );
00365 }
00366
00371 private function getLinks( $inputPages, $pageSet, $table, $fields, $join ) {
00372 $dbr = wfGetDB( DB_SLAVE );
00373 foreach( $inputPages as $page ) {
00374 $title = Title::newFromText( $page );
00375 if( $title ) {
00376 $pageSet[$title->getPrefixedText()] = true;
00379 $result = $dbr->select(
00380 array( 'page', $table ),
00381 $fields,
00382 array_merge( $join,
00383 array(
00384 'page_namespace' => $title->getNamespace(),
00385 'page_title' => $title->getDBKey() ) ),
00386 __METHOD__ );
00387 foreach( $result as $row ) {
00388 $template = Title::makeTitle( $row->namespace, $row->title );
00389 $pageSet[$template->getPrefixedText()] = true;
00390 }
00391 }
00392 }
00393 return $pageSet;
00394 }
00395
00399 private function filterPage( $page ) {
00400 return $page !== '' && $page !== null;
00401 }
00402 }
00403