MediaWiki master
XmlDumpWriter.php
Go to the documentation of this file.
1<?php
12namespace MediaWiki\Export;
13
14use ErrorException;
15use InvalidArgumentException;
38use RuntimeException;
39use Wikimedia\Assert\Assert;
40use Wikimedia\IPUtils;
41use Wikimedia\Timestamp\TimestampFormat as TS;
42
47
49 public const WRITE_CONTENT = 0;
50
52 public const WRITE_STUB = 1;
53
58 private const WRITE_STUB_DELETED = 2;
59
64 public static $supportedSchemas = [
67 ];
68
74 private $schemaVersion;
75
81 private $currentTitle = null;
82
86 private $contentMode;
87
89 private $hookRunner;
90
92 private $commentStore;
93
102 public function __construct(
103 $contentMode = self::WRITE_CONTENT,
104 $schemaVersion = XML_DUMP_SCHEMA_VERSION_11,
105 ?HookContainer $hookContainer = null,
106 ?CommentStore $commentStore = null
107 ) {
108 Assert::parameter(
109 in_array( $contentMode, [ self::WRITE_CONTENT, self::WRITE_STUB ], true ),
110 '$contentMode',
111 'must be one of the following constants: WRITE_CONTENT or WRITE_STUB.'
112 );
113
114 Assert::parameter(
115 in_array( $schemaVersion, self::$supportedSchemas, true ),
116 '$schemaVersion',
117 'must be one of the following schema versions: '
118 . implode( ',', self::$supportedSchemas )
119 );
120
121 $this->contentMode = $contentMode;
122 $this->schemaVersion = $schemaVersion;
123 $this->hookRunner = new HookRunner(
124 $hookContainer ?? MediaWikiServices::getInstance()->getHookContainer()
125 );
126 $this->commentStore = $commentStore ?? MediaWikiServices::getInstance()->getCommentStore();
127 }
128
139 public function openStream() {
140 $ver = $this->schemaVersion;
141 return Xml::element( 'mediawiki', [
142 'xmlns' => "http://www.mediawiki.org/xml/export-$ver/",
143 'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
144 /*
145 * When a new version of the schema is created, it needs staging on mediawiki.org.
146 * This requires a change in the operations/mediawiki-config git repo.
147 *
148 * Create a changeset like https://gerrit.wikimedia.org/r/#/c/149643/ in which
149 * you copy in the new xsd file.
150 *
151 * After it is reviewed, merged and deployed (sync-docroot), the index.html needs purging.
152 * echo "https://www.mediawiki.org/xml/index.html" | mwscript purgeList.php --wiki=aawiki
153 */
154 'xsi:schemaLocation' => "http://www.mediawiki.org/xml/export-$ver/ " .
155 "http://www.mediawiki.org/xml/export-$ver.xsd",
156 'version' => $ver,
157 'xml:lang' => MediaWikiServices::getInstance()->getContentLanguage()->getHtmlCode() ],
158 null ) .
159 "\n" .
160 $this->siteInfo();
161 }
162
166 private function siteInfo() {
167 $info = [
168 $this->sitename(),
169 $this->dbname(),
170 $this->homelink(),
171 $this->generator(),
172 $this->caseSetting(),
173 $this->namespaces() ];
174 return " <siteinfo>\n " .
175 implode( "\n ", $info ) .
176 "\n </siteinfo>\n";
177 }
178
182 private function sitename() {
183 $sitename = MediaWikiServices::getInstance()->getMainConfig()->get(
185 return Xml::element( 'sitename', [], $sitename );
186 }
187
191 private function dbname() {
192 $dbname = MediaWikiServices::getInstance()->getMainConfig()->get( MainConfigNames::DBname );
193 return Xml::element( 'dbname', [], $dbname );
194 }
195
199 private function generator() {
200 return Xml::element( 'generator', [], 'MediaWiki ' . MW_VERSION );
201 }
202
206 private function homelink() {
207 return Xml::element( 'base', [], Title::newMainPage()->getCanonicalURL() );
208 }
209
213 private function caseSetting() {
214 $capitalLinks = MediaWikiServices::getInstance()->getMainConfig()->get(
216 // "case-insensitive" option is reserved for future
217 $sensitivity = $capitalLinks ? 'first-letter' : 'case-sensitive';
218 return Xml::element( 'case', [], $sensitivity );
219 }
220
224 private function namespaces() {
225 $spaces = "<namespaces>\n";
226 $nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
227 foreach (
228 MediaWikiServices::getInstance()->getContentLanguage()->getFormattedNamespaces()
229 as $ns => $title
230 ) {
231 $spaces .= ' ' .
232 Xml::element( 'namespace',
233 [
234 'key' => $ns,
235 'case' => $nsInfo->isCapitalized( $ns )
236 ? 'first-letter' : 'case-sensitive',
237 ], $title ) . "\n";
238 }
239 $spaces .= " </namespaces>";
240 return $spaces;
241 }
242
249 public function closeStream() {
250 return "</mediawiki>\n";
251 }
252
260 public function openPage( $row ) {
261 $out = " <page>\n";
262 $this->currentTitle = Title::newFromRow( $row );
263 $canonicalTitle = self::canonicalTitle( $this->currentTitle );
264 $out .= ' ' . Xml::elementClean( 'title', [], $canonicalTitle ) . "\n";
265 $out .= ' ' . Xml::element( 'ns', [], strval( $row->page_namespace ) ) . "\n";
266 $out .= ' ' . Xml::element( 'id', [], strval( $row->page_id ) ) . "\n";
267 if ( $row->page_is_redirect ) {
268 $services = MediaWikiServices::getInstance();
269 $page = $services->getWikiPageFactory()->newFromTitle( $this->currentTitle );
270 $redirectStore = $services->getRedirectStore();
271 $redirect = $this->invokeLenient(
272 static function () use ( $page, $redirectStore ) {
273 return $redirectStore->getRedirectTarget( $page );
274 },
275 'Failed to get redirect target of page ' . $page->getId()
276 );
277 $redirect = Title::castFromLinkTarget( $redirect );
278 if ( $redirect instanceof Title && $redirect->isValidRedirectTarget() ) {
279 $out .= ' ';
280 $out .= Xml::element( 'redirect', [ 'title' => self::canonicalTitle( $redirect ) ] );
281 $out .= "\n";
282 }
283 }
284 $this->hookRunner->onXmlDumpWriterOpenPage( $this, $out, $row, $this->currentTitle );
285
286 return $out;
287 }
288
295 public function closePage() {
296 if ( $this->currentTitle !== null ) {
297 $linkCache = MediaWikiServices::getInstance()->getLinkCache();
298 // In rare cases, link cache has the same key for some pages which
299 // might be read as part of the same batch. T220424 and T220316
300 $linkCache->clearLink( $this->currentTitle );
301 }
302 return " </page>\n";
303 }
304
308 private function getRevisionStore() {
309 return MediaWikiServices::getInstance()->getRevisionStore();
310 }
311
315 private function getBlobStore() {
316 // @phan-suppress-next-line PhanTypeMismatchReturnSuperType
317 return MediaWikiServices::getInstance()->getBlobStore();
318 }
319
329 private function invokeLenient( $callback, $warning ) {
330 try {
331 return $callback();
332 } catch ( SuppressedDataException ) {
333 return null;
334 } catch ( MWException | RuntimeException | InvalidArgumentException | ErrorException $ex ) {
335 MWDebug::warning( $warning . ': ' . $ex->getMessage() );
336 return null;
337 }
338 }
339
350 public function writeRevision( $row, $slotRows = null ) {
351 $rev = $this->getRevisionStore()->newRevisionFromRowAndSlots(
352 $row,
353 $slotRows,
354 0,
355 $this->currentTitle
356 );
357
358 $out = " <revision>\n";
359 $out .= " " . Xml::element( 'id', null, strval( $rev->getId() ) ) . "\n";
360
361 if ( $rev->getParentId() ) {
362 $out .= " " . Xml::element( 'parentid', null, strval( $rev->getParentId() ) ) . "\n";
363 }
364
365 $out .= $this->writeTimestamp( $rev->getTimestamp() );
366
367 if ( $rev->isDeleted( RevisionRecord::DELETED_USER ) ) {
368 $out .= " " . Xml::element( 'contributor', [ 'deleted' => 'deleted' ] ) . "\n";
369 } else {
370 // empty values get written out as uid 0, see T224221
371 $user = $rev->getUser();
372 $out .= $this->writeContributor(
373 $user ? $user->getId() : 0,
374 $user ? $user->getName() : ''
375 );
376 }
377
378 if ( $rev->isMinor() ) {
379 $out .= " <minor/>\n";
380 }
381 if ( $rev->isDeleted( RevisionRecord::DELETED_COMMENT ) ) {
382 $out .= " " . Xml::element( 'comment', [ 'deleted' => 'deleted' ] ) . "\n";
383 } else {
384 if ( $rev->getComment()->text != '' ) {
385 $out .= " "
386 . Xml::elementClean( 'comment', [], strval( $rev->getComment()->text ) )
387 . "\n";
388 }
389 }
390
391 $contentMode = $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ? self::WRITE_STUB_DELETED
392 : $this->contentMode;
393
394 $slots = $rev->getSlots()->getSlots();
395
396 // use predictable order, put main slot first
397 ksort( $slots );
398 $out .= $this->writeSlot( $slots[SlotRecord::MAIN], $contentMode );
399
400 foreach ( $slots as $role => $slot ) {
401 if ( $role === SlotRecord::MAIN ) {
402 continue;
403 }
404 $out .= $this->writeSlot( $slot, $contentMode );
405 }
406
407 if ( $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
408 $out .= " <sha1/>\n";
409 } else {
410 $sha1 = $this->invokeLenient(
411 static function () use ( $rev ) {
412 return $rev->getSha1();
413 },
414 'failed to determine sha1 for revision ' . $rev->getId()
415 );
416 $out .= " " . Xml::element( 'sha1', null, strval( $sha1 ) ) . "\n";
417 }
418
419 $text = '';
420 if ( $contentMode === self::WRITE_CONTENT ) {
422 $content = $this->invokeLenient(
423 static function () use ( $rev ) {
424 return $rev->getMainContentRaw();
425 },
426 'Failed to load main slot content of revision ' . $rev->getId()
427 );
428
429 $text = $content ? $content->serialize() : '';
430 }
431 $this->hookRunner->onXmlDumpWriterWriteRevision( $this, $out, $row, $text, $rev );
432
433 $out .= " </revision>\n";
434
435 return $out;
436 }
437
444 private function writeSlot( SlotRecord $slot, $contentMode ) {
445 $isMain = $slot->getRole() === SlotRecord::MAIN;
446 $isV11 = $this->schemaVersion >= XML_DUMP_SCHEMA_VERSION_11;
447
448 if ( !$isV11 && !$isMain ) {
449 // ignore extra slots
450 return '';
451 }
452
453 $out = '';
454 $indent = ' ';
455
456 if ( !$isMain ) {
457 // non-main slots are wrapped into an additional element.
458 $out .= ' ' . Xml::openElement( 'content' ) . "\n";
459 $indent .= ' ';
460 $out .= $indent . Xml::element( 'role', null, strval( $slot->getRole() ) ) . "\n";
461 }
462
463 if ( $isV11 ) {
464 $out .= $indent . Xml::element( 'origin', null, strval( $slot->getOrigin() ) ) . "\n";
465 }
466
467 $contentModel = $slot->getModel();
468 $contentHandlerFactory = MediaWikiServices::getInstance()->getContentHandlerFactory();
469 $contentHandler = null;
470
471 try {
472 $contentHandler = $contentHandlerFactory->getContentHandler( $contentModel );
473
474 } catch ( UnknownContentModelException ) {
475 // A content model should not be removed, as this would cause old revisions
476 // to fail to render. If this does happen, let dumps keep going but warn.
477 // To stop these warnings, register a fallback content model like so:
478 // $wgContentHandlers['Your.Removed.Handler'] = 'FallbackContentHandler'
479 MWDebug::warning( 'Revision ' . $slot->getRevision() . ' is using an unknown '
480 . ' content model, falling back to FallbackContentHandler.' );
481 $contentModel = CONTENT_MODEL_UNKNOWN;
482 $contentHandler = $contentHandlerFactory->getContentHandler( $contentModel );
483 }
484 $contentFormat = $contentHandler->getDefaultFormat();
485
486 // XXX: The content format is only relevant when actually outputting serialized content.
487 // It should probably be an attribute on the text tag.
488 $out .= $indent . Xml::element( 'model', null, strval( $contentModel ) ) . "\n";
489 $out .= $indent . Xml::element( 'format', null, strval( $contentFormat ) ) . "\n";
490
491 $textAttributes = [
492 'bytes' => $this->invokeLenient(
493 static function () use ( $slot ) {
494 return $slot->getSize();
495 },
496 'failed to determine size for slot ' . $slot->getRole() . ' of revision '
497 . $slot->getRevision()
498 ) ?: '0'
499 ];
500
501 if ( $isV11 ) {
502 $textAttributes['sha1'] = $this->invokeLenient(
503 static function () use ( $slot ) {
504 return $slot->getSha1();
505 },
506 'failed to determine sha1 for slot ' . $slot->getRole() . ' of revision '
507 . $slot->getRevision()
508 ) ?: '';
509 }
510
511 if ( $contentMode === self::WRITE_CONTENT ) {
512 $content = $this->invokeLenient(
513 static function () use ( $slot ) {
514 return $slot->getContent();
515 },
516 'failed to load content for slot ' . $slot->getRole() . ' of revision '
517 . $slot->getRevision()
518 );
519
520 if ( $content === null ) {
521 $out .= $indent . Xml::element( 'text', $textAttributes ) . "\n";
522 } else {
523 $out .= $this->writeText( $content, $textAttributes, $indent, $contentHandler, $contentFormat );
524 }
525 } elseif ( $contentMode === self::WRITE_STUB_DELETED ) {
526 // write <text> placeholder tag
527 $textAttributes['deleted'] = 'deleted';
528 $out .= $indent . Xml::element( 'text', $textAttributes ) . "\n";
529 } else {
530 // write <text> stub tag
531 if ( $isV11 ) {
532 $textAttributes['location'] = $slot->getAddress();
533 }
534 $schema = null;
535
536 if ( $isMain ) {
537 // Output the numerical text ID if possible, for backwards compatibility.
538 // Note that this is currently the ONLY reason we have a BlobStore here at all.
539 // When removing this line, check whether the BlobStore has become unused.
540 try {
541 // NOTE: this will only work for addresses of the form "tt:12345" or "es:DB://cluster1/1234".
542 // If we want to support other kinds of addresses in the future,
543 // we will have to silently ignore failures here.
544 // For now, this fails for "tt:0", which is present in the WMF production
545 // database as of July 2019, due to data corruption.
546 [ $schema, $textId ] = $this->getBlobStore()->splitBlobAddress( $slot->getAddress() );
547 } catch ( InvalidArgumentException $ex ) {
548 MWDebug::warning( 'Bad content address for slot ' . $slot->getRole()
549 . ' of revision ' . $slot->getRevision() . ': ' . $ex->getMessage() );
550 $textId = 0;
551 }
552
553 if ( $schema === 'tt' ) {
554 $textAttributes['id'] = $textId;
555 } elseif ( $schema === 'es' ) {
556 $textAttributes['id'] = bin2hex( $textId );
557 }
558 }
559
560 $out .= $indent . Xml::element( 'text', $textAttributes ) . "\n";
561 }
562
563 if ( !$isMain ) {
564 $out .= ' ' . Xml::closeElement( 'content' ) . "\n";
565 }
566
567 return $out;
568 }
569
582 private function writeText(
583 Content $content,
584 $textAttributes,
585 string $indent,
586 ContentHandler $contentHandler,
587 string $contentFormat
588 ) {
589 if ( $content instanceof TextContent ) {
590 // HACK: For text based models, bypass the serialization step. This allows extensions (like Flow)
591 // that use incompatible combinations of serialization format and content model.
592 $data = $content->getText();
593 } else {
594 $data = $content->serialize( $contentFormat );
595 }
596
597 $data = $contentHandler->exportTransform( $data, $contentFormat );
598 // make sure to use the actual size
599 $textAttributes['bytes'] = strlen( $data );
600 $textAttributes['xml:space'] = 'preserve';
601 return $indent . Xml::elementClean( 'text', $textAttributes, strval( $data ) ) . "\n";
602 }
603
611 public function writeLogItem( $row ) {
612 $out = " <logitem>\n";
613 $out .= " " . Xml::element( 'id', null, strval( $row->log_id ) ) . "\n";
614
615 $out .= $this->writeTimestamp( $row->log_timestamp, " " );
616
617 if ( $row->log_deleted & LogPage::DELETED_USER ) {
618 $out .= " " . Xml::element( 'contributor', [ 'deleted' => 'deleted' ] ) . "\n";
619 } else {
620 $out .= $this->writeContributor( $row->actor_user, $row->actor_name, " " );
621 }
622
623 if ( $row->log_deleted & LogPage::DELETED_COMMENT ) {
624 $out .= " " . Xml::element( 'comment', [ 'deleted' => 'deleted' ] ) . "\n";
625 } else {
626 $comment = $this->commentStore->getComment( 'log_comment', $row )->text;
627 if ( $comment != '' ) {
628 $out .= " " . Xml::elementClean( 'comment', null, strval( $comment ) ) . "\n";
629 }
630 }
631
632 $out .= " " . Xml::element( 'type', null, strval( $row->log_type ) ) . "\n";
633 $out .= " " . Xml::element( 'action', null, strval( $row->log_action ) ) . "\n";
634
635 if ( $row->log_deleted & LogPage::DELETED_ACTION ) {
636 $out .= " " . Xml::element( 'text', [ 'deleted' => 'deleted' ] ) . "\n";
637 } else {
638 $title = Title::makeTitle( $row->log_namespace, $row->log_title );
639 $out .= " " . Xml::elementClean( 'logtitle', null, self::canonicalTitle( $title ) ) . "\n";
640 $out .= " " . Xml::elementClean( 'params',
641 [ 'xml:space' => 'preserve' ],
642 strval( $row->log_params ) ) . "\n";
643 }
644
645 $out .= " </logitem>\n";
646
647 return $out;
648 }
649
655 public function writeTimestamp( $timestamp, $indent = " " ) {
656 $ts = wfTimestamp( TS::ISO_8601, $timestamp );
657 return $indent . Xml::element( 'timestamp', null, $ts ) . "\n";
658 }
659
666 public function writeContributor( $id, $text, $indent = " " ) {
667 $out = $indent . "<contributor>\n";
668 if ( $id || !IPUtils::isValid( $text ) ) {
669 $out .= $indent . " " . Xml::elementClean( 'username', null, strval( $text ) ) . "\n";
670 $out .= $indent . " " . Xml::element( 'id', null, strval( $id ) ) . "\n";
671 } else {
672 $out .= $indent . " " . Xml::elementClean( 'ip', null, strval( $text ) ) . "\n";
673 }
674 $out .= $indent . "</contributor>\n";
675 return $out;
676 }
677
684 public function writeUploads( $row, $dumpContents = false ) {
685 if ( $row->page_namespace == NS_FILE ) {
686 $img = MediaWikiServices::getInstance()->getRepoGroup()->getLocalRepo()
687 ->newFile( $row->page_title );
688 if ( $img && $img->exists() ) {
689 $out = '';
690 foreach ( array_reverse( $img->getHistory() ) as $ver ) {
691 $out .= $this->writeUpload( $ver, $dumpContents );
692 }
693 $out .= $this->writeUpload( $img, $dumpContents );
694 return $out;
695 }
696 }
697 return '';
698 }
699
705 private function writeUpload( $file, $dumpContents = false ) {
706 if ( $file->isOld() ) {
708 '@phan-var OldLocalFile $file';
709 $archiveName = " " .
710 Xml::element( 'archivename', null, $file->getArchiveName() ) . "\n";
711 } else {
712 $archiveName = '';
713 }
714 if ( $dumpContents ) {
715 $be = $file->getRepo()->getBackend();
716 # Dump file as base64
717 # Uses only XML-safe characters, so does not need escaping
718 # @todo Too bad this loads the contents into memory (script might swap)
719 $contents = ' <contents encoding="base64">' .
720 chunk_split( base64_encode(
721 $be->getFileContents( [ 'src' => $file->getPath() ] ) ) ) .
722 " </contents>\n";
723 } else {
724 $contents = '';
725 }
726 $uploader = $file->getUploader( File::FOR_PUBLIC );
727 if ( $uploader ) {
728 $uploader = $this->writeContributor( $uploader->getId(), $uploader->getName() );
729 } else {
730 $uploader = Xml::element( 'contributor', [ 'deleted' => 'deleted' ] ) . "\n";
731 }
732 $comment = $file->getDescription( File::FOR_PUBLIC );
733 if ( ( $comment ?? '' ) !== '' ) {
734 $comment = Xml::elementClean( 'comment', null, $comment );
735 } else {
736 $comment = Xml::element( 'comment', [ 'deleted' => 'deleted' ] );
737 }
738 return " <upload>\n" .
739 $this->writeTimestamp( $file->getTimestamp() ) .
740 $uploader .
741 " " . $comment . "\n" .
742 " " . Xml::element( 'filename', null, $file->getName() ) . "\n" .
743 $archiveName .
744 " " . Xml::element( 'src', null, $file->getCanonicalUrl() ) . "\n" .
745 " " . Xml::element( 'size', null, (string)( $file->getSize() ?: 0 ) ) . "\n" .
746 " " . Xml::element( 'sha1base36', null, $file->getSha1() ) . "\n" .
747 " " . Xml::element( 'rel', null, $file->getRel() ) . "\n" .
748 $contents .
749 " </upload>\n";
750 }
751
762 public static function canonicalTitle( Title $title ) {
763 if ( $title->isExternal() ) {
764 return $title->getPrefixedText();
765 }
766
767 $prefix = MediaWikiServices::getInstance()->getContentLanguage()->
768 getFormattedNsText( $title->getNamespace() );
769
770 // @todo Emit some kind of warning to the user if $title->getNamespace() !==
771 // NS_MAIN and $prefix === '' (viz. pages in an unregistered namespace)
772
773 if ( $prefix !== '' ) {
774 $prefix .= ':';
775 }
776
777 return $prefix . $title->getText();
778 }
779}
780
782class_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.
Base class for content handling.
Content object implementation for representing flat text.
Exception thrown when an unregistered content model is requested.
Debug toolbar.
Definition MWDebug.php:35
makeTitle( $linkId)
Convert a link ID to a Title.to override Title
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:80
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:333
static closeElement( $element)
Returns "</$element>".
Definition Html.php:370
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition Html.php:309
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:3447
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)