r40994 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r40993‎ | r40994 | r40995 >
Date:16:41, 18 September 2008
Author:aaron
Status:old
Tags:
Comment:
* Move Import stuff to it's own file like Export
* Force logging index
* Some code style tweaks
* Set visibility in some places
Modified paths:
  • /trunk/phase3/includes/AutoLoader.php (modified) (history)
  • /trunk/phase3/includes/Export.php (modified) (history)
  • /trunk/phase3/includes/Import.php (added) (history)
  • /trunk/phase3/includes/specials/SpecialImport.php (modified) (history)

Diff [purge]

Index: trunk/phase3/includes/Export.php
@@ -72,16 +72,16 @@
7373 *
7474 * @param $sink mixed
7575 */
76 - function setOutputSink( &$sink ) {
 76+ public function setOutputSink( &$sink ) {
7777 $this->sink =& $sink;
7878 }
7979
80 - function openStream() {
 80+ public function openStream() {
8181 $output = $this->writer->openStream();
8282 $this->sink->writeOpenStream( $output );
8383 }
8484
85 - function closeStream() {
 85+ public function closeStream() {
8686 $output = $this->writer->closeStream();
8787 $this->sink->writeCloseStream( $output );
8888 }
@@ -91,7 +91,7 @@
9292 * in the database, either including complete history or only
9393 * the most recent version.
9494 */
95 - function allPages() {
 95+ public function allPages() {
9696 return $this->dumpFrom( '' );
9797 }
9898
@@ -102,36 +102,24 @@
103103 * @param $end Int: Exclusive upper limit (this id is not included)
104104 * If 0, no upper limit.
105105 */
106 - function pagesByRange( $start, $end ) {
 106+ public function pagesByRange( $start, $end ) {
107107 $condition = 'page_id >= ' . intval( $start );
108108 if( $end ) {
109109 $condition .= ' AND page_id < ' . intval( $end );
110110 }
111111 return $this->dumpFrom( $condition );
112112 }
113 -
114 - function allLogs() {
115 - return $this->dumpFrom( '' );
116 - }
117 -
118 - function logsByRange( $start, $end ) {
119 - $condition = 'log_id >= ' . intval( $start );
120 - if( $end ) {
121 - $condition .= ' AND log_id < ' . intval( $end );
122 - }
123 - return $this->dumpFrom( $condition );
124 - }
125113
126114 /**
127115 * @param $title Title
128116 */
129 - function pageByTitle( $title ) {
 117+ public function pageByTitle( $title ) {
130118 return $this->dumpFrom(
131119 'page_namespace=' . $title->getNamespace() .
132120 ' AND page_title=' . $this->db->addQuotes( $title->getDBkey() ) );
133121 }
134122
135 - function pageByName( $name ) {
 123+ public function pageByName( $name ) {
136124 $title = Title::newFromText( $name );
137125 if( is_null( $title ) ) {
138126 return new WikiError( "Can't export invalid title" );
@@ -140,26 +128,36 @@
141129 }
142130 }
143131
144 - function pagesByName( $names ) {
 132+ public function pagesByName( $names ) {
145133 foreach( $names as $name ) {
146134 $this->pageByName( $name );
147135 }
148136 }
149137
 138+ public function allLogs() {
 139+ return $this->dumpFrom( '' );
 140+ }
150141
151 - // -------------------- private implementation below --------------------
 142+ public function logsByRange( $start, $end ) {
 143+ $condition = 'log_id >= ' . intval( $start );
 144+ if( $end ) {
 145+ $condition .= ' AND log_id < ' . intval( $end );
 146+ }
 147+ return $this->dumpFrom( $condition );
 148+ }
152149
153150 # Generates the distinct list of authors of an article
154151 # Not called by default (depends on $this->list_authors)
155152 # Can be set by Special:Export when not exporting whole history
156 - function do_list_authors ( $page , $revision , $cond ) {
 153+ protected function do_list_authors( $page , $revision , $cond ) {
157154 $fname = "do_list_authors" ;
158155 wfProfileIn( $fname );
159156 $this->author_list = "<contributors>";
160157 //rev_deleted
161158 $nothidden = '(rev_deleted & '.Revision::DELETED_USER.') = 0';
162159
163 - $sql = "SELECT DISTINCT rev_user_text,rev_user FROM {$page},{$revision} WHERE page_id=rev_page AND $nothidden AND " . $cond ;
 160+ $sql = "SELECT DISTINCT rev_user_text,rev_user FROM {$page},{$revision}
 161+ WHERE page_id=rev_page AND $nothidden AND " . $cond ;
164162 $result = $this->db->query( $sql, $fname );
165163 $resultset = $this->db->resultObject( $result );
166164 while( $row = $resultset->fetchObject() ) {
@@ -176,7 +174,7 @@
177175 $this->author_list .= "</contributors>";
178176 }
179177
180 - function dumpFrom( $cond = '' ) {
 178+ protected function dumpFrom( $cond = '' ) {
181179 $fname = 'WikiExporter::dumpFrom';
182180 wfProfileIn( $fname );
183181
@@ -190,91 +188,87 @@
191189 '*',
192190 $where,
193191 $fname,
194 - array( 'ORDER BY' => 'log_id')
 192+ array( 'ORDER BY' => 'log_id', 'USE INDEX' => array('logging' => 'PRIMARY') )
195193 );
196194 $wrapper = $this->db->resultObject( $result );
197195 $this->outputLogStream( $wrapper );
198 - wfProfileOut( $fname );
199 - return;
200 - }
201196 # For page dumps...
202 - $page = $this->db->tableName( 'page' );
203 - $revision = $this->db->tableName( 'revision' );
204 - $text = $this->db->tableName( 'text' );
 197+ } else {
 198+ list($page,$revision,$text) = $this->db->tableNamesN('page','revision','text');
205199
206 - $order = 'ORDER BY page_id';
207 - $limit = '';
 200+ $order = 'ORDER BY page_id';
 201+ $limit = '';
208202
209 - if( $this->history == WikiExporter::FULL ) {
210 - $join = 'page_id=rev_page';
211 - } elseif( $this->history == WikiExporter::CURRENT ) {
212 - if ( $this->list_authors && $cond != '' ) { // List authors, if so desired
213 - $this->do_list_authors ( $page , $revision , $cond );
214 - }
215 - $join = 'page_id=rev_page AND page_latest=rev_id';
216 - } elseif ( is_array( $this->history ) ) {
217 - $join = 'page_id=rev_page';
218 - if ( $this->history['dir'] == 'asc' ) {
219 - $op = '>';
220 - $order .= ', rev_timestamp';
 203+ if( $this->history == WikiExporter::FULL ) {
 204+ $join = 'page_id=rev_page';
 205+ } elseif( $this->history == WikiExporter::CURRENT ) {
 206+ if ( $this->list_authors && $cond != '' ) { // List authors, if so desired
 207+ $this->do_list_authors ( $page , $revision , $cond );
 208+ }
 209+ $join = 'page_id=rev_page AND page_latest=rev_id';
 210+ } elseif ( is_array( $this->history ) ) {
 211+ $join = 'page_id=rev_page';
 212+ if ( $this->history['dir'] == 'asc' ) {
 213+ $op = '>';
 214+ $order .= ', rev_timestamp';
 215+ } else {
 216+ $op = '<';
 217+ $order .= ', rev_timestamp DESC';
 218+ }
 219+ if ( !empty( $this->history['offset'] ) ) {
 220+ $join .= " AND rev_timestamp $op " . $this->db->addQuotes(
 221+ $this->db->timestamp( $this->history['offset'] ) );
 222+ }
 223+ if ( !empty( $this->history['limit'] ) ) {
 224+ $limitNum = intval( $this->history['limit'] );
 225+ if ( $limitNum > 0 ) {
 226+ $limit = "LIMIT $limitNum";
 227+ }
 228+ }
221229 } else {
222 - $op = '<';
223 - $order .= ', rev_timestamp DESC';
 230+ wfProfileOut( $fname );
 231+ return new WikiError( "$fname given invalid history dump type." );
224232 }
225 - if ( !empty( $this->history['offset'] ) ) {
226 - $join .= " AND rev_timestamp $op " . $this->db->addQuotes(
227 - $this->db->timestamp( $this->history['offset'] ) );
 233+ $where = ( $cond == '' ) ? '' : "$cond AND";
 234+
 235+ if( $this->buffer == WikiExporter::STREAM ) {
 236+ $prev = $this->db->bufferResults( false );
228237 }
229 - if ( !empty( $this->history['limit'] ) ) {
230 - $limitNum = intval( $this->history['limit'] );
231 - if ( $limitNum > 0 ) {
232 - $limit = "LIMIT $limitNum";
233 - }
 238+ if( $cond == '' ) {
 239+ // Optimization hack for full-database dump
 240+ $revindex = $pageindex = $this->db->useIndexClause("PRIMARY");
 241+ $straight = ' /*! STRAIGHT_JOIN */ ';
 242+ } else {
 243+ $pageindex = '';
 244+ $revindex = '';
 245+ $straight = '';
234246 }
235 - } else {
236 - wfProfileOut( $fname );
237 - return new WikiError( "$fname given invalid history dump type." );
238 - }
239 - $where = ( $cond == '' ) ? '' : "$cond AND";
240 -
241 - if( $this->buffer == WikiExporter::STREAM ) {
242 - $prev = $this->db->bufferResults( false );
243 - }
244 - if( $cond == '' ) {
245 - // Optimization hack for full-database dump
246 - $revindex = $pageindex = $this->db->useIndexClause("PRIMARY");
247 - $straight = ' /*! STRAIGHT_JOIN */ ';
248 - } else {
249 - $pageindex = '';
250 - $revindex = '';
251 - $straight = '';
252 - }
253 - if( $this->text == WikiExporter::STUB ) {
254 - $sql = "SELECT $straight * FROM
 247+ if( $this->text == WikiExporter::STUB ) {
 248+ $sql = "SELECT $straight * FROM
255249 $page $pageindex,
256250 $revision $revindex
257251 WHERE $where $join
258252 $order $limit";
259 - } else {
260 - $sql = "SELECT $straight * FROM
 253+ } else {
 254+ $sql = "SELECT $straight * FROM
261255 $page $pageindex,
262256 $revision $revindex,
263257 $text
264258 WHERE $where $join AND rev_text_id=old_id
265259 $order $limit";
266 - }
267 - $result = $this->db->query( $sql, $fname );
268 - $wrapper = $this->db->resultObject( $result );
269 - $this->outputPageStream( $wrapper );
270 -
271 - if ( $this->list_authors ) {
 260+ }
 261+ $result = $this->db->query( $sql, $fname );
 262+ $wrapper = $this->db->resultObject( $result );
272263 $this->outputPageStream( $wrapper );
273 - }
274264
275 - if( $this->buffer == WikiExporter::STREAM ) {
276 - $this->db->bufferResults( $prev );
277 - }
 265+ if ( $this->list_authors ) {
 266+ $this->outputPageStream( $wrapper );
 267+ }
278268
 269+ if( $this->buffer == WikiExporter::STREAM ) {
 270+ $this->db->bufferResults( $prev );
 271+ }
 272+ }
279273 wfProfileOut( $fname );
280274 }
281275
@@ -289,9 +283,8 @@
290284 * blob storage types will make queries to pull source data.
291285 *
292286 * @param $resultset ResultWrapper
293 - * @access private
294287 */
295 - function outputPageStream( $resultset ) {
 288+ protected function outputPageStream( $resultset ) {
296289 $last = null;
297290 while( $row = $resultset->fetchObject() ) {
298291 if( is_null( $last ) ||
@@ -324,7 +317,7 @@
325318 $resultset->free();
326319 }
327320
328 - function outputLogStream( $resultset ) {
 321+ protected function outputLogStream( $resultset ) {
329322 while( $row = $resultset->fetchObject() ) {
330323 $output = $this->writer->writeLogItem( $row );
331324 $this->sink->writeLogItem( $row, $output );
Index: trunk/phase3/includes/AutoLoader.php
@@ -441,9 +441,9 @@
442442 'IPBlockForm' => 'includes/specials/SpecialBlockip.php',
443443 'IPBlocklistPager' => 'includes/specials/SpecialIpblocklist.php',
444444 'IPUnblockForm' => 'includes/specials/SpecialIpblocklist.php',
445 - 'ImportReporter' => 'includes/specials/SpecialImport.php',
446 - 'ImportStreamSource' => 'includes/specials/SpecialImport.php',
447 - 'ImportStringSource' => 'includes/specials/SpecialImport.php',
 445+ 'ImportReporter' => 'includes/Import.php',
 446+ 'ImportStreamSource' => 'includes/Import.php',
 447+ 'ImportStringSource' => 'includes/Import.php',
448448 'LinkSearchPage' => 'includes/specials/SpecialLinkSearch.php',
449449 'ListredirectsPage' => 'includes/specials/SpecialListredirects.php',
450450 'LoginForm' => 'includes/specials/SpecialUserlogin.php',
@@ -492,8 +492,8 @@
493493 'WantedFilesPage' => 'includes/specials/SpecialWantedfiles.php',
494494 'WantedPagesPage' => 'includes/specials/SpecialWantedpages.php',
495495 'WhatLinksHerePage' => 'includes/specials/SpecialWhatlinkshere.php',
496 - 'WikiImporter' => 'includes/specials/SpecialImport.php',
497 - 'WikiRevision' => 'includes/specials/SpecialImport.php',
 496+ 'WikiImporter' => 'includes/Import.php',
 497+ 'WikiRevision' => 'includes/Import.php',
498498 'WithoutInterwikiPage' => 'includes/specials/SpecialWithoutinterwiki.php',
499499
500500 # includes/templates
Index: trunk/phase3/includes/specials/SpecialImport.php
@@ -168,988 +168,3 @@
169169 );
170170 }
171171 }
172 -
173 -/**
174 - * Reporting callback
175 - * @ingroup SpecialPage
176 - */
177 -class ImportReporter {
178 - function __construct( $importer, $upload, $interwiki ) {
179 - $importer->setPageOutCallback( array( $this, 'reportPage' ) );
180 - $this->mPageCount = 0;
181 - $this->mIsUpload = $upload;
182 - $this->mInterwiki = $interwiki;
183 - }
184 -
185 - function open() {
186 - global $wgOut;
187 - $wgOut->addHtml( "<ul>\n" );
188 - }
189 -
190 - function reportPage( $title, $origTitle, $revisionCount, $successCount ) {
191 - global $wgOut, $wgUser, $wgLang, $wgContLang;
192 -
193 - $skin = $wgUser->getSkin();
194 -
195 - $this->mPageCount++;
196 -
197 - $localCount = $wgLang->formatNum( $successCount );
198 - $contentCount = $wgContLang->formatNum( $successCount );
199 -
200 - if( $successCount > 0 ) {
201 - $wgOut->addHtml( "<li>" . $skin->makeKnownLinkObj( $title ) . " " .
202 - wfMsgExt( 'import-revision-count', array( 'parsemag', 'escape' ), $localCount ) .
203 - "</li>\n"
204 - );
205 -
206 - $log = new LogPage( 'import' );
207 - if( $this->mIsUpload ) {
208 - $detail = wfMsgExt( 'import-logentry-upload-detail', array( 'content', 'parsemag' ),
209 - $contentCount );
210 - $log->addEntry( 'upload', $title, $detail );
211 - } else {
212 - $interwiki = '[[:' . $this->mInterwiki . ':' .
213 - $origTitle->getPrefixedText() . ']]';
214 - $detail = wfMsgExt( 'import-logentry-interwiki-detail', array( 'content', 'parsemag' ),
215 - $contentCount, $interwiki );
216 - $log->addEntry( 'interwiki', $title, $detail );
217 - }
218 -
219 - $comment = $detail; // quick
220 - $dbw = wfGetDB( DB_MASTER );
221 - $latest = $title->getLatestRevID();
222 - $nullRevision = Revision::newNullRevision( $dbw, $title->getArticleId(), $comment, true );
223 - $nullRevision->insertOn( $dbw );
224 - $article = new Article( $title );
225 - # Update page record
226 - $article->updateRevisionOn( $dbw, $nullRevision );
227 - wfRunHooks( 'NewRevisionFromEditComplete', array($article, $nullRevision, $latest) );
228 - } else {
229 - $wgOut->addHtml( '<li>' . wfMsgHtml( 'import-nonewrevisions' ) . '</li>' );
230 - }
231 - }
232 -
233 - function close() {
234 - global $wgOut;
235 - if( $this->mPageCount == 0 ) {
236 - $wgOut->addHtml( "</ul>\n" );
237 - return new WikiErrorMsg( "importnopages" );
238 - }
239 - $wgOut->addHtml( "</ul>\n" );
240 -
241 - return $this->mPageCount;
242 - }
243 -}
244 -
245 -/**
246 - *
247 - * @ingroup SpecialPage
248 - */
249 -class WikiRevision {
250 - var $title = null;
251 - var $id = 0;
252 - var $timestamp = "20010115000000";
253 - var $user = 0;
254 - var $user_text = "";
255 - var $text = "";
256 - var $comment = "";
257 - var $minor = false;
258 -
259 - function setTitle( $title ) {
260 - if( is_object( $title ) ) {
261 - $this->title = $title;
262 - } elseif( is_null( $title ) ) {
263 - throw new MWException( "WikiRevision given a null title in import. You may need to adjust \$wgLegalTitleChars." );
264 - } else {
265 - throw new MWException( "WikiRevision given non-object title in import." );
266 - }
267 - }
268 -
269 - function setID( $id ) {
270 - $this->id = $id;
271 - }
272 -
273 - function setTimestamp( $ts ) {
274 - # 2003-08-05T18:30:02Z
275 - $this->timestamp = wfTimestamp( TS_MW, $ts );
276 - }
277 -
278 - function setUsername( $user ) {
279 - $this->user_text = $user;
280 - }
281 -
282 - function setUserIP( $ip ) {
283 - $this->user_text = $ip;
284 - }
285 -
286 - function setText( $text ) {
287 - $this->text = $text;
288 - }
289 -
290 - function setComment( $text ) {
291 - $this->comment = $text;
292 - }
293 -
294 - function setMinor( $minor ) {
295 - $this->minor = (bool)$minor;
296 - }
297 -
298 - function setSrc( $src ) {
299 - $this->src = $src;
300 - }
301 -
302 - function setFilename( $filename ) {
303 - $this->filename = $filename;
304 - }
305 -
306 - function setSize( $size ) {
307 - $this->size = intval( $size );
308 - }
309 -
310 - function getTitle() {
311 - return $this->title;
312 - }
313 -
314 - function getID() {
315 - return $this->id;
316 - }
317 -
318 - function getTimestamp() {
319 - return $this->timestamp;
320 - }
321 -
322 - function getUser() {
323 - return $this->user_text;
324 - }
325 -
326 - function getText() {
327 - return $this->text;
328 - }
329 -
330 - function getComment() {
331 - return $this->comment;
332 - }
333 -
334 - function getMinor() {
335 - return $this->minor;
336 - }
337 -
338 - function getSrc() {
339 - return $this->src;
340 - }
341 -
342 - function getFilename() {
343 - return $this->filename;
344 - }
345 -
346 - function getSize() {
347 - return $this->size;
348 - }
349 -
350 - function importOldRevision() {
351 - $dbw = wfGetDB( DB_MASTER );
352 -
353 - # Sneak a single revision into place
354 - $user = User::newFromName( $this->getUser() );
355 - if( $user ) {
356 - $userId = intval( $user->getId() );
357 - $userText = $user->getName();
358 - } else {
359 - $userId = 0;
360 - $userText = $this->getUser();
361 - }
362 -
363 - // avoid memory leak...?
364 - $linkCache = LinkCache::singleton();
365 - $linkCache->clear();
366 -
367 - $article = new Article( $this->title );
368 - $pageId = $article->getId();
369 - if( $pageId == 0 ) {
370 - # must create the page...
371 - $pageId = $article->insertOn( $dbw );
372 - $created = true;
373 - } else {
374 - $created = false;
375 -
376 - $prior = Revision::loadFromTimestamp( $dbw, $this->title, $this->timestamp );
377 - if( !is_null( $prior ) ) {
378 - // FIXME: this could fail slightly for multiple matches :P
379 - wfDebug( __METHOD__ . ": skipping existing revision for [[" .
380 - $this->title->getPrefixedText() . "]], timestamp " .
381 - $this->timestamp . "\n" );
382 - return false;
383 - }
384 - }
385 -
386 - # FIXME: Use original rev_id optionally
387 - # FIXME: blah blah blah
388 -
389 - #if( $numrows > 0 ) {
390 - # return wfMsg( "importhistoryconflict" );
391 - #}
392 -
393 - # Insert the row
394 - $revision = new Revision( array(
395 - 'page' => $pageId,
396 - 'text' => $this->getText(),
397 - 'comment' => $this->getComment(),
398 - 'user' => $userId,
399 - 'user_text' => $userText,
400 - 'timestamp' => $this->timestamp,
401 - 'minor_edit' => $this->minor,
402 - ) );
403 - $revId = $revision->insertOn( $dbw );
404 - $changed = $article->updateIfNewerOn( $dbw, $revision );
405 -
406 - if( $created ) {
407 - wfDebug( __METHOD__ . ": running onArticleCreate\n" );
408 - Article::onArticleCreate( $this->title );
409 -
410 - wfDebug( __METHOD__ . ": running create updates\n" );
411 - $article->createUpdates( $revision );
412 -
413 - } elseif( $changed ) {
414 - wfDebug( __METHOD__ . ": running onArticleEdit\n" );
415 - Article::onArticleEdit( $this->title );
416 -
417 - wfDebug( __METHOD__ . ": running edit updates\n" );
418 - $article->editUpdates(
419 - $this->getText(),
420 - $this->getComment(),
421 - $this->minor,
422 - $this->timestamp,
423 - $revId );
424 - }
425 -
426 - return true;
427 - }
428 -
429 - function importUpload() {
430 - wfDebug( __METHOD__ . ": STUB\n" );
431 -
432 - /**
433 - // from file revert...
434 - $source = $this->file->getArchiveVirtualUrl( $this->oldimage );
435 - $comment = $wgRequest->getText( 'wpComment' );
436 - // TODO: Preserve file properties from database instead of reloading from file
437 - $status = $this->file->upload( $source, $comment, $comment );
438 - if( $status->isGood() ) {
439 - */
440 -
441 - /**
442 - // from file upload...
443 - $this->mLocalFile = wfLocalFile( $nt );
444 - $this->mDestName = $this->mLocalFile->getName();
445 - //....
446 - $status = $this->mLocalFile->upload( $this->mTempPath, $this->mComment, $pageText,
447 - File::DELETE_SOURCE, $this->mFileProps );
448 - if ( !$status->isGood() ) {
449 - $resultDetails = array( 'internal' => $status->getWikiText() );
450 - */
451 -
452 - // @fixme upload() uses $wgUser, which is wrong here
453 - // it may also create a page without our desire, also wrong potentially.
454 - // and, it will record a *current* upload, but we might want an archive version here
455 -
456 - $file = wfLocalFile( $this->getTitle() );
457 - if( !$file ) {
458 - var_dump( $file );
459 - wfDebug( "IMPORT: Bad file. :(\n" );
460 - return false;
461 - }
462 -
463 - $source = $this->downloadSource();
464 - if( !$source ) {
465 - wfDebug( "IMPORT: Could not fetch remote file. :(\n" );
466 - return false;
467 - }
468 -
469 - $status = $file->upload( $source,
470 - $this->getComment(),
471 - $this->getComment(), // Initial page, if none present...
472 - File::DELETE_SOURCE,
473 - false, // props...
474 - $this->getTimestamp() );
475 -
476 - if( $status->isGood() ) {
477 - // yay?
478 - wfDebug( "IMPORT: is ok?\n" );
479 - return true;
480 - }
481 -
482 - wfDebug( "IMPORT: is bad? " . $status->getXml() . "\n" );
483 - return false;
484 -
485 - }
486 -
487 - function downloadSource() {
488 - global $wgEnableUploads;
489 - if( !$wgEnableUploads ) {
490 - return false;
491 - }
492 -
493 - $tempo = tempnam( wfTempDir(), 'download' );
494 - $f = fopen( $tempo, 'wb' );
495 - if( !$f ) {
496 - wfDebug( "IMPORT: couldn't write to temp file $tempo\n" );
497 - return false;
498 - }
499 -
500 - // @fixme!
501 - $src = $this->getSrc();
502 - $data = Http::get( $src );
503 - if( !$data ) {
504 - wfDebug( "IMPORT: couldn't fetch source $src\n" );
505 - fclose( $f );
506 - unlink( $tempo );
507 - return false;
508 - }
509 -
510 - fwrite( $f, $data );
511 - fclose( $f );
512 -
513 - return $tempo;
514 - }
515 -
516 -}
517 -
518 -/**
519 - * implements Special:Import
520 - * @ingroup SpecialPage
521 - */
522 -class WikiImporter {
523 - var $mDebug = false;
524 - var $mSource = null;
525 - var $mPageCallback = null;
526 - var $mPageOutCallback = null;
527 - var $mRevisionCallback = null;
528 - var $mUploadCallback = null;
529 - var $mTargetNamespace = null;
530 - var $lastfield;
531 - var $tagStack = array();
532 -
533 - function __construct( $source ) {
534 - $this->setRevisionCallback( array( $this, "importRevision" ) );
535 - $this->setUploadCallback( array( $this, "importUpload" ) );
536 - $this->mSource = $source;
537 - }
538 -
539 - function throwXmlError( $err ) {
540 - $this->debug( "FAILURE: $err" );
541 - wfDebug( "WikiImporter XML error: $err\n" );
542 - }
543 -
544 - # --------------
545 -
546 - function doImport() {
547 - if( empty( $this->mSource ) ) {
548 - return new WikiErrorMsg( "importnotext" );
549 - }
550 -
551 - $parser = xml_parser_create( "UTF-8" );
552 -
553 - # case folding violates XML standard, turn it off
554 - xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
555 -
556 - xml_set_object( $parser, $this );
557 - xml_set_element_handler( $parser, "in_start", "" );
558 -
559 - $offset = 0; // for context extraction on error reporting
560 - do {
561 - $chunk = $this->mSource->readChunk();
562 - if( !xml_parse( $parser, $chunk, $this->mSource->atEnd() ) ) {
563 - wfDebug( "WikiImporter::doImport encountered XML parsing error\n" );
564 - return new WikiXmlError( $parser, wfMsgHtml( 'import-parse-failure' ), $chunk, $offset );
565 - }
566 - $offset += strlen( $chunk );
567 - } while( $chunk !== false && !$this->mSource->atEnd() );
568 - xml_parser_free( $parser );
569 -
570 - return true;
571 - }
572 -
573 - function debug( $data ) {
574 - if( $this->mDebug ) {
575 - wfDebug( "IMPORT: $data\n" );
576 - }
577 - }
578 -
579 - function notice( $data ) {
580 - global $wgCommandLineMode;
581 - if( $wgCommandLineMode ) {
582 - print "$data\n";
583 - } else {
584 - global $wgOut;
585 - $wgOut->addHTML( "<li>" . htmlspecialchars( $data ) . "</li>\n" );
586 - }
587 - }
588 -
589 - /**
590 - * Set debug mode...
591 - */
592 - function setDebug( $debug ) {
593 - $this->mDebug = $debug;
594 - }
595 -
596 - /**
597 - * Sets the action to perform as each new page in the stream is reached.
598 - * @param $callback callback
599 - * @return callback
600 - */
601 - function setPageCallback( $callback ) {
602 - $previous = $this->mPageCallback;
603 - $this->mPageCallback = $callback;
604 - return $previous;
605 - }
606 -
607 - /**
608 - * Sets the action to perform as each page in the stream is completed.
609 - * Callback accepts the page title (as a Title object), a second object
610 - * with the original title form (in case it's been overridden into a
611 - * local namespace), and a count of revisions.
612 - *
613 - * @param $callback callback
614 - * @return callback
615 - */
616 - function setPageOutCallback( $callback ) {
617 - $previous = $this->mPageOutCallback;
618 - $this->mPageOutCallback = $callback;
619 - return $previous;
620 - }
621 -
622 - /**
623 - * Sets the action to perform as each page revision is reached.
624 - * @param $callback callback
625 - * @return callback
626 - */
627 - function setRevisionCallback( $callback ) {
628 - $previous = $this->mRevisionCallback;
629 - $this->mRevisionCallback = $callback;
630 - return $previous;
631 - }
632 -
633 - /**
634 - * Sets the action to perform as each file upload version is reached.
635 - * @param $callback callback
636 - * @return callback
637 - */
638 - function setUploadCallback( $callback ) {
639 - $previous = $this->mUploadCallback;
640 - $this->mUploadCallback = $callback;
641 - return $previous;
642 - }
643 -
644 - /**
645 - * Set a target namespace to override the defaults
646 - */
647 - function setTargetNamespace( $namespace ) {
648 - if( is_null( $namespace ) ) {
649 - // Don't override namespaces
650 - $this->mTargetNamespace = null;
651 - } elseif( $namespace >= 0 ) {
652 - // FIXME: Check for validity
653 - $this->mTargetNamespace = intval( $namespace );
654 - } else {
655 - return false;
656 - }
657 - }
658 -
659 - /**
660 - * Default per-revision callback, performs the import.
661 - * @param $revision WikiRevision
662 - * @private
663 - */
664 - function importRevision( $revision ) {
665 - $dbw = wfGetDB( DB_MASTER );
666 - return $dbw->deadlockLoop( array( $revision, 'importOldRevision' ) );
667 - }
668 -
669 - /**
670 - * Dummy for now...
671 - */
672 - function importUpload( $revision ) {
673 - //$dbw = wfGetDB( DB_MASTER );
674 - //return $dbw->deadlockLoop( array( $revision, 'importUpload' ) );
675 - return false;
676 - }
677 -
678 - /**
679 - * Alternate per-revision callback, for debugging.
680 - * @param $revision WikiRevision
681 - * @private
682 - */
683 - function debugRevisionHandler( &$revision ) {
684 - $this->debug( "Got revision:" );
685 - if( is_object( $revision->title ) ) {
686 - $this->debug( "-- Title: " . $revision->title->getPrefixedText() );
687 - } else {
688 - $this->debug( "-- Title: <invalid>" );
689 - }
690 - $this->debug( "-- User: " . $revision->user_text );
691 - $this->debug( "-- Timestamp: " . $revision->timestamp );
692 - $this->debug( "-- Comment: " . $revision->comment );
693 - $this->debug( "-- Text: " . $revision->text );
694 - }
695 -
696 - /**
697 - * Notify the callback function when a new <page> is reached.
698 - * @param $title Title
699 - * @private
700 - */
701 - function pageCallback( $title ) {
702 - if( is_callable( $this->mPageCallback ) ) {
703 - call_user_func( $this->mPageCallback, $title );
704 - }
705 - }
706 -
707 - /**
708 - * Notify the callback function when a </page> is closed.
709 - * @param $title Title
710 - * @param $origTitle Title
711 - * @param $revisionCount int
712 - * @param $successCount Int: number of revisions for which callback returned true
713 - * @private
714 - */
715 - function pageOutCallback( $title, $origTitle, $revisionCount, $successCount ) {
716 - if( is_callable( $this->mPageOutCallback ) ) {
717 - call_user_func( $this->mPageOutCallback, $title, $origTitle,
718 - $revisionCount, $successCount );
719 - }
720 - }
721 -
722 -
723 - # XML parser callbacks from here out -- beware!
724 - function donothing( $parser, $x, $y="" ) {
725 - #$this->debug( "donothing" );
726 - }
727 -
728 - function in_start( $parser, $name, $attribs ) {
729 - $this->debug( "in_start $name" );
730 - if( $name != "mediawiki" ) {
731 - return $this->throwXMLerror( "Expected <mediawiki>, got <$name>" );
732 - }
733 - xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
734 - }
735 -
736 - function in_mediawiki( $parser, $name, $attribs ) {
737 - $this->debug( "in_mediawiki $name" );
738 - if( $name == 'siteinfo' ) {
739 - xml_set_element_handler( $parser, "in_siteinfo", "out_siteinfo" );
740 - } elseif( $name == 'page' ) {
741 - $this->push( $name );
742 - $this->workRevisionCount = 0;
743 - $this->workSuccessCount = 0;
744 - $this->uploadCount = 0;
745 - $this->uploadSuccessCount = 0;
746 - xml_set_element_handler( $parser, "in_page", "out_page" );
747 - } else {
748 - return $this->throwXMLerror( "Expected <page>, got <$name>" );
749 - }
750 - }
751 - function out_mediawiki( $parser, $name ) {
752 - $this->debug( "out_mediawiki $name" );
753 - if( $name != "mediawiki" ) {
754 - return $this->throwXMLerror( "Expected </mediawiki>, got </$name>" );
755 - }
756 - xml_set_element_handler( $parser, "donothing", "donothing" );
757 - }
758 -
759 -
760 - function in_siteinfo( $parser, $name, $attribs ) {
761 - // no-ops for now
762 - $this->debug( "in_siteinfo $name" );
763 - switch( $name ) {
764 - case "sitename":
765 - case "base":
766 - case "generator":
767 - case "case":
768 - case "namespaces":
769 - case "namespace":
770 - break;
771 - default:
772 - return $this->throwXMLerror( "Element <$name> not allowed in <siteinfo>." );
773 - }
774 - }
775 -
776 - function out_siteinfo( $parser, $name ) {
777 - if( $name == "siteinfo" ) {
778 - xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
779 - }
780 - }
781 -
782 -
783 - function in_page( $parser, $name, $attribs ) {
784 - $this->debug( "in_page $name" );
785 - switch( $name ) {
786 - case "id":
787 - case "title":
788 - case "restrictions":
789 - $this->appendfield = $name;
790 - $this->appenddata = "";
791 - xml_set_element_handler( $parser, "in_nothing", "out_append" );
792 - xml_set_character_data_handler( $parser, "char_append" );
793 - break;
794 - case "revision":
795 - $this->push( "revision" );
796 - if( is_object( $this->pageTitle ) ) {
797 - $this->workRevision = new WikiRevision;
798 - $this->workRevision->setTitle( $this->pageTitle );
799 - $this->workRevisionCount++;
800 - } else {
801 - // Skipping items due to invalid page title
802 - $this->workRevision = null;
803 - }
804 - xml_set_element_handler( $parser, "in_revision", "out_revision" );
805 - break;
806 - case "upload":
807 - $this->push( "upload" );
808 - if( is_object( $this->pageTitle ) ) {
809 - $this->workRevision = new WikiRevision;
810 - $this->workRevision->setTitle( $this->pageTitle );
811 - $this->uploadCount++;
812 - } else {
813 - // Skipping items due to invalid page title
814 - $this->workRevision = null;
815 - }
816 - xml_set_element_handler( $parser, "in_upload", "out_upload" );
817 - break;
818 - default:
819 - return $this->throwXMLerror( "Element <$name> not allowed in a <page>." );
820 - }
821 - }
822 -
823 - function out_page( $parser, $name ) {
824 - $this->debug( "out_page $name" );
825 - $this->pop();
826 - if( $name != "page" ) {
827 - return $this->throwXMLerror( "Expected </page>, got </$name>" );
828 - }
829 - xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
830 -
831 - $this->pageOutCallback( $this->pageTitle, $this->origTitle,
832 - $this->workRevisionCount, $this->workSuccessCount );
833 -
834 - $this->workTitle = null;
835 - $this->workRevision = null;
836 - $this->workRevisionCount = 0;
837 - $this->workSuccessCount = 0;
838 - $this->pageTitle = null;
839 - $this->origTitle = null;
840 - }
841 -
842 - function in_nothing( $parser, $name, $attribs ) {
843 - $this->debug( "in_nothing $name" );
844 - return $this->throwXMLerror( "No child elements allowed here; got <$name>" );
845 - }
846 - function char_append( $parser, $data ) {
847 - $this->debug( "char_append '$data'" );
848 - $this->appenddata .= $data;
849 - }
850 - function out_append( $parser, $name ) {
851 - $this->debug( "out_append $name" );
852 - if( $name != $this->appendfield ) {
853 - return $this->throwXMLerror( "Expected </{$this->appendfield}>, got </$name>" );
854 - }
855 -
856 - switch( $this->appendfield ) {
857 - case "title":
858 - $this->workTitle = $this->appenddata;
859 - $this->origTitle = Title::newFromText( $this->workTitle );
860 - if( !is_null( $this->mTargetNamespace ) && !is_null( $this->origTitle ) ) {
861 - $this->pageTitle = Title::makeTitle( $this->mTargetNamespace,
862 - $this->origTitle->getDBkey() );
863 - } else {
864 - $this->pageTitle = Title::newFromText( $this->workTitle );
865 - }
866 - if( is_null( $this->pageTitle ) ) {
867 - // Invalid page title? Ignore the page
868 - $this->notice( "Skipping invalid page title '$this->workTitle'" );
869 - } else {
870 - $this->pageCallback( $this->workTitle );
871 - }
872 - break;
873 - case "id":
874 - if ( $this->parentTag() == 'revision' ) {
875 - if( $this->workRevision )
876 - $this->workRevision->setID( $this->appenddata );
877 - }
878 - break;
879 - case "text":
880 - if( $this->workRevision )
881 - $this->workRevision->setText( $this->appenddata );
882 - break;
883 - case "username":
884 - if( $this->workRevision )
885 - $this->workRevision->setUsername( $this->appenddata );
886 - break;
887 - case "ip":
888 - if( $this->workRevision )
889 - $this->workRevision->setUserIP( $this->appenddata );
890 - break;
891 - case "timestamp":
892 - if( $this->workRevision )
893 - $this->workRevision->setTimestamp( $this->appenddata );
894 - break;
895 - case "comment":
896 - if( $this->workRevision )
897 - $this->workRevision->setComment( $this->appenddata );
898 - break;
899 - case "minor":
900 - if( $this->workRevision )
901 - $this->workRevision->setMinor( true );
902 - break;
903 - case "filename":
904 - if( $this->workRevision )
905 - $this->workRevision->setFilename( $this->appenddata );
906 - break;
907 - case "src":
908 - if( $this->workRevision )
909 - $this->workRevision->setSrc( $this->appenddata );
910 - break;
911 - case "size":
912 - if( $this->workRevision )
913 - $this->workRevision->setSize( intval( $this->appenddata ) );
914 - break;
915 - default:
916 - $this->debug( "Bad append: {$this->appendfield}" );
917 - }
918 - $this->appendfield = "";
919 - $this->appenddata = "";
920 -
921 - $parent = $this->parentTag();
922 - xml_set_element_handler( $parser, "in_$parent", "out_$parent" );
923 - xml_set_character_data_handler( $parser, "donothing" );
924 - }
925 -
926 - function in_revision( $parser, $name, $attribs ) {
927 - $this->debug( "in_revision $name" );
928 - switch( $name ) {
929 - case "id":
930 - case "timestamp":
931 - case "comment":
932 - case "minor":
933 - case "text":
934 - $this->appendfield = $name;
935 - xml_set_element_handler( $parser, "in_nothing", "out_append" );
936 - xml_set_character_data_handler( $parser, "char_append" );
937 - break;
938 - case "contributor":
939 - $this->push( "contributor" );
940 - xml_set_element_handler( $parser, "in_contributor", "out_contributor" );
941 - break;
942 - default:
943 - return $this->throwXMLerror( "Element <$name> not allowed in a <revision>." );
944 - }
945 - }
946 -
947 - function out_revision( $parser, $name ) {
948 - $this->debug( "out_revision $name" );
949 - $this->pop();
950 - if( $name != "revision" ) {
951 - return $this->throwXMLerror( "Expected </revision>, got </$name>" );
952 - }
953 - xml_set_element_handler( $parser, "in_page", "out_page" );
954 -
955 - if( $this->workRevision ) {
956 - $ok = call_user_func_array( $this->mRevisionCallback,
957 - array( $this->workRevision, $this ) );
958 - if( $ok ) {
959 - $this->workSuccessCount++;
960 - }
961 - }
962 - }
963 -
964 - function in_upload( $parser, $name, $attribs ) {
965 - $this->debug( "in_upload $name" );
966 - switch( $name ) {
967 - case "timestamp":
968 - case "comment":
969 - case "text":
970 - case "filename":
971 - case "src":
972 - case "size":
973 - $this->appendfield = $name;
974 - xml_set_element_handler( $parser, "in_nothing", "out_append" );
975 - xml_set_character_data_handler( $parser, "char_append" );
976 - break;
977 - case "contributor":
978 - $this->push( "contributor" );
979 - xml_set_element_handler( $parser, "in_contributor", "out_contributor" );
980 - break;
981 - default:
982 - return $this->throwXMLerror( "Element <$name> not allowed in an <upload>." );
983 - }
984 - }
985 -
986 - function out_upload( $parser, $name ) {
987 - $this->debug( "out_revision $name" );
988 - $this->pop();
989 - if( $name != "upload" ) {
990 - return $this->throwXMLerror( "Expected </upload>, got </$name>" );
991 - }
992 - xml_set_element_handler( $parser, "in_page", "out_page" );
993 -
994 - if( $this->workRevision ) {
995 - $ok = call_user_func_array( $this->mUploadCallback,
996 - array( $this->workRevision, $this ) );
997 - if( $ok ) {
998 - $this->workUploadSuccessCount++;
999 - }
1000 - }
1001 - }
1002 -
1003 - function in_contributor( $parser, $name, $attribs ) {
1004 - $this->debug( "in_contributor $name" );
1005 - switch( $name ) {
1006 - case "username":
1007 - case "ip":
1008 - case "id":
1009 - $this->appendfield = $name;
1010 - xml_set_element_handler( $parser, "in_nothing", "out_append" );
1011 - xml_set_character_data_handler( $parser, "char_append" );
1012 - break;
1013 - default:
1014 - $this->throwXMLerror( "Invalid tag <$name> in <contributor>" );
1015 - }
1016 - }
1017 -
1018 - function out_contributor( $parser, $name ) {
1019 - $this->debug( "out_contributor $name" );
1020 - $this->pop();
1021 - if( $name != "contributor" ) {
1022 - return $this->throwXMLerror( "Expected </contributor>, got </$name>" );
1023 - }
1024 - $parent = $this->parentTag();
1025 - xml_set_element_handler( $parser, "in_$parent", "out_$parent" );
1026 - }
1027 -
1028 - private function push( $name ) {
1029 - array_push( $this->tagStack, $name );
1030 - $this->debug( "PUSH $name" );
1031 - }
1032 -
1033 - private function pop() {
1034 - $name = array_pop( $this->tagStack );
1035 - $this->debug( "POP $name" );
1036 - return $name;
1037 - }
1038 -
1039 - private function parentTag() {
1040 - $name = $this->tagStack[count( $this->tagStack ) - 1];
1041 - $this->debug( "PARENT $name" );
1042 - return $name;
1043 - }
1044 -
1045 -}
1046 -
1047 -/**
1048 - * @todo document (e.g. one-sentence class description).
1049 - * @ingroup SpecialPage
1050 - */
1051 -class ImportStringSource {
1052 - function __construct( $string ) {
1053 - $this->mString = $string;
1054 - $this->mRead = false;
1055 - }
1056 -
1057 - function atEnd() {
1058 - return $this->mRead;
1059 - }
1060 -
1061 - function readChunk() {
1062 - if( $this->atEnd() ) {
1063 - return false;
1064 - } else {
1065 - $this->mRead = true;
1066 - return $this->mString;
1067 - }
1068 - }
1069 -}
1070 -
1071 -/**
1072 - * @todo document (e.g. one-sentence class description).
1073 - * @ingroup SpecialPage
1074 - */
1075 -class ImportStreamSource {
1076 - function __construct( $handle ) {
1077 - $this->mHandle = $handle;
1078 - }
1079 -
1080 - function atEnd() {
1081 - return feof( $this->mHandle );
1082 - }
1083 -
1084 - function readChunk() {
1085 - return fread( $this->mHandle, 32768 );
1086 - }
1087 -
1088 - static function newFromFile( $filename ) {
1089 - $file = @fopen( $filename, 'rt' );
1090 - if( !$file ) {
1091 - return new WikiErrorMsg( "importcantopen" );
1092 - }
1093 - return new ImportStreamSource( $file );
1094 - }
1095 -
1096 - static function newFromUpload( $fieldname = "xmlimport" ) {
1097 - $upload =& $_FILES[$fieldname];
1098 -
1099 - if( !isset( $upload ) || !$upload['name'] ) {
1100 - return new WikiErrorMsg( 'importnofile' );
1101 - }
1102 - if( !empty( $upload['error'] ) ) {
1103 - switch($upload['error']){
1104 - case 1: # The uploaded file exceeds the upload_max_filesize directive in php.ini.
1105 - return new WikiErrorMsg( 'importuploaderrorsize' );
1106 - case 2: # The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.
1107 - return new WikiErrorMsg( 'importuploaderrorsize' );
1108 - case 3: # The uploaded file was only partially uploaded
1109 - return new WikiErrorMsg( 'importuploaderrorpartial' );
1110 - case 6: #Missing a temporary folder. Introduced in PHP 4.3.10 and PHP 5.0.3.
1111 - return new WikiErrorMsg( 'importuploaderrortemp' );
1112 - # case else: # Currently impossible
1113 - }
1114 -
1115 - }
1116 - $fname = $upload['tmp_name'];
1117 - if( is_uploaded_file( $fname ) ) {
1118 - return ImportStreamSource::newFromFile( $fname );
1119 - } else {
1120 - return new WikiErrorMsg( 'importnofile' );
1121 - }
1122 - }
1123 -
1124 - static function newFromURL( $url, $method = 'GET' ) {
1125 - wfDebug( __METHOD__ . ": opening $url\n" );
1126 - # Use the standard HTTP fetch function; it times out
1127 - # quicker and sorts out user-agent problems which might
1128 - # otherwise prevent importing from large sites, such
1129 - # as the Wikimedia cluster, etc.
1130 - $data = Http::request( $method, $url );
1131 - if( $data !== false ) {
1132 - $file = tmpfile();
1133 - fwrite( $file, $data );
1134 - fflush( $file );
1135 - fseek( $file, 0 );
1136 - return new ImportStreamSource( $file );
1137 - } else {
1138 - return new WikiErrorMsg( 'importcantopen' );
1139 - }
1140 - }
1141 -
1142 - public static function newFromInterwiki( $interwiki, $page, $history=false ) {
1143 - if( $page == '' ) {
1144 - return new WikiErrorMsg( 'import-noarticle' );
1145 - }
1146 - $link = Title::newFromText( "$interwiki:Special:Export/$page" );
1147 - if( is_null( $link ) || $link->getInterwiki() == '' ) {
1148 - return new WikiErrorMsg( 'importbadinterwiki' );
1149 - } else {
1150 - $params = $history ? 'history=1' : '';
1151 - $url = $link->getFullUrl( $params );
1152 - # For interwikis, use POST to avoid redirects.
1153 - return ImportStreamSource::newFromURL( $url, "POST" );
1154 - }
1155 - }
1156 -}
Index: trunk/phase3/includes/Import.php
@@ -0,0 +1,1009 @@
 2+<?php
 3+/**
 4+ * MediaWiki page data importer
 5+ * Copyright (C) 2003,2005 Brion Vibber <brion@pobox.com>
 6+ * http://www.mediawiki.org/
 7+ *
 8+ * This program is free software; you can redistribute it and/or modify
 9+ * it under the terms of the GNU General Public License as published by
 10+ * the Free Software Foundation; either version 2 of the License, or
 11+ * (at your option) any later version.
 12+ *
 13+ * This program is distributed in the hope that it will be useful,
 14+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 16+ * GNU General Public License for more details.
 17+ *
 18+ * You should have received a copy of the GNU General Public License along
 19+ * with this program; if not, write to the Free Software Foundation, Inc.,
 20+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 21+ * http://www.gnu.org/copyleft/gpl.html
 22+ *
 23+ * @file
 24+ * @ingroup SpecialPage
 25+ */
 26+
 27+/**
 28+ * Reporting callback
 29+ * @ingroup SpecialPage
 30+ */
 31+class ImportReporter {
 32+ function __construct( $importer, $upload, $interwiki ) {
 33+ $importer->setPageOutCallback( array( $this, 'reportPage' ) );
 34+ $this->mPageCount = 0;
 35+ $this->mIsUpload = $upload;
 36+ $this->mInterwiki = $interwiki;
 37+ }
 38+
 39+ function open() {
 40+ global $wgOut;
 41+ $wgOut->addHtml( "<ul>\n" );
 42+ }
 43+
 44+ function reportPage( $title, $origTitle, $revisionCount, $successCount ) {
 45+ global $wgOut, $wgUser, $wgLang, $wgContLang;
 46+
 47+ $skin = $wgUser->getSkin();
 48+
 49+ $this->mPageCount++;
 50+
 51+ $localCount = $wgLang->formatNum( $successCount );
 52+ $contentCount = $wgContLang->formatNum( $successCount );
 53+
 54+ if( $successCount > 0 ) {
 55+ $wgOut->addHtml( "<li>" . $skin->makeKnownLinkObj( $title ) . " " .
 56+ wfMsgExt( 'import-revision-count', array( 'parsemag', 'escape' ), $localCount ) .
 57+ "</li>\n"
 58+ );
 59+
 60+ $log = new LogPage( 'import' );
 61+ if( $this->mIsUpload ) {
 62+ $detail = wfMsgExt( 'import-logentry-upload-detail', array( 'content', 'parsemag' ),
 63+ $contentCount );
 64+ $log->addEntry( 'upload', $title, $detail );
 65+ } else {
 66+ $interwiki = '[[:' . $this->mInterwiki . ':' .
 67+ $origTitle->getPrefixedText() . ']]';
 68+ $detail = wfMsgExt( 'import-logentry-interwiki-detail', array( 'content', 'parsemag' ),
 69+ $contentCount, $interwiki );
 70+ $log->addEntry( 'interwiki', $title, $detail );
 71+ }
 72+
 73+ $comment = $detail; // quick
 74+ $dbw = wfGetDB( DB_MASTER );
 75+ $latest = $title->getLatestRevID();
 76+ $nullRevision = Revision::newNullRevision( $dbw, $title->getArticleId(), $comment, true );
 77+ $nullRevision->insertOn( $dbw );
 78+ $article = new Article( $title );
 79+ # Update page record
 80+ $article->updateRevisionOn( $dbw, $nullRevision );
 81+ wfRunHooks( 'NewRevisionFromEditComplete', array($article, $nullRevision, $latest) );
 82+ } else {
 83+ $wgOut->addHtml( '<li>' . wfMsgHtml( 'import-nonewrevisions' ) . '</li>' );
 84+ }
 85+ }
 86+
 87+ function close() {
 88+ global $wgOut;
 89+ if( $this->mPageCount == 0 ) {
 90+ $wgOut->addHtml( "</ul>\n" );
 91+ return new WikiErrorMsg( "importnopages" );
 92+ }
 93+ $wgOut->addHtml( "</ul>\n" );
 94+
 95+ return $this->mPageCount;
 96+ }
 97+}
 98+
 99+/**
 100+ *
 101+ * @ingroup SpecialPage
 102+ */
 103+class WikiRevision {
 104+ var $title = null;
 105+ var $id = 0;
 106+ var $timestamp = "20010115000000";
 107+ var $user = 0;
 108+ var $user_text = "";
 109+ var $text = "";
 110+ var $comment = "";
 111+ var $minor = false;
 112+
 113+ function setTitle( $title ) {
 114+ if( is_object( $title ) ) {
 115+ $this->title = $title;
 116+ } elseif( is_null( $title ) ) {
 117+ throw new MWException( "WikiRevision given a null title in import. You may need to adjust \$wgLegalTitleChars." );
 118+ } else {
 119+ throw new MWException( "WikiRevision given non-object title in import." );
 120+ }
 121+ }
 122+
 123+ function setID( $id ) {
 124+ $this->id = $id;
 125+ }
 126+
 127+ function setTimestamp( $ts ) {
 128+ # 2003-08-05T18:30:02Z
 129+ $this->timestamp = wfTimestamp( TS_MW, $ts );
 130+ }
 131+
 132+ function setUsername( $user ) {
 133+ $this->user_text = $user;
 134+ }
 135+
 136+ function setUserIP( $ip ) {
 137+ $this->user_text = $ip;
 138+ }
 139+
 140+ function setText( $text ) {
 141+ $this->text = $text;
 142+ }
 143+
 144+ function setComment( $text ) {
 145+ $this->comment = $text;
 146+ }
 147+
 148+ function setMinor( $minor ) {
 149+ $this->minor = (bool)$minor;
 150+ }
 151+
 152+ function setSrc( $src ) {
 153+ $this->src = $src;
 154+ }
 155+
 156+ function setFilename( $filename ) {
 157+ $this->filename = $filename;
 158+ }
 159+
 160+ function setSize( $size ) {
 161+ $this->size = intval( $size );
 162+ }
 163+
 164+ function getTitle() {
 165+ return $this->title;
 166+ }
 167+
 168+ function getID() {
 169+ return $this->id;
 170+ }
 171+
 172+ function getTimestamp() {
 173+ return $this->timestamp;
 174+ }
 175+
 176+ function getUser() {
 177+ return $this->user_text;
 178+ }
 179+
 180+ function getText() {
 181+ return $this->text;
 182+ }
 183+
 184+ function getComment() {
 185+ return $this->comment;
 186+ }
 187+
 188+ function getMinor() {
 189+ return $this->minor;
 190+ }
 191+
 192+ function getSrc() {
 193+ return $this->src;
 194+ }
 195+
 196+ function getFilename() {
 197+ return $this->filename;
 198+ }
 199+
 200+ function getSize() {
 201+ return $this->size;
 202+ }
 203+
 204+ function importOldRevision() {
 205+ $dbw = wfGetDB( DB_MASTER );
 206+
 207+ # Sneak a single revision into place
 208+ $user = User::newFromName( $this->getUser() );
 209+ if( $user ) {
 210+ $userId = intval( $user->getId() );
 211+ $userText = $user->getName();
 212+ } else {
 213+ $userId = 0;
 214+ $userText = $this->getUser();
 215+ }
 216+
 217+ // avoid memory leak...?
 218+ $linkCache = LinkCache::singleton();
 219+ $linkCache->clear();
 220+
 221+ $article = new Article( $this->title );
 222+ $pageId = $article->getId();
 223+ if( $pageId == 0 ) {
 224+ # must create the page...
 225+ $pageId = $article->insertOn( $dbw );
 226+ $created = true;
 227+ } else {
 228+ $created = false;
 229+
 230+ $prior = Revision::loadFromTimestamp( $dbw, $this->title, $this->timestamp );
 231+ if( !is_null( $prior ) ) {
 232+ // FIXME: this could fail slightly for multiple matches :P
 233+ wfDebug( __METHOD__ . ": skipping existing revision for [[" .
 234+ $this->title->getPrefixedText() . "]], timestamp " .
 235+ $this->timestamp . "\n" );
 236+ return false;
 237+ }
 238+ }
 239+
 240+ # FIXME: Use original rev_id optionally
 241+ # FIXME: blah blah blah
 242+
 243+ #if( $numrows > 0 ) {
 244+ # return wfMsg( "importhistoryconflict" );
 245+ #}
 246+
 247+ # Insert the row
 248+ $revision = new Revision( array(
 249+ 'page' => $pageId,
 250+ 'text' => $this->getText(),
 251+ 'comment' => $this->getComment(),
 252+ 'user' => $userId,
 253+ 'user_text' => $userText,
 254+ 'timestamp' => $this->timestamp,
 255+ 'minor_edit' => $this->minor,
 256+ ) );
 257+ $revId = $revision->insertOn( $dbw );
 258+ $changed = $article->updateIfNewerOn( $dbw, $revision );
 259+
 260+ if( $created ) {
 261+ wfDebug( __METHOD__ . ": running onArticleCreate\n" );
 262+ Article::onArticleCreate( $this->title );
 263+
 264+ wfDebug( __METHOD__ . ": running create updates\n" );
 265+ $article->createUpdates( $revision );
 266+
 267+ } elseif( $changed ) {
 268+ wfDebug( __METHOD__ . ": running onArticleEdit\n" );
 269+ Article::onArticleEdit( $this->title );
 270+
 271+ wfDebug( __METHOD__ . ": running edit updates\n" );
 272+ $article->editUpdates(
 273+ $this->getText(),
 274+ $this->getComment(),
 275+ $this->minor,
 276+ $this->timestamp,
 277+ $revId );
 278+ }
 279+
 280+ return true;
 281+ }
 282+
 283+ function importUpload() {
 284+ wfDebug( __METHOD__ . ": STUB\n" );
 285+
 286+ /**
 287+ // from file revert...
 288+ $source = $this->file->getArchiveVirtualUrl( $this->oldimage );
 289+ $comment = $wgRequest->getText( 'wpComment' );
 290+ // TODO: Preserve file properties from database instead of reloading from file
 291+ $status = $this->file->upload( $source, $comment, $comment );
 292+ if( $status->isGood() ) {
 293+ */
 294+
 295+ /**
 296+ // from file upload...
 297+ $this->mLocalFile = wfLocalFile( $nt );
 298+ $this->mDestName = $this->mLocalFile->getName();
 299+ //....
 300+ $status = $this->mLocalFile->upload( $this->mTempPath, $this->mComment, $pageText,
 301+ File::DELETE_SOURCE, $this->mFileProps );
 302+ if ( !$status->isGood() ) {
 303+ $resultDetails = array( 'internal' => $status->getWikiText() );
 304+ */
 305+
 306+ // @fixme upload() uses $wgUser, which is wrong here
 307+ // it may also create a page without our desire, also wrong potentially.
 308+ // and, it will record a *current* upload, but we might want an archive version here
 309+
 310+ $file = wfLocalFile( $this->getTitle() );
 311+ if( !$file ) {
 312+ var_dump( $file );
 313+ wfDebug( "IMPORT: Bad file. :(\n" );
 314+ return false;
 315+ }
 316+
 317+ $source = $this->downloadSource();
 318+ if( !$source ) {
 319+ wfDebug( "IMPORT: Could not fetch remote file. :(\n" );
 320+ return false;
 321+ }
 322+
 323+ $status = $file->upload( $source,
 324+ $this->getComment(),
 325+ $this->getComment(), // Initial page, if none present...
 326+ File::DELETE_SOURCE,
 327+ false, // props...
 328+ $this->getTimestamp() );
 329+
 330+ if( $status->isGood() ) {
 331+ // yay?
 332+ wfDebug( "IMPORT: is ok?\n" );
 333+ return true;
 334+ }
 335+
 336+ wfDebug( "IMPORT: is bad? " . $status->getXml() . "\n" );
 337+ return false;
 338+
 339+ }
 340+
 341+ function downloadSource() {
 342+ global $wgEnableUploads;
 343+ if( !$wgEnableUploads ) {
 344+ return false;
 345+ }
 346+
 347+ $tempo = tempnam( wfTempDir(), 'download' );
 348+ $f = fopen( $tempo, 'wb' );
 349+ if( !$f ) {
 350+ wfDebug( "IMPORT: couldn't write to temp file $tempo\n" );
 351+ return false;
 352+ }
 353+
 354+ // @fixme!
 355+ $src = $this->getSrc();
 356+ $data = Http::get( $src );
 357+ if( !$data ) {
 358+ wfDebug( "IMPORT: couldn't fetch source $src\n" );
 359+ fclose( $f );
 360+ unlink( $tempo );
 361+ return false;
 362+ }
 363+
 364+ fwrite( $f, $data );
 365+ fclose( $f );
 366+
 367+ return $tempo;
 368+ }
 369+
 370+}
 371+
 372+/**
 373+ * implements Special:Import
 374+ * @ingroup SpecialPage
 375+ */
 376+class WikiImporter {
 377+ var $mDebug = false;
 378+ var $mSource = null;
 379+ var $mPageCallback = null;
 380+ var $mPageOutCallback = null;
 381+ var $mRevisionCallback = null;
 382+ var $mUploadCallback = null;
 383+ var $mTargetNamespace = null;
 384+ var $lastfield;
 385+ var $tagStack = array();
 386+
 387+ function __construct( $source ) {
 388+ $this->setRevisionCallback( array( $this, "importRevision" ) );
 389+ $this->setUploadCallback( array( $this, "importUpload" ) );
 390+ $this->mSource = $source;
 391+ }
 392+
 393+ function throwXmlError( $err ) {
 394+ $this->debug( "FAILURE: $err" );
 395+ wfDebug( "WikiImporter XML error: $err\n" );
 396+ }
 397+
 398+ # --------------
 399+
 400+ function doImport() {
 401+ if( empty( $this->mSource ) ) {
 402+ return new WikiErrorMsg( "importnotext" );
 403+ }
 404+
 405+ $parser = xml_parser_create( "UTF-8" );
 406+
 407+ # case folding violates XML standard, turn it off
 408+ xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
 409+
 410+ xml_set_object( $parser, $this );
 411+ xml_set_element_handler( $parser, "in_start", "" );
 412+
 413+ $offset = 0; // for context extraction on error reporting
 414+ do {
 415+ $chunk = $this->mSource->readChunk();
 416+ if( !xml_parse( $parser, $chunk, $this->mSource->atEnd() ) ) {
 417+ wfDebug( "WikiImporter::doImport encountered XML parsing error\n" );
 418+ return new WikiXmlError( $parser, wfMsgHtml( 'import-parse-failure' ), $chunk, $offset );
 419+ }
 420+ $offset += strlen( $chunk );
 421+ } while( $chunk !== false && !$this->mSource->atEnd() );
 422+ xml_parser_free( $parser );
 423+
 424+ return true;
 425+ }
 426+
 427+ function debug( $data ) {
 428+ if( $this->mDebug ) {
 429+ wfDebug( "IMPORT: $data\n" );
 430+ }
 431+ }
 432+
 433+ function notice( $data ) {
 434+ global $wgCommandLineMode;
 435+ if( $wgCommandLineMode ) {
 436+ print "$data\n";
 437+ } else {
 438+ global $wgOut;
 439+ $wgOut->addHTML( "<li>" . htmlspecialchars( $data ) . "</li>\n" );
 440+ }
 441+ }
 442+
 443+ /**
 444+ * Set debug mode...
 445+ */
 446+ function setDebug( $debug ) {
 447+ $this->mDebug = $debug;
 448+ }
 449+
 450+ /**
 451+ * Sets the action to perform as each new page in the stream is reached.
 452+ * @param $callback callback
 453+ * @return callback
 454+ */
 455+ function setPageCallback( $callback ) {
 456+ $previous = $this->mPageCallback;
 457+ $this->mPageCallback = $callback;
 458+ return $previous;
 459+ }
 460+
 461+ /**
 462+ * Sets the action to perform as each page in the stream is completed.
 463+ * Callback accepts the page title (as a Title object), a second object
 464+ * with the original title form (in case it's been overridden into a
 465+ * local namespace), and a count of revisions.
 466+ *
 467+ * @param $callback callback
 468+ * @return callback
 469+ */
 470+ function setPageOutCallback( $callback ) {
 471+ $previous = $this->mPageOutCallback;
 472+ $this->mPageOutCallback = $callback;
 473+ return $previous;
 474+ }
 475+
 476+ /**
 477+ * Sets the action to perform as each page revision is reached.
 478+ * @param $callback callback
 479+ * @return callback
 480+ */
 481+ function setRevisionCallback( $callback ) {
 482+ $previous = $this->mRevisionCallback;
 483+ $this->mRevisionCallback = $callback;
 484+ return $previous;
 485+ }
 486+
 487+ /**
 488+ * Sets the action to perform as each file upload version is reached.
 489+ * @param $callback callback
 490+ * @return callback
 491+ */
 492+ function setUploadCallback( $callback ) {
 493+ $previous = $this->mUploadCallback;
 494+ $this->mUploadCallback = $callback;
 495+ return $previous;
 496+ }
 497+
 498+ /**
 499+ * Set a target namespace to override the defaults
 500+ */
 501+ function setTargetNamespace( $namespace ) {
 502+ if( is_null( $namespace ) ) {
 503+ // Don't override namespaces
 504+ $this->mTargetNamespace = null;
 505+ } elseif( $namespace >= 0 ) {
 506+ // FIXME: Check for validity
 507+ $this->mTargetNamespace = intval( $namespace );
 508+ } else {
 509+ return false;
 510+ }
 511+ }
 512+
 513+ /**
 514+ * Default per-revision callback, performs the import.
 515+ * @param $revision WikiRevision
 516+ * @private
 517+ */
 518+ function importRevision( $revision ) {
 519+ $dbw = wfGetDB( DB_MASTER );
 520+ return $dbw->deadlockLoop( array( $revision, 'importOldRevision' ) );
 521+ }
 522+
 523+ /**
 524+ * Dummy for now...
 525+ */
 526+ function importUpload( $revision ) {
 527+ //$dbw = wfGetDB( DB_MASTER );
 528+ //return $dbw->deadlockLoop( array( $revision, 'importUpload' ) );
 529+ return false;
 530+ }
 531+
 532+ /**
 533+ * Alternate per-revision callback, for debugging.
 534+ * @param $revision WikiRevision
 535+ * @private
 536+ */
 537+ function debugRevisionHandler( &$revision ) {
 538+ $this->debug( "Got revision:" );
 539+ if( is_object( $revision->title ) ) {
 540+ $this->debug( "-- Title: " . $revision->title->getPrefixedText() );
 541+ } else {
 542+ $this->debug( "-- Title: <invalid>" );
 543+ }
 544+ $this->debug( "-- User: " . $revision->user_text );
 545+ $this->debug( "-- Timestamp: " . $revision->timestamp );
 546+ $this->debug( "-- Comment: " . $revision->comment );
 547+ $this->debug( "-- Text: " . $revision->text );
 548+ }
 549+
 550+ /**
 551+ * Notify the callback function when a new <page> is reached.
 552+ * @param $title Title
 553+ * @private
 554+ */
 555+ function pageCallback( $title ) {
 556+ if( is_callable( $this->mPageCallback ) ) {
 557+ call_user_func( $this->mPageCallback, $title );
 558+ }
 559+ }
 560+
 561+ /**
 562+ * Notify the callback function when a </page> is closed.
 563+ * @param $title Title
 564+ * @param $origTitle Title
 565+ * @param $revisionCount int
 566+ * @param $successCount Int: number of revisions for which callback returned true
 567+ * @private
 568+ */
 569+ function pageOutCallback( $title, $origTitle, $revisionCount, $successCount ) {
 570+ if( is_callable( $this->mPageOutCallback ) ) {
 571+ call_user_func( $this->mPageOutCallback, $title, $origTitle,
 572+ $revisionCount, $successCount );
 573+ }
 574+ }
 575+
 576+
 577+ # XML parser callbacks from here out -- beware!
 578+ function donothing( $parser, $x, $y="" ) {
 579+ #$this->debug( "donothing" );
 580+ }
 581+
 582+ function in_start( $parser, $name, $attribs ) {
 583+ $this->debug( "in_start $name" );
 584+ if( $name != "mediawiki" ) {
 585+ return $this->throwXMLerror( "Expected <mediawiki>, got <$name>" );
 586+ }
 587+ xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
 588+ }
 589+
 590+ function in_mediawiki( $parser, $name, $attribs ) {
 591+ $this->debug( "in_mediawiki $name" );
 592+ if( $name == 'siteinfo' ) {
 593+ xml_set_element_handler( $parser, "in_siteinfo", "out_siteinfo" );
 594+ } elseif( $name == 'page' ) {
 595+ $this->push( $name );
 596+ $this->workRevisionCount = 0;
 597+ $this->workSuccessCount = 0;
 598+ $this->uploadCount = 0;
 599+ $this->uploadSuccessCount = 0;
 600+ xml_set_element_handler( $parser, "in_page", "out_page" );
 601+ } else {
 602+ return $this->throwXMLerror( "Expected <page>, got <$name>" );
 603+ }
 604+ }
 605+ function out_mediawiki( $parser, $name ) {
 606+ $this->debug( "out_mediawiki $name" );
 607+ if( $name != "mediawiki" ) {
 608+ return $this->throwXMLerror( "Expected </mediawiki>, got </$name>" );
 609+ }
 610+ xml_set_element_handler( $parser, "donothing", "donothing" );
 611+ }
 612+
 613+
 614+ function in_siteinfo( $parser, $name, $attribs ) {
 615+ // no-ops for now
 616+ $this->debug( "in_siteinfo $name" );
 617+ switch( $name ) {
 618+ case "sitename":
 619+ case "base":
 620+ case "generator":
 621+ case "case":
 622+ case "namespaces":
 623+ case "namespace":
 624+ break;
 625+ default:
 626+ return $this->throwXMLerror( "Element <$name> not allowed in <siteinfo>." );
 627+ }
 628+ }
 629+
 630+ function out_siteinfo( $parser, $name ) {
 631+ if( $name == "siteinfo" ) {
 632+ xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
 633+ }
 634+ }
 635+
 636+
 637+ function in_page( $parser, $name, $attribs ) {
 638+ $this->debug( "in_page $name" );
 639+ switch( $name ) {
 640+ case "id":
 641+ case "title":
 642+ case "restrictions":
 643+ $this->appendfield = $name;
 644+ $this->appenddata = "";
 645+ xml_set_element_handler( $parser, "in_nothing", "out_append" );
 646+ xml_set_character_data_handler( $parser, "char_append" );
 647+ break;
 648+ case "revision":
 649+ $this->push( "revision" );
 650+ if( is_object( $this->pageTitle ) ) {
 651+ $this->workRevision = new WikiRevision;
 652+ $this->workRevision->setTitle( $this->pageTitle );
 653+ $this->workRevisionCount++;
 654+ } else {
 655+ // Skipping items due to invalid page title
 656+ $this->workRevision = null;
 657+ }
 658+ xml_set_element_handler( $parser, "in_revision", "out_revision" );
 659+ break;
 660+ case "upload":
 661+ $this->push( "upload" );
 662+ if( is_object( $this->pageTitle ) ) {
 663+ $this->workRevision = new WikiRevision;
 664+ $this->workRevision->setTitle( $this->pageTitle );
 665+ $this->uploadCount++;
 666+ } else {
 667+ // Skipping items due to invalid page title
 668+ $this->workRevision = null;
 669+ }
 670+ xml_set_element_handler( $parser, "in_upload", "out_upload" );
 671+ break;
 672+ default:
 673+ return $this->throwXMLerror( "Element <$name> not allowed in a <page>." );
 674+ }
 675+ }
 676+
 677+ function out_page( $parser, $name ) {
 678+ $this->debug( "out_page $name" );
 679+ $this->pop();
 680+ if( $name != "page" ) {
 681+ return $this->throwXMLerror( "Expected </page>, got </$name>" );
 682+ }
 683+ xml_set_element_handler( $parser, "in_mediawiki", "out_mediawiki" );
 684+
 685+ $this->pageOutCallback( $this->pageTitle, $this->origTitle,
 686+ $this->workRevisionCount, $this->workSuccessCount );
 687+
 688+ $this->workTitle = null;
 689+ $this->workRevision = null;
 690+ $this->workRevisionCount = 0;
 691+ $this->workSuccessCount = 0;
 692+ $this->pageTitle = null;
 693+ $this->origTitle = null;
 694+ }
 695+
 696+ function in_nothing( $parser, $name, $attribs ) {
 697+ $this->debug( "in_nothing $name" );
 698+ return $this->throwXMLerror( "No child elements allowed here; got <$name>" );
 699+ }
 700+ function char_append( $parser, $data ) {
 701+ $this->debug( "char_append '$data'" );
 702+ $this->appenddata .= $data;
 703+ }
 704+ function out_append( $parser, $name ) {
 705+ $this->debug( "out_append $name" );
 706+ if( $name != $this->appendfield ) {
 707+ return $this->throwXMLerror( "Expected </{$this->appendfield}>, got </$name>" );
 708+ }
 709+
 710+ switch( $this->appendfield ) {
 711+ case "title":
 712+ $this->workTitle = $this->appenddata;
 713+ $this->origTitle = Title::newFromText( $this->workTitle );
 714+ if( !is_null( $this->mTargetNamespace ) && !is_null( $this->origTitle ) ) {
 715+ $this->pageTitle = Title::makeTitle( $this->mTargetNamespace,
 716+ $this->origTitle->getDBkey() );
 717+ } else {
 718+ $this->pageTitle = Title::newFromText( $this->workTitle );
 719+ }
 720+ if( is_null( $this->pageTitle ) ) {
 721+ // Invalid page title? Ignore the page
 722+ $this->notice( "Skipping invalid page title '$this->workTitle'" );
 723+ } else {
 724+ $this->pageCallback( $this->workTitle );
 725+ }
 726+ break;
 727+ case "id":
 728+ if ( $this->parentTag() == 'revision' ) {
 729+ if( $this->workRevision )
 730+ $this->workRevision->setID( $this->appenddata );
 731+ }
 732+ break;
 733+ case "text":
 734+ if( $this->workRevision )
 735+ $this->workRevision->setText( $this->appenddata );
 736+ break;
 737+ case "username":
 738+ if( $this->workRevision )
 739+ $this->workRevision->setUsername( $this->appenddata );
 740+ break;
 741+ case "ip":
 742+ if( $this->workRevision )
 743+ $this->workRevision->setUserIP( $this->appenddata );
 744+ break;
 745+ case "timestamp":
 746+ if( $this->workRevision )
 747+ $this->workRevision->setTimestamp( $this->appenddata );
 748+ break;
 749+ case "comment":
 750+ if( $this->workRevision )
 751+ $this->workRevision->setComment( $this->appenddata );
 752+ break;
 753+ case "minor":
 754+ if( $this->workRevision )
 755+ $this->workRevision->setMinor( true );
 756+ break;
 757+ case "filename":
 758+ if( $this->workRevision )
 759+ $this->workRevision->setFilename( $this->appenddata );
 760+ break;
 761+ case "src":
 762+ if( $this->workRevision )
 763+ $this->workRevision->setSrc( $this->appenddata );
 764+ break;
 765+ case "size":
 766+ if( $this->workRevision )
 767+ $this->workRevision->setSize( intval( $this->appenddata ) );
 768+ break;
 769+ default:
 770+ $this->debug( "Bad append: {$this->appendfield}" );
 771+ }
 772+ $this->appendfield = "";
 773+ $this->appenddata = "";
 774+
 775+ $parent = $this->parentTag();
 776+ xml_set_element_handler( $parser, "in_$parent", "out_$parent" );
 777+ xml_set_character_data_handler( $parser, "donothing" );
 778+ }
 779+
 780+ function in_revision( $parser, $name, $attribs ) {
 781+ $this->debug( "in_revision $name" );
 782+ switch( $name ) {
 783+ case "id":
 784+ case "timestamp":
 785+ case "comment":
 786+ case "minor":
 787+ case "text":
 788+ $this->appendfield = $name;
 789+ xml_set_element_handler( $parser, "in_nothing", "out_append" );
 790+ xml_set_character_data_handler( $parser, "char_append" );
 791+ break;
 792+ case "contributor":
 793+ $this->push( "contributor" );
 794+ xml_set_element_handler( $parser, "in_contributor", "out_contributor" );
 795+ break;
 796+ default:
 797+ return $this->throwXMLerror( "Element <$name> not allowed in a <revision>." );
 798+ }
 799+ }
 800+
 801+ function out_revision( $parser, $name ) {
 802+ $this->debug( "out_revision $name" );
 803+ $this->pop();
 804+ if( $name != "revision" ) {
 805+ return $this->throwXMLerror( "Expected </revision>, got </$name>" );
 806+ }
 807+ xml_set_element_handler( $parser, "in_page", "out_page" );
 808+
 809+ if( $this->workRevision ) {
 810+ $ok = call_user_func_array( $this->mRevisionCallback,
 811+ array( $this->workRevision, $this ) );
 812+ if( $ok ) {
 813+ $this->workSuccessCount++;
 814+ }
 815+ }
 816+ }
 817+
 818+ function in_upload( $parser, $name, $attribs ) {
 819+ $this->debug( "in_upload $name" );
 820+ switch( $name ) {
 821+ case "timestamp":
 822+ case "comment":
 823+ case "text":
 824+ case "filename":
 825+ case "src":
 826+ case "size":
 827+ $this->appendfield = $name;
 828+ xml_set_element_handler( $parser, "in_nothing", "out_append" );
 829+ xml_set_character_data_handler( $parser, "char_append" );
 830+ break;
 831+ case "contributor":
 832+ $this->push( "contributor" );
 833+ xml_set_element_handler( $parser, "in_contributor", "out_contributor" );
 834+ break;
 835+ default:
 836+ return $this->throwXMLerror( "Element <$name> not allowed in an <upload>." );
 837+ }
 838+ }
 839+
 840+ function out_upload( $parser, $name ) {
 841+ $this->debug( "out_revision $name" );
 842+ $this->pop();
 843+ if( $name != "upload" ) {
 844+ return $this->throwXMLerror( "Expected </upload>, got </$name>" );
 845+ }
 846+ xml_set_element_handler( $parser, "in_page", "out_page" );
 847+
 848+ if( $this->workRevision ) {
 849+ $ok = call_user_func_array( $this->mUploadCallback,
 850+ array( $this->workRevision, $this ) );
 851+ if( $ok ) {
 852+ $this->workUploadSuccessCount++;
 853+ }
 854+ }
 855+ }
 856+
 857+ function in_contributor( $parser, $name, $attribs ) {
 858+ $this->debug( "in_contributor $name" );
 859+ switch( $name ) {
 860+ case "username":
 861+ case "ip":
 862+ case "id":
 863+ $this->appendfield = $name;
 864+ xml_set_element_handler( $parser, "in_nothing", "out_append" );
 865+ xml_set_character_data_handler( $parser, "char_append" );
 866+ break;
 867+ default:
 868+ $this->throwXMLerror( "Invalid tag <$name> in <contributor>" );
 869+ }
 870+ }
 871+
 872+ function out_contributor( $parser, $name ) {
 873+ $this->debug( "out_contributor $name" );
 874+ $this->pop();
 875+ if( $name != "contributor" ) {
 876+ return $this->throwXMLerror( "Expected </contributor>, got </$name>" );
 877+ }
 878+ $parent = $this->parentTag();
 879+ xml_set_element_handler( $parser, "in_$parent", "out_$parent" );
 880+ }
 881+
 882+ private function push( $name ) {
 883+ array_push( $this->tagStack, $name );
 884+ $this->debug( "PUSH $name" );
 885+ }
 886+
 887+ private function pop() {
 888+ $name = array_pop( $this->tagStack );
 889+ $this->debug( "POP $name" );
 890+ return $name;
 891+ }
 892+
 893+ private function parentTag() {
 894+ $name = $this->tagStack[count( $this->tagStack ) - 1];
 895+ $this->debug( "PARENT $name" );
 896+ return $name;
 897+ }
 898+
 899+}
 900+
 901+/**
 902+ * @todo document (e.g. one-sentence class description).
 903+ * @ingroup SpecialPage
 904+ */
 905+class ImportStringSource {
 906+ function __construct( $string ) {
 907+ $this->mString = $string;
 908+ $this->mRead = false;
 909+ }
 910+
 911+ function atEnd() {
 912+ return $this->mRead;
 913+ }
 914+
 915+ function readChunk() {
 916+ if( $this->atEnd() ) {
 917+ return false;
 918+ } else {
 919+ $this->mRead = true;
 920+ return $this->mString;
 921+ }
 922+ }
 923+}
 924+
 925+/**
 926+ * @todo document (e.g. one-sentence class description).
 927+ * @ingroup SpecialPage
 928+ */
 929+class ImportStreamSource {
 930+ function __construct( $handle ) {
 931+ $this->mHandle = $handle;
 932+ }
 933+
 934+ function atEnd() {
 935+ return feof( $this->mHandle );
 936+ }
 937+
 938+ function readChunk() {
 939+ return fread( $this->mHandle, 32768 );
 940+ }
 941+
 942+ static function newFromFile( $filename ) {
 943+ $file = @fopen( $filename, 'rt' );
 944+ if( !$file ) {
 945+ return new WikiErrorMsg( "importcantopen" );
 946+ }
 947+ return new ImportStreamSource( $file );
 948+ }
 949+
 950+ static function newFromUpload( $fieldname = "xmlimport" ) {
 951+ $upload =& $_FILES[$fieldname];
 952+
 953+ if( !isset( $upload ) || !$upload['name'] ) {
 954+ return new WikiErrorMsg( 'importnofile' );
 955+ }
 956+ if( !empty( $upload['error'] ) ) {
 957+ switch($upload['error']){
 958+ case 1: # The uploaded file exceeds the upload_max_filesize directive in php.ini.
 959+ return new WikiErrorMsg( 'importuploaderrorsize' );
 960+ case 2: # The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.
 961+ return new WikiErrorMsg( 'importuploaderrorsize' );
 962+ case 3: # The uploaded file was only partially uploaded
 963+ return new WikiErrorMsg( 'importuploaderrorpartial' );
 964+ case 6: #Missing a temporary folder. Introduced in PHP 4.3.10 and PHP 5.0.3.
 965+ return new WikiErrorMsg( 'importuploaderrortemp' );
 966+ # case else: # Currently impossible
 967+ }
 968+
 969+ }
 970+ $fname = $upload['tmp_name'];
 971+ if( is_uploaded_file( $fname ) ) {
 972+ return ImportStreamSource::newFromFile( $fname );
 973+ } else {
 974+ return new WikiErrorMsg( 'importnofile' );
 975+ }
 976+ }
 977+
 978+ static function newFromURL( $url, $method = 'GET' ) {
 979+ wfDebug( __METHOD__ . ": opening $url\n" );
 980+ # Use the standard HTTP fetch function; it times out
 981+ # quicker and sorts out user-agent problems which might
 982+ # otherwise prevent importing from large sites, such
 983+ # as the Wikimedia cluster, etc.
 984+ $data = Http::request( $method, $url );
 985+ if( $data !== false ) {
 986+ $file = tmpfile();
 987+ fwrite( $file, $data );
 988+ fflush( $file );
 989+ fseek( $file, 0 );
 990+ return new ImportStreamSource( $file );
 991+ } else {
 992+ return new WikiErrorMsg( 'importcantopen' );
 993+ }
 994+ }
 995+
 996+ public static function newFromInterwiki( $interwiki, $page, $history=false ) {
 997+ if( $page == '' ) {
 998+ return new WikiErrorMsg( 'import-noarticle' );
 999+ }
 1000+ $link = Title::newFromText( "$interwiki:Special:Export/$page" );
 1001+ if( is_null( $link ) || $link->getInterwiki() == '' ) {
 1002+ return new WikiErrorMsg( 'importbadinterwiki' );
 1003+ } else {
 1004+ $params = $history ? 'history=1' : '';
 1005+ $url = $link->getFullUrl( $params );
 1006+ # For interwikis, use POST to avoid redirects.
 1007+ return ImportStreamSource::newFromURL( $url, "POST" );
 1008+ }
 1009+ }
 1010+}
Property changes on: trunk/phase3/includes/Import.php
___________________________________________________________________
Added: svn:eol-style
11011 + native