31 use Wikimedia\Assert\Assert;
91 in_array(
$contentMode, [ self::WRITE_CONTENT, self::WRITE_STUB ] ),
93 'must be one of the following constants: WRITE_CONTENT or WRITE_STUB.'
99 'must be one of the following schema versions: '
100 . implode(
',', self::$supportedSchemas )
120 'xmlns' =>
"http://www.mediawiki.org/xml/export-$ver/",
121 'xmlns:xsi' =>
"http://www.w3.org/2001/XMLSchema-instance",
132 'xsi:schemaLocation' =>
"http://www.mediawiki.org/xml/export-$ver/ " .
133 "http://www.mediawiki.org/xml/export-$ver.xsd",
135 'xml:lang' => MediaWikiServices::getInstance()->getContentLanguage()->getHtmlCode() ],
152 return " <siteinfo>\n " .
153 implode(
"\n ", $info ) .
178 return Xml::element(
'generator', [],
"MediaWiki $wgVersion" );
202 $spaces =
"<namespaces>\n";
203 $nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
205 MediaWikiServices::getInstance()->getContentLanguage()->getFormattedNamespaces()
212 'case' => $nsInfo->isCapitalized( $ns )
213 ?
'first-letter' :
'case-sensitive',
216 $spaces .=
" </namespaces>";
227 return "</mediawiki>\n";
242 $out .=
' ' .
Xml::element(
'ns', [], strval( $row->page_namespace ) ) .
"\n";
243 $out .=
' ' .
Xml::element(
'id', [], strval( $row->page_id ) ) .
"\n";
244 if ( $row->page_is_redirect ) {
246 $redirect = $page->getRedirectTarget();
249 $out .=
Xml::element(
'redirect', [
'title' => self::canonicalTitle( $redirect ) ] );
254 if ( $row->page_restrictions !=
'' ) {
256 strval( $row->page_restrictions ) ) .
"\n";
259 Hooks::run(
'XmlDumpWriterOpenPage', [ $this, &$out, $row, $this->currentTitle ] );
271 if ( $this->currentTitle !==
null ) {
272 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
275 $linkCache->clearLink( $this->currentTitle );
284 return MediaWikiServices::getInstance()->getRevisionStore();
291 return MediaWikiServices::getInstance()->getBlobStore();
309 return call_user_func_array( [ $obj, $method ],
$args );
312 }
catch ( Exception $ex ) {
313 if ( $ex instanceof
MWException || $ex instanceof RuntimeException ||
314 $ex instanceof InvalidArgumentException ) {
343 $out =
" <revision>\n";
344 $out .=
" " .
Xml::element(
'id',
null, strval( $rev->getId() ) ) .
"\n";
346 if ( $rev->getParentId() ) {
347 $out .=
" " .
Xml::element(
'parentid',
null, strval( $rev->getParentId() ) ) .
"\n";
352 if ( $rev->isDeleted( RevisionRecord::DELETED_USER ) ) {
353 $out .=
" " .
Xml::element(
'contributor', [
'deleted' =>
'deleted' ] ) .
"\n";
356 $user = $rev->getUser();
358 $user ? $user->getId() : 0,
359 $user ? $user->getName() :
''
363 if ( $rev->isMinor() ) {
364 $out .=
" <minor/>\n";
366 if ( $rev->isDeleted( RevisionRecord::DELETED_COMMENT ) ) {
367 $out .=
" " .
Xml::element(
'comment', [
'deleted' =>
'deleted' ] ) .
"\n";
369 if ( $rev->getComment()->text !=
'' ) {
379 foreach ( $rev->getSlots()->getSlots() as $slot ) {
383 if ( $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
384 $out .=
" <sha1/>\n";
390 'failed to determine sha1 for revision ' . $rev->getId()
392 $out .=
" " .
Xml::element(
'sha1',
null, strval( $sha1 ) ) .
"\n";
403 [ SlotRecord::MAIN, RevisionRecord::RAW ],
404 'Failed to load main slot content of revision ' . $rev->getId()
409 Hooks::run(
'XmlDumpWriterWriteRevision', [ &$writer, &$out, $row, $text, $rev ] );
411 $out .=
" </revision>\n";
423 $isMain = $slot->
getRole() === SlotRecord::MAIN;
426 if ( !$isV11 && !$isMain ) {
447 $contentFormat = $contentHandler->getDefaultFormat();
451 $out .= $indent .
Xml::element(
'model',
null, strval( $contentModel ) ) .
"\n";
452 $out .= $indent .
Xml::element(
'format',
null, strval( $contentFormat ) ) .
"\n";
455 'xml:space' =>
'preserve',
460 'failed to determine size for slot ' . $slot->
getRole() .
' of revision '
470 'failed to determine sha1 for slot ' . $slot->
getRole() .
' of revision '
480 'failed to load content for slot ' . $slot->
getRole() .
' of revision '
485 $out .= $indent .
Xml::element(
'text', $textAttributes ) .
"\n";
489 } elseif (
$contentMode === self::WRITE_STUB_DELETED ) {
491 $textAttributes[
'deleted'] =
'deleted';
492 $out .= $indent .
Xml::element(
'text', $textAttributes ) .
"\n";
496 $textAttributes[
'location'] = $slot->
getAddress();
509 }
catch ( InvalidArgumentException $ex ) {
511 .
' of revision ' . $slot->
getRevision() .
': ' . $ex->getMessage() );
516 $textAttributes[
'id'] = $textId;
519 $out .= $indent .
Xml::element(
'text', $textAttributes ) .
"\n";
539 $contentHandler =
$content->getContentHandler();
540 $contentFormat = $contentHandler->getDefaultFormat();
547 $data =
$content->serialize( $contentFormat );
550 $data = $contentHandler->exportTransform( $data, $contentFormat );
551 $textAttributes[
'bytes'] = $size = strlen( $data );
552 $out .= $indent .
Xml::elementClean(
'text', $textAttributes, strval( $data ) ) .
"\n";
566 $out =
" <logitem>\n";
567 $out .=
" " .
Xml::element(
'id',
null, strval( $row->log_id ) ) .
"\n";
572 $out .=
" " .
Xml::element(
'contributor', [
'deleted' =>
'deleted' ] ) .
"\n";
578 $out .=
" " .
Xml::element(
'comment', [
'deleted' =>
'deleted' ] ) .
"\n";
581 if ( $comment !=
'' ) {
586 $out .=
" " .
Xml::element(
'type',
null, strval( $row->log_type ) ) .
"\n";
587 $out .=
" " .
Xml::element(
'action',
null, strval( $row->log_action ) ) .
"\n";
590 $out .=
" " .
Xml::element(
'text', [
'deleted' =>
'deleted' ] ) .
"\n";
595 [
'xml:space' =>
'preserve' ],
596 strval( $row->log_params ) ) .
"\n";
599 $out .=
" </logitem>\n";
611 return $indent .
Xml::element(
'timestamp',
null, $ts ) .
"\n";
621 $out = $indent .
"<contributor>\n";
623 $out .= $indent .
" " .
Xml::elementClean(
'username',
null, strval( $text ) ) .
"\n";
624 $out .= $indent .
" " .
Xml::element(
'id',
null, strval( $id ) ) .
"\n";
628 $out .= $indent .
"</contributor>\n";
639 if ( $row->page_namespace ==
NS_FILE ) {
640 $img = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()
641 ->newFile( $row->page_title );
642 if ( $img && $img->exists() ) {
644 foreach ( array_reverse( $img->getHistory() ) as $ver ) {
660 if (
$file->isOld() ) {
662 '@phan-var OldLocalFile $file';
668 if ( $dumpContents ) {
669 $be =
$file->getRepo()->getBackend();
670 # Dump file as base64
671 # Uses only XML-safe characters, so does not need escaping
672 # @todo Too bad this loads the contents into memory (script might swap)
673 $contents =
' <contents encoding="base64">' .
674 chunk_split( base64_encode(
675 $be->getFileContents( [
'src' =>
$file->getPath() ] ) ) ) .
681 $comment =
Xml::element(
'comment', [
'deleted' =>
'deleted' ] );
685 return " <upload>\n" .
688 " " . $comment .
"\n" .
710 if (
$title->isExternal() ) {
711 return $title->getPrefixedText();
714 $prefix = MediaWikiServices::getInstance()->getContentLanguage()->
715 getFormattedNsText(
$title->getNamespace() );
720 if ( $prefix !==
'' ) {
724 return $prefix .
$title->getText();