MediaWiki master
XmlDumpWriter.php
Go to the documentation of this file.
1<?php
12namespace MediaWiki\Export;
13
14use ErrorException;
15use InvalidArgumentException;
37use RuntimeException;
38use Wikimedia\Assert\Assert;
39use Wikimedia\IPUtils;
40use Wikimedia\Timestamp\TimestampFormat as TS;
41
46
48 public const WRITE_CONTENT = 0;
49
51 public const WRITE_STUB = 1;
52
57 private const WRITE_STUB_DELETED = 2;
58
63 public static $supportedSchemas = [
66 ];
67
73 private $schemaVersion;
74
80 private $currentTitle = null;
81
85 private $contentMode;
86
88 private $hookRunner;
89
91 private $commentStore;
92
101 public function __construct(
102 $contentMode = self::WRITE_CONTENT,
103 $schemaVersion = XML_DUMP_SCHEMA_VERSION_11,
104 ?HookContainer $hookContainer = null,
105 ?CommentStore $commentStore = null
106 ) {
107 Assert::parameter(
108 in_array( $contentMode, [ self::WRITE_CONTENT, self::WRITE_STUB ], true ),
109 '$contentMode',
110 'must be one of the following constants: WRITE_CONTENT or WRITE_STUB.'
111 );
112
113 Assert::parameter(
114 in_array( $schemaVersion, self::$supportedSchemas, true ),
115 '$schemaVersion',
116 'must be one of the following schema versions: '
117 . implode( ',', self::$supportedSchemas )
118 );
119
120 $this->contentMode = $contentMode;
121 $this->schemaVersion = $schemaVersion;
122 $this->hookRunner = new HookRunner(
123 $hookContainer ?? MediaWikiServices::getInstance()->getHookContainer()
124 );
125 $this->commentStore = $commentStore ?? MediaWikiServices::getInstance()->getCommentStore();
126 }
127
138 public function openStream() {
139 $ver = $this->schemaVersion;
140 return Xml::element( 'mediawiki', [
141 'xmlns' => "http://www.mediawiki.org/xml/export-$ver/",
142 'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
143 /*
144 * When a new version of the schema is created, it needs staging on mediawiki.org.
145 * This requires a change in the operations/mediawiki-config git repo.
146 *
147 * Create a changeset like https://gerrit.wikimedia.org/r/#/c/149643/ in which
148 * you copy in the new xsd file.
149 *
150 * After it is reviewed, merged and deployed (sync-docroot), the index.html needs purging.
151 * echo "https://www.mediawiki.org/xml/index.html" | mwscript purgeList.php --wiki=aawiki
152 */
153 'xsi:schemaLocation' => "http://www.mediawiki.org/xml/export-$ver/ " .
154 "http://www.mediawiki.org/xml/export-$ver.xsd",
155 'version' => $ver,
156 'xml:lang' => MediaWikiServices::getInstance()->getContentLanguage()->getHtmlCode() ],
157 null ) .
158 "\n" .
159 $this->siteInfo();
160 }
161
165 private function siteInfo() {
166 $info = [
167 $this->sitename(),
168 $this->dbname(),
169 $this->homelink(),
170 $this->generator(),
171 $this->caseSetting(),
172 $this->namespaces() ];
173 return " <siteinfo>\n " .
174 implode( "\n ", $info ) .
175 "\n </siteinfo>\n";
176 }
177
181 private function sitename() {
182 $sitename = MediaWikiServices::getInstance()->getMainConfig()->get(
184 return Xml::element( 'sitename', [], $sitename );
185 }
186
190 private function dbname() {
191 $dbname = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::DBname );
192 return Xml::element( 'dbname', [], $dbname );
193 }
194
198 private function generator() {
199 return Xml::element( 'generator', [], 'MediaWiki ' . MW_VERSION );
200 }
201
205 private function homelink() {
206 return Xml::element( 'base', [], Title::newMainPage()->getCanonicalURL() );
207 }
208
212 private function caseSetting() {
213 $capitalLinks = MediaWikiServices::getInstance()->getMainConfig()->get(
215 // "case-insensitive" option is reserved for future
216 $sensitivity = $capitalLinks ? 'first-letter' : 'case-sensitive';
217 return Xml::element( 'case', [], $sensitivity );
218 }
219
223 private function namespaces() {
224 $spaces = "<namespaces>\n";
225 $nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
226 foreach (
227 MediaWikiServices::getInstance()->getContentLanguage()->getFormattedNamespaces()
228 as $ns => $title
229 ) {
230 $spaces .= ' ' .
231 Xml::element( 'namespace',
232 [
233 'key' => $ns,
234 'case' => $nsInfo->isCapitalized( $ns )
235 ? 'first-letter' : 'case-sensitive',
236 ], $title ) . "\n";
237 }
238 $spaces .= " </namespaces>";
239 return $spaces;
240 }
241
248 public function closeStream() {
249 return "</mediawiki>\n";
250 }
251
259 public function openPage( $row ) {
260 $out = " <page>\n";
261 $this->currentTitle = Title::newFromRow( $row );
262 $canonicalTitle = self::canonicalTitle( $this->currentTitle );
263 $out .= ' ' . Xml::elementClean( 'title', [], $canonicalTitle ) . "\n";
264 $out .= ' ' . Xml::element( 'ns', [], strval( $row->page_namespace ) ) . "\n";
265 $out .= ' ' . Xml::element( 'id', [], strval( $row->page_id ) ) . "\n";
266 if ( $row->page_is_redirect ) {
267 $services = MediaWikiServices::getInstance();
268 $page = $services->getWikiPageFactory()->newFromTitle( $this->currentTitle );
269 $redirectStore = $services->getRedirectStore();
270 $redirect = $this->invokeLenient(
271 static function () use ( $page, $redirectStore ) {
272 return $redirectStore->getRedirectTarget( $page );
273 },
274 'Failed to get redirect target of page ' . $page->getId()
275 );
276 $redirect = Title::castFromLinkTarget( $redirect );
277 if ( $redirect instanceof Title && $redirect->isValidRedirectTarget() ) {
278 $out .= ' ';
279 $out .= Xml::element( 'redirect', [ 'title' => self::canonicalTitle( $redirect ) ] );
280 $out .= "\n";
281 }
282 }
283 $this->hookRunner->onXmlDumpWriterOpenPage( $this, $out, $row, $this->currentTitle );
284
285 return $out;
286 }
287
294 public function closePage() {
295 if ( $this->currentTitle !== null ) {
296 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
297 // In rare cases, link cache has the same key for some pages which
298 // might be read as part of the same batch. T220424 and T220316
299 $linkCache->clearLink( $this->currentTitle );
300 }
301 return " </page>\n";
302 }
303
307 private function getRevisionStore() {
308 return MediaWikiServices::getInstance()->getRevisionStore();
309 }
310
314 private function getBlobStore() {
315 // @phan-suppress-next-line PhanTypeMismatchReturnSuperType
316 return MediaWikiServices::getInstance()->getBlobStore();
317 }
318
328 private function invokeLenient( $callback, $warning ) {
329 try {
330 return $callback();
331 } catch ( SuppressedDataException ) {
332 return null;
333 } catch ( MWException | RuntimeException | InvalidArgumentException | ErrorException $ex ) {
334 MWDebug::warning( $warning . ': ' . $ex->getMessage() );
335 return null;
336 }
337 }
338
349 public function writeRevision( $row, $slotRows = null ) {
350 $rev = $this->getRevisionStore()->newRevisionFromRowAndSlots(
351 $row,
352 $slotRows,
353 0,
354 $this->currentTitle
355 );
356
357 $out = " <revision>\n";
358 $out .= " " . Xml::element( 'id', null, strval( $rev->getId() ) ) . "\n";
359
360 if ( $rev->getParentId() ) {
361 $out .= " " . Xml::element( 'parentid', null, strval( $rev->getParentId() ) ) . "\n";
362 }
363
364 $out .= $this->writeTimestamp( $rev->getTimestamp() );
365
366 if ( $rev->isDeleted( RevisionRecord::DELETED_USER ) ) {
367 $out .= " " . Xml::element( 'contributor', [ 'deleted' => 'deleted' ] ) . "\n";
368 } else {
369 // empty values get written out as uid 0, see T224221
370 $user = $rev->getUser();
371 $out .= $this->writeContributor(
372 $user ? $user->getId() : 0,
373 $user ? $user->getName() : ''
374 );
375 }
376
377 if ( $rev->isMinor() ) {
378 $out .= " <minor/>\n";
379 }
380 if ( $rev->isDeleted( RevisionRecord::DELETED_COMMENT ) ) {
381 $out .= " " . Xml::element( 'comment', [ 'deleted' => 'deleted' ] ) . "\n";
382 } else {
383 if ( $rev->getComment()->text != '' ) {
384 $out .= " "
385 . Xml::elementClean( 'comment', [], strval( $rev->getComment()->text ) )
386 . "\n";
387 }
388 }
389
390 $contentMode = $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ? self::WRITE_STUB_DELETED
391 : $this->contentMode;
392
393 $slots = $rev->getSlots()->getSlots();
394
395 // use predictable order, put main slot first
396 ksort( $slots );
397 $out .= $this->writeSlot( $slots[SlotRecord::MAIN], $contentMode );
398
399 foreach ( $slots as $role => $slot ) {
400 if ( $role === SlotRecord::MAIN ) {
401 continue;
402 }
403 $out .= $this->writeSlot( $slot, $contentMode );
404 }
405
406 if ( $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
407 $out .= " <sha1/>\n";
408 } else {
409 $sha1 = $this->invokeLenient(
410 static function () use ( $rev ) {
411 return $rev->getSha1();
412 },
413 'failed to determine sha1 for revision ' . $rev->getId()
414 );
415 $out .= " " . Xml::element( 'sha1', null, strval( $sha1 ) ) . "\n";
416 }
417
418 $text = '';
419 if ( $contentMode === self::WRITE_CONTENT ) {
421 $content = $this->invokeLenient(
422 static function () use ( $rev ) {
423 return $rev->getMainContentRaw();
424 },
425 'Failed to load main slot content of revision ' . $rev->getId()
426 );
427
428 $text = $content ? $content->serialize() : '';
429 }
430 $this->hookRunner->onXmlDumpWriterWriteRevision( $this, $out, $row, $text, $rev );
431
432 $out .= " </revision>\n";
433
434 return $out;
435 }
436
443 private function writeSlot( SlotRecord $slot, $contentMode ) {
444 $isMain = $slot->getRole() === SlotRecord::MAIN;
445 $isV11 = $this->schemaVersion >= XML_DUMP_SCHEMA_VERSION_11;
446
447 if ( !$isV11 && !$isMain ) {
448 // ignore extra slots
449 return '';
450 }
451
452 $out = '';
453 $indent = ' ';
454
455 if ( !$isMain ) {
456 // non-main slots are wrapped into an additional element.
457 $out .= ' ' . Xml::openElement( 'content' ) . "\n";
458 $indent .= ' ';
459 $out .= $indent . Xml::element( 'role', null, strval( $slot->getRole() ) ) . "\n";
460 }
461
462 if ( $isV11 ) {
463 $out .= $indent . Xml::element( 'origin', null, strval( $slot->getOrigin() ) ) . "\n";
464 }
465
466 $contentModel = $slot->getModel();
467 $contentHandlerFactory = MediaWikiServices::getInstance()->getContentHandlerFactory();
468 $contentHandler = null;
469
470 try {
471 $contentHandler = $contentHandlerFactory->getContentHandler( $contentModel );
472
473 } catch ( MWUnknownContentModelException ) {
474 // A content model should not be removed, as this would cause old revisions
475 // to fail to render. If this does happen, let dumps keep going but warn.
476 // To stop these warnings, register a fallback content model like so:
477 // $wgContentHandlers['Your.Removed.Handler'] = 'FallbackContentHandler'
478 MWDebug::warning( 'Revision ' . $slot->getRevision() . ' is using an unknown '
479 . ' content model, falling back to FallbackContentHandler.' );
480 $contentModel = CONTENT_MODEL_UNKNOWN;
481 $contentHandler = $contentHandlerFactory->getContentHandler( $contentModel );
482 }
483 $contentFormat = $contentHandler->getDefaultFormat();
484
485 // XXX: The content format is only relevant when actually outputting serialized content.
486 // It should probably be an attribute on the text tag.
487 $out .= $indent . Xml::element( 'model', null, strval( $contentModel ) ) . "\n";
488 $out .= $indent . Xml::element( 'format', null, strval( $contentFormat ) ) . "\n";
489
490 $textAttributes = [
491 'bytes' => $this->invokeLenient(
492 static function () use ( $slot ) {
493 return $slot->getSize();
494 },
495 'failed to determine size for slot ' . $slot->getRole() . ' of revision '
496 . $slot->getRevision()
497 ) ?: '0'
498 ];
499
500 if ( $isV11 ) {
501 $textAttributes['sha1'] = $this->invokeLenient(
502 static function () use ( $slot ) {
503 return $slot->getSha1();
504 },
505 'failed to determine sha1 for slot ' . $slot->getRole() . ' of revision '
506 . $slot->getRevision()
507 ) ?: '';
508 }
509
510 if ( $contentMode === self::WRITE_CONTENT ) {
511 $content = $this->invokeLenient(
512 static function () use ( $slot ) {
513 return $slot->getContent();
514 },
515 'failed to load content for slot ' . $slot->getRole() . ' of revision '
516 . $slot->getRevision()
517 );
518
519 if ( $content === null ) {
520 $out .= $indent . Xml::element( 'text', $textAttributes ) . "\n";
521 } else {
522 $out .= $this->writeText( $content, $textAttributes, $indent );
523 }
524 } elseif ( $contentMode === self::WRITE_STUB_DELETED ) {
525 // write <text> placeholder tag
526 $textAttributes['deleted'] = 'deleted';
527 $out .= $indent . Xml::element( 'text', $textAttributes ) . "\n";
528 } else {
529 // write <text> stub tag
530 if ( $isV11 ) {
531 $textAttributes['location'] = $slot->getAddress();
532 }
533 $schema = null;
534
535 if ( $isMain ) {
536 // Output the numerical text ID if possible, for backwards compatibility.
537 // Note that this is currently the ONLY reason we have a BlobStore here at all.
538 // When removing this line, check whether the BlobStore has become unused.
539 try {
540 // NOTE: this will only work for addresses of the form "tt:12345" or "es:DB://cluster1/1234".
541 // If we want to support other kinds of addresses in the future,
542 // we will have to silently ignore failures here.
543 // For now, this fails for "tt:0", which is present in the WMF production
544 // database as of July 2019, due to data corruption.
545 [ $schema, $textId ] = $this->getBlobStore()->splitBlobAddress( $slot->getAddress() );
546 } catch ( InvalidArgumentException $ex ) {
547 MWDebug::warning( 'Bad content address for slot ' . $slot->getRole()
548 . ' of revision ' . $slot->getRevision() . ': ' . $ex->getMessage() );
549 $textId = 0;
550 }
551
552 if ( $schema === 'tt' ) {
553 $textAttributes['id'] = $textId;
554 } elseif ( $schema === 'es' ) {
555 $textAttributes['id'] = bin2hex( $textId );
556 }
557 }
558
559 $out .= $indent . Xml::element( 'text', $textAttributes ) . "\n";
560 }
561
562 if ( !$isMain ) {
563 $out .= ' ' . Xml::closeElement( 'content' ) . "\n";
564 }
565
566 return $out;
567 }
568
576 private function writeText( Content $content, $textAttributes, $indent ) {
577 $contentHandler = $content->getContentHandler();
578 $contentFormat = $contentHandler->getDefaultFormat();
579
580 if ( $content instanceof TextContent ) {
581 // HACK: For text based models, bypass the serialization step. This allows extensions (like Flow)
582 // that use incompatible combinations of serialization format and content model.
583 $data = $content->getText();
584 } else {
585 $data = $content->serialize( $contentFormat );
586 }
587
588 $data = $contentHandler->exportTransform( $data, $contentFormat );
589 // make sure to use the actual size
590 $textAttributes['bytes'] = strlen( $data );
591 $textAttributes['xml:space'] = 'preserve';
592 return $indent . Xml::elementClean( 'text', $textAttributes, strval( $data ) ) . "\n";
593 }
594
602 public function writeLogItem( $row ) {
603 $out = " <logitem>\n";
604 $out .= " " . Xml::element( 'id', null, strval( $row->log_id ) ) . "\n";
605
606 $out .= $this->writeTimestamp( $row->log_timestamp, " " );
607
608 if ( $row->log_deleted & LogPage::DELETED_USER ) {
609 $out .= " " . Xml::element( 'contributor', [ 'deleted' => 'deleted' ] ) . "\n";
610 } else {
611 $out .= $this->writeContributor( $row->actor_user, $row->actor_name, " " );
612 }
613
614 if ( $row->log_deleted & LogPage::DELETED_COMMENT ) {
615 $out .= " " . Xml::element( 'comment', [ 'deleted' => 'deleted' ] ) . "\n";
616 } else {
617 $comment = $this->commentStore->getComment( 'log_comment', $row )->text;
618 if ( $comment != '' ) {
619 $out .= " " . Xml::elementClean( 'comment', null, strval( $comment ) ) . "\n";
620 }
621 }
622
623 $out .= " " . Xml::element( 'type', null, strval( $row->log_type ) ) . "\n";
624 $out .= " " . Xml::element( 'action', null, strval( $row->log_action ) ) . "\n";
625
626 if ( $row->log_deleted & LogPage::DELETED_ACTION ) {
627 $out .= " " . Xml::element( 'text', [ 'deleted' => 'deleted' ] ) . "\n";
628 } else {
629 $title = Title::makeTitle( $row->log_namespace, $row->log_title );
630 $out .= " " . Xml::elementClean( 'logtitle', null, self::canonicalTitle( $title ) ) . "\n";
631 $out .= " " . Xml::elementClean( 'params',
632 [ 'xml:space' => 'preserve' ],
633 strval( $row->log_params ) ) . "\n";
634 }
635
636 $out .= " </logitem>\n";
637
638 return $out;
639 }
640
646 public function writeTimestamp( $timestamp, $indent = " " ) {
647 $ts = wfTimestamp( TS::ISO_8601, $timestamp );
648 return $indent . Xml::element( 'timestamp', null, $ts ) . "\n";
649 }
650
657 public function writeContributor( $id, $text, $indent = " " ) {
658 $out = $indent . "<contributor>\n";
659 if ( $id || !IPUtils::isValid( $text ) ) {
660 $out .= $indent . " " . Xml::elementClean( 'username', null, strval( $text ) ) . "\n";
661 $out .= $indent . " " . Xml::element( 'id', null, strval( $id ) ) . "\n";
662 } else {
663 $out .= $indent . " " . Xml::elementClean( 'ip', null, strval( $text ) ) . "\n";
664 }
665 $out .= $indent . "</contributor>\n";
666 return $out;
667 }
668
675 public function writeUploads( $row, $dumpContents = false ) {
676 if ( $row->page_namespace == NS_FILE ) {
677 $img = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()
678 ->newFile( $row->page_title );
679 if ( $img && $img->exists() ) {
680 $out = '';
681 foreach ( array_reverse( $img->getHistory() ) as $ver ) {
682 $out .= $this->writeUpload( $ver, $dumpContents );
683 }
684 $out .= $this->writeUpload( $img, $dumpContents );
685 return $out;
686 }
687 }
688 return '';
689 }
690
696 private function writeUpload( $file, $dumpContents = false ) {
697 if ( $file->isOld() ) {
699 '@phan-var OldLocalFile $file';
700 $archiveName = " " .
701 Xml::element( 'archivename', null, $file->getArchiveName() ) . "\n";
702 } else {
703 $archiveName = '';
704 }
705 if ( $dumpContents ) {
706 $be = $file->getRepo()->getBackend();
707 # Dump file as base64
708 # Uses only XML-safe characters, so does not need escaping
709 # @todo Too bad this loads the contents into memory (script might swap)
710 $contents = ' <contents encoding="base64">' .
711 chunk_split( base64_encode(
712 $be->getFileContents( [ 'src' => $file->getPath() ] ) ) ) .
713 " </contents>\n";
714 } else {
715 $contents = '';
716 }
717 $uploader = $file->getUploader( File::FOR_PUBLIC );
718 if ( $uploader ) {
719 $uploader = $this->writeContributor( $uploader->getId(), $uploader->getName() );
720 } else {
721 $uploader = Xml::element( 'contributor', [ 'deleted' => 'deleted' ] ) . "\n";
722 }
723 $comment = $file->getDescription( File::FOR_PUBLIC );
724 if ( ( $comment ?? '' ) !== '' ) {
725 $comment = Xml::elementClean( 'comment', null, $comment );
726 } else {
727 $comment = Xml::element( 'comment', [ 'deleted' => 'deleted' ] );
728 }
729 return " <upload>\n" .
730 $this->writeTimestamp( $file->getTimestamp() ) .
731 $uploader .
732 " " . $comment . "\n" .
733 " " . Xml::element( 'filename', null, $file->getName() ) . "\n" .
734 $archiveName .
735 " " . Xml::element( 'src', null, $file->getCanonicalUrl() ) . "\n" .
736 " " . Xml::element( 'size', null, (string)( $file->getSize() ?: 0 ) ) . "\n" .
737 " " . Xml::element( 'sha1base36', null, $file->getSha1() ) . "\n" .
738 " " . Xml::element( 'rel', null, $file->getRel() ) . "\n" .
739 $contents .
740 " </upload>\n";
741 }
742
753 public static function canonicalTitle( Title $title ) {
754 if ( $title->isExternal() ) {
755 return $title->getPrefixedText();
756 }
757
758 $prefix = MediaWikiServices::getInstance()->getContentLanguage()->
759 getFormattedNsText( $title->getNamespace() );
760
761 // @todo Emit some kind of warning to the user if $title->getNamespace() !==
762 // NS_MAIN and $prefix === '' (viz. pages in an unregistered namespace)
763
764 if ( $prefix !== '' ) {
765 $prefix .= ':';
766 }
767
768 return $prefix . $title->getText();
769 }
770}
771
773class_alias( XmlDumpWriter::class, 'XmlDumpWriter' );
const NS_FILE
Definition Defines.php:57
const MW_VERSION
The running version of MediaWiki.
Definition Defines.php:23
const XML_DUMP_SCHEMA_VERSION_11
Definition Defines.php:349
const XML_DUMP_SCHEMA_VERSION_10
Definition Defines.php:348
const CONTENT_MODEL_UNKNOWN
Definition Defines.php:242
wfTimestamp( $outputtype=TS::UNIX, $ts=0)
Get a timestamp string in one of various formats.
Handle database storage of comments such as edit summaries and log reasons.
Content object implementation for representing flat text.
Debug toolbar.
Definition MWDebug.php:35
makeTitle( $linkId)
Convert a link ID to a Title.to override Title
Exception thrown when an unregistered content model is requested.
closePage()
Closes a "<page>" section on the output stream.
openStream()
Opens the XML output stream's root "<mediawiki>" element.
openPage( $row)
Opens a "<page>" section on the output stream, with data from the given database row.
const WRITE_CONTENT
Output serialized revision content.
static string[] $supportedSchemas
the schema versions supported for output @final
writeUploads( $row, $dumpContents=false)
Warning! This data is potentially inconsistent.
writeRevision( $row, $slotRows=null)
Dumps a "<revision>" section on the output stream, with data filled in from the given database row.
static canonicalTitle(Title $title)
Return prefixed text form of title, but using the content language's canonical namespace.
const WRITE_STUB
Only output subs for revision content.
__construct( $contentMode=self::WRITE_CONTENT, $schemaVersion=XML_DUMP_SCHEMA_VERSION_11, ?HookContainer $hookContainer=null, ?CommentStore $commentStore=null)
writeContributor( $id, $text, $indent=" ")
writeLogItem( $row)
Dumps a "<logitem>" section on the output stream, with data filled in from the given database row.
writeTimestamp( $timestamp, $indent=" ")
closeStream()
Closes the output stream with the closing root element.
Implements some public methods and some protected utility functions which are required by multiple ch...
Definition File.php:79
Old file in the oldimage table.
This class provides an implementation of the core hook interfaces, forwarding hook calls to HookConta...
static openElement( $element, $attribs=[])
Identical to rawElement(), but has no third parameter and omits the end tag (and the self-closing '/'...
Definition Html.php:332
static closeElement( $element)
Returns "</$element>".
Definition Html.php:369
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition Html.php:308
Class to simplify the use of log pages.
Definition LogPage.php:35
A class containing constants representing the names of configuration variables.
const CapitalLinks
Name constant for the CapitalLinks setting, for use with Config::get()
const DBname
Name constant for the DBname setting, for use with Config::get()
const Sitename
Name constant for the Sitename setting, for use with Config::get()
Service locator for MediaWiki core services.
static getInstance()
Returns the global default instance of the top level service locator.
Exception representing a failure to look up a revision.
Page revision base class.
Service for looking up page revisions.
Value object representing a content slot associated with a page revision.
getContent()
Returns the Content of the given slot.
getRole()
Returns the role of the slot.
getSha1()
Returns the content size.
getSize()
Returns the content size.
getAddress()
Returns the address of this slot's content.
getModel()
Returns the content model.
getOrigin()
Returns the revision ID of the revision that originated the slot's content.
getRevision()
Returns the ID of the revision this slot is associated with.
Exception raised in response to an audience check when attempting to access suppressed information wi...
Service for storing and loading Content objects representing revision data blobs.
Represents a title within MediaWiki.
Definition Title.php:69
isValidRedirectTarget()
Check if this Title is a valid redirect target.
Definition Title.php:3446
getNamespace()
Get the namespace index, i.e.
Definition Title.php:1037
getText()
Get the text form (spaces not underscores) of the main part.
Definition Title.php:1010
getPrefixedText()
Get the prefixed title with spaces.
Definition Title.php:1857
Module of static functions for generating XML.
Definition Xml.php:19
Content objects represent page content, e.g.
Definition Content.php:28
isExternal()
Whether this LinkTarget has an interwiki component.
element(SerializerNode $parent, SerializerNode $node, $contents)