37use InvalidArgumentException;
46use Psr\Log\LoggerAwareInterface;
47use Psr\Log\LoggerInterface;
48use Psr\Log\NullLogger;
54use Wikimedia\Assert\Assert;
129 Assert::parameterType(
'string|boolean',
$wikiId,
'$wikiId' );
137 $this->logger =
new NullLogger();
148 return $this->blobStore->isReadOnly();
179 return $lb->getConnection( $mode, [], $this->wikiId );
187 $lb->reuseConnection( $connection );
197 return $lb->getConnectionRef( $mode, [], $this->wikiId );
214 public function getTitle( $pageId, $revId, $queryFlags = self::READ_NORMAL ) {
215 if ( !$pageId && !$revId ) {
216 throw new InvalidArgumentException(
'$pageId and $revId cannot both be 0 or null' );
221 if ( DBAccessObjectUtils::hasFlags( $queryFlags, self::READ_LATEST_IMMUTABLE ) ) {
222 $queryFlags = self::READ_NORMAL;
225 $canUseTitleNewFromId = ( $pageId !==
null && $pageId > 0 && $this->wikiId ===
false );
226 list( $dbMode, $dbOptions ) = DBAccessObjectUtils::getDBOptions( $queryFlags );
227 $titleFlags = ( $dbMode ==
DB_MASTER ? Title::GAID_FOR_UPDATE : 0 );
230 if ( $canUseTitleNewFromId ) {
232 $title = Title::newFromID( $pageId, $titleFlags );
239 $canUseRevId = ( $revId !==
null && $revId > 0 );
241 if ( $canUseRevId ) {
244 $row =
$dbr->selectRow(
245 [
'revision',
'page' ],
254 [
'rev_id' => $revId ],
257 [
'page' => [
'JOIN',
'page_id=rev_page' ] ]
261 return Title::newFromRow( $row );
267 $title = $this->
getTitle( $pageId, $revId, self::READ_LATEST );
270 __METHOD__ .
' fell back to READ_LATEST and got a Title.',
278 "Could not determine title for page ID $pageId and revision ID $revId"
292 "$name must not be " . var_export(
$value,
true ) .
"!"
309 "$name must not be " . var_export(
$value,
true ) .
"!"
332 if ( !
$rev->getSlotRoles() ) {
333 throw new InvalidArgumentException(
'At least one slot needs to be defined!' );
336 if (
$rev->getSlotRoles() !== [
'main' ] ) {
337 throw new InvalidArgumentException(
'Only the main slot is supported for now!' );
341 $title = Title::newFromLinkTarget(
$rev->getPageAsLinkTarget() );
344 $parentId =
$rev->getParentId() ===
null
346 :
$rev->getParentId();
354 if ( !$slot->hasAddress() ) {
355 $content = $slot->getContent();
356 $format = $content->getDefaultFormat();
357 $model = $content->getModel();
361 $data = $content->serialize( $format );
377 $blobAddress = $this->blobStore->storeBlob( $data, $blobHints );
379 $blobAddress = $slot->getAddress();
380 $model = $slot->getModel();
381 $format = $slot->getFormat();
384 $textId = $this->blobStore->getTextIdFromAddress( $blobAddress );
387 throw new LogicException(
388 'Blob address not supported in 1.29 database schema: ' . $blobAddress
394 $blobAddress =
'tt:' . $textId;
401 $this->
failOnNull( $user->getId(),
'user field' );
402 $this->
failOnEmpty( $user->getName(),
'user_text field' );
404 # Record the edit in revisions
406 'rev_page' => $pageId,
407 'rev_parent_id' => $parentId,
408 'rev_text_id' => $textId,
409 'rev_minor_edit' =>
$rev->isMinor() ? 1 : 0,
410 'rev_timestamp' => $dbw->
timestamp( $timestamp ),
411 'rev_deleted' =>
$rev->getVisibility(),
416 if (
$rev->getId() !==
null ) {
418 $row[
'rev_id'] =
$rev->getId();
421 list( $commentFields, $commentCallback ) =
422 $this->commentStore->insertWithTempTable( $dbw,
'rev_comment', $comment );
423 $row += $commentFields;
425 list( $actorFields, $actorCallback ) =
426 $this->actorMigration->getInsertValuesWithTempTable( $dbw,
'rev_user', $user );
427 $row += $actorFields;
429 if ( $this->contentHandlerUseDB ) {
432 $defaultModel = ContentHandler::getDefaultModelFor( $title );
433 $defaultFormat = ContentHandler::getForModelID( $defaultModel )->getDefaultFormat();
435 $row[
'rev_content_model'] = ( $model === $defaultModel ) ?
null : $model;
436 $row[
'rev_content_format'] = ( $format === $defaultFormat ) ?
null : $format;
439 $dbw->
insert(
'revision', $row, __METHOD__ );
441 if ( !isset( $row[
'rev_id'] ) ) {
443 $row[
'rev_id'] = intval( $dbw->
insertId() );
445 $commentCallback( $row[
'rev_id'] );
446 $actorCallback( $row[
'rev_id'], $row );
449 if ( $user->getId() === 0 && IP::isValid( $user->getName() ) ) {
451 'ipc_rev_id' => $row[
'rev_id'],
452 'ipc_rev_timestamp' => $row[
'rev_timestamp'],
453 'ipc_hex' => IP::toHex( $user->getName() ),
455 $dbw->
insert(
'ip_changes', $ipcRow, __METHOD__ );
473 Assert::postcondition(
$rev->getId() > 0,
'revision must have an ID' );
474 Assert::postcondition(
$rev->getPageId() > 0,
'revision must have a page ID' );
475 Assert::postcondition(
477 'revision must have a comment'
479 Assert::postcondition(
481 'revision must have a user'
484 Assert::postcondition( $newSlot !==
null,
'revision must have a main slot' );
485 Assert::postcondition(
486 $newSlot->getAddress() !==
null,
487 'main slot must have an addess'
490 Hooks::run(
'RevisionRecordInserted', [
$rev ] );
513 if ( !
$handler->isSupportedFormat( $format ) ) {
514 throw new MWException(
"Can't use format $format with content model $model on $name" );
517 if ( !$this->contentHandlerUseDB ) {
521 $defaultModel = ContentHandler::getDefaultModelFor( $title );
522 $defaultHandler = ContentHandler::getForModelID( $defaultModel );
523 $defaultFormat = $defaultHandler->getDefaultFormat();
525 if ( $model != $defaultModel ) {
526 throw new MWException(
"Can't save non-default content model with "
527 .
"\$wgContentHandlerUseDB disabled: model is $model, "
528 .
"default for $name is $defaultModel"
532 if ( $format != $defaultFormat ) {
533 throw new MWException(
"Can't use non-default content format with "
534 .
"\$wgContentHandlerUseDB disabled: format is $format, "
535 .
"default for $name is $defaultFormat"
542 "New content for $name is not valid! Content model is $model"
576 $fields = [
'page_latest',
'page_namespace',
'page_title',
577 'rev_id',
'rev_text_id',
'rev_len',
'rev_sha1' ];
579 if ( $this->contentHandlerUseDB ) {
580 $fields[] =
'rev_content_model';
581 $fields[] =
'rev_content_format';
585 [
'page',
'revision' ],
588 'page_id' => $title->getArticleID(),
589 'page_latest=rev_id',
597 'page' => $title->getArticleID(),
598 'user_text' => $user->getName(),
599 'user' => $user->getId(),
600 'actor' => $user->getActorId(),
601 'comment' => $comment,
602 'minor_edit' => $minor,
603 'text_id' => $current->rev_text_id,
604 'parent_id' => $current->page_latest,
605 'slot_origin' => $current->page_latest,
606 'len' => $current->rev_len,
607 'sha1' => $current->rev_sha1
610 if ( $this->contentHandlerUseDB ) {
611 $fields[
'content_model'] = $current->rev_content_model;
612 $fields[
'content_format'] = $current->rev_content_format;
615 $fields[
'title'] = Title::makeTitle( $current->page_namespace, $current->page_title );
620 $revision->setSlot( $mainSlot );
640 return $rc->getAttribute(
'rc_id' );
662 list( $dbType, ) = DBAccessObjectUtils::getDBOptions( $flags );
666 if ( !$userIdentity ) {
673 $actorWhere = $this->actorMigration->getWhere(
$dbr,
'rc_user',
$rev->getUser(),
false );
676 $actorWhere[
'conds'],
677 'rc_timestamp' =>
$dbr->timestamp(
$rev->getTimestamp() ),
678 'rc_this_oldid' =>
$rev->getId()
703 'ar_page_id' =>
'rev_page',
704 'ar_rev_id' =>
'rev_id',
707 'ar_text_id' =>
'rev_text_id',
708 'ar_timestamp' =>
'rev_timestamp',
709 'ar_user_text' =>
'rev_user_text',
710 'ar_user' =>
'rev_user',
711 'ar_actor' =>
'rev_actor',
712 'ar_minor_edit' =>
'rev_minor_edit',
713 'ar_deleted' =>
'rev_deleted',
714 'ar_len' =>
'rev_len',
715 'ar_parent_id' =>
'rev_parent_id',
716 'ar_sha1' =>
'rev_sha1',
717 'ar_comment' =>
'rev_comment',
718 'ar_comment_cid' =>
'rev_comment_cid',
719 'ar_comment_id' =>
'rev_comment_id',
720 'ar_comment_text' =>
'rev_comment_text',
721 'ar_comment_data' =>
'rev_comment_data',
722 'ar_comment_old' =>
'rev_comment_old',
723 'ar_content_format' =>
'rev_content_format',
724 'ar_content_model' =>
'rev_content_model',
727 $revRow =
new stdClass();
728 foreach ( $fieldMap as $arKey => $revKey ) {
729 if ( property_exists( $archiveRow, $arKey ) ) {
730 $revRow->$revKey = $archiveRow->$arKey;
748 $mainSlotRow =
new stdClass();
749 $mainSlotRow->role_name =
'main';
750 $mainSlotRow->model_name =
null;
751 $mainSlotRow->slot_revision_id =
null;
752 $mainSlotRow->content_address =
null;
753 $mainSlotRow->slot_content_id =
null;
759 if ( is_object( $row ) ) {
761 if ( !isset( $row->rev_id ) && ( isset( $row->ar_user ) || isset( $row->ar_actor ) ) ) {
765 if ( isset( $row->rev_text_id ) && $row->rev_text_id > 0 ) {
766 $mainSlotRow->slot_content_id = $row->rev_text_id;
767 $mainSlotRow->content_address =
'tt:' . $row->rev_text_id;
771 $mainSlotRow->slot_origin = isset( $row->slot_origin )
772 ? intval( $row->slot_origin )
775 if ( isset( $row->old_text ) ) {
777 $blobData = isset( $row->old_text ) ? strval( $row->old_text ) :
null;
779 if ( !property_exists( $row,
'old_flags' ) ) {
780 throw new InvalidArgumentException(
'old_flags was not set in $row' );
782 $blobFlags = ( $row->old_flags === null ) ?
'' : $row->old_flags;
785 $mainSlotRow->slot_revision_id = intval( $row->rev_id );
787 $mainSlotRow->content_size = isset( $row->rev_len ) ? intval( $row->rev_len ) :
null;
788 $mainSlotRow->content_sha1 = isset( $row->rev_sha1 ) ? strval( $row->rev_sha1 ) :
null;
789 $mainSlotRow->model_name = isset( $row->rev_content_model )
790 ? strval( $row->rev_content_model )
793 $mainSlotRow->format_name = isset( $row->rev_content_format )
794 ? strval( $row->rev_content_format )
796 } elseif ( is_array( $row ) ) {
797 $mainSlotRow->slot_revision_id = isset( $row[
'id'] ) ? intval( $row[
'id'] ) :
null;
799 $mainSlotRow->slot_content_id = isset( $row[
'text_id'] )
800 ? intval( $row[
'text_id'] )
802 $mainSlotRow->slot_origin = isset( $row[
'slot_origin'] )
803 ? intval( $row[
'slot_origin'] )
805 $mainSlotRow->content_address = isset( $row[
'text_id'] )
806 ?
'tt:' . intval( $row[
'text_id'] )
808 $mainSlotRow->content_size = isset( $row[
'len'] ) ? intval( $row[
'len'] ) :
null;
809 $mainSlotRow->content_sha1 = isset( $row[
'sha1'] ) ? strval( $row[
'sha1'] ) :
null;
811 $mainSlotRow->model_name = isset( $row[
'content_model'] )
812 ? strval( $row[
'content_model'] ) :
null;
814 $mainSlotRow->format_name = isset( $row[
'content_format'] )
815 ? strval( $row[
'content_format'] ) :
null;
816 $blobData = isset( $row[
'text'] ) ? rtrim( strval( $row[
'text'] ) ) :
null;
819 $blobFlags = isset( $row[
'flags'] ) ? trim( strval( $row[
'flags'] ) ) :
null;
822 if ( !empty( $row[
'content'] ) ) {
823 if ( !( $row[
'content'] instanceof
Content ) ) {
824 throw new MWException(
'content field must contain a Content object.' );
828 $content = $row[
'content'];
829 $handler = $content->getContentHandler();
831 $mainSlotRow->model_name = $content->getModel();
834 if ( $mainSlotRow->format_name ===
null ) {
835 $mainSlotRow->format_name =
$handler->getDefaultFormat();
839 throw new MWException(
'Revision constructor passed invalid row format.' );
844 if ( !isset( $mainSlotRow->slot_origin ) ) {
845 $mainSlotRow->slot_origin = $mainSlotRow->slot_revision_id;
848 if ( $mainSlotRow->model_name ===
null ) {
849 $mainSlotRow->model_name =
function (
SlotRecord $slot ) use ( $title ) {
852 return ContentHandler::getDefaultModelFor( $title );
858 use ( $blobData, $blobFlags, $queryFlags, $mainSlotRow )
864 $mainSlotRow->format_name,
870 $mainSlotRow->slot_id = $mainSlotRow->slot_revision_id;
871 return new SlotRecord( $mainSlotRow, $content );
900 if ( $blobData !==
null ) {
901 Assert::parameterType(
'string', $blobData,
'$blobData' );
902 Assert::parameterType(
'string|null', $blobFlags,
'$blobFlags' );
906 if ( $blobFlags ===
null ) {
910 $data = $this->blobStore->expandBlob( $blobData, $blobFlags, $cacheKey );
911 if ( $data ===
false ) {
913 "Failed to expand blob data using flags $blobFlags (key: $cacheKey)"
921 $data = $this->blobStore->getBlob( $address, $queryFlags );
924 "Failed to load data blob from $address: " .
$e->getMessage(), 0,
$e
932 $content =
$handler->unserializeContent( $data, $blobFormat );
973 'page_title' => $linkTarget->
getDBkey()
981 $conds[
'rev_id'] = $revId;
991 $conds[] =
'rev_id=page_latest';
1016 $conds = [
'page_id' => $pageId ];
1023 $conds[
'rev_id'] = $revId;
1033 $conds[] =
'rev_id=page_latest';
1056 'rev_timestamp' => $db->timestamp( $timestamp ),
1057 'page_namespace' => $title->getNamespace(),
1058 'page_title' => $title->getDBkey()
1085 Title $title =
null,
1086 array $overrides = []
1088 Assert::parameterType(
'object', $row,
'$row' );
1091 Assert::parameterType(
'integer', $queryFlags,
'$queryFlags' );
1093 if ( !$title && isset( $overrides[
'title'] ) ) {
1094 if ( !( $overrides[
'title'] instanceof
Title ) ) {
1095 throw new MWException(
'title field override must contain a Title object.' );
1098 $title = $overrides[
'title'];
1101 if ( !isset( $title ) ) {
1102 if ( isset( $row->ar_namespace ) && isset( $row->ar_title ) ) {
1103 $title = Title::makeTitle( $row->ar_namespace, $row->ar_title );
1105 throw new InvalidArgumentException(
1106 'A Title or ar_namespace and ar_title must be given'
1111 foreach ( $overrides as $key =>
$value ) {
1118 isset( $row->ar_user ) ? $row->ar_user :
null,
1119 isset( $row->ar_user_text ) ? $row->ar_user_text :
null,
1120 isset( $row->ar_actor ) ? $row->ar_actor : null
1122 }
catch ( InvalidArgumentException $ex ) {
1123 wfWarn( __METHOD__ .
': ' . $ex->getMessage() );
1127 $comment = $this->commentStore
1151 Assert::parameterType(
'object', $row,
'$row' );
1154 $pageId = isset( $row->rev_page ) ? $row->rev_page : 0;
1155 $revId = isset( $row->rev_id ) ? $row->rev_id : 0;
1157 $title = $this->
getTitle( $pageId, $revId, $queryFlags );
1160 if ( !isset( $row->page_latest ) ) {
1161 $row->page_latest = $title->getLatestRevID();
1162 if ( $row->page_latest === 0 && $title->exists() ) {
1163 wfWarn(
'Encountered title object in limbo: ID ' . $title->getArticleID() );
1169 isset( $row->rev_user ) ? $row->rev_user :
null,
1170 isset( $row->rev_user_text ) ? $row->rev_user_text :
null,
1171 isset( $row->rev_actor ) ? $row->rev_actor : null
1173 }
catch ( InvalidArgumentException $ex ) {
1174 wfWarn( __METHOD__ .
': ' . $ex->getMessage() );
1178 $comment = $this->commentStore
1222 if ( !$title && isset( $fields[
'title'] ) ) {
1223 if ( !( $fields[
'title'] instanceof
Title ) ) {
1224 throw new MWException(
'title field must contain a Title object.' );
1227 $title = $fields[
'title'];
1231 $pageId = isset( $fields[
'page'] ) ? $fields[
'page'] : 0;
1232 $revId = isset( $fields[
'id'] ) ? $fields[
'id'] : 0;
1234 $title = $this->
getTitle( $pageId, $revId, $queryFlags );
1237 if ( !isset( $fields[
'page'] ) ) {
1238 $fields[
'page'] = $title->getArticleID( $queryFlags );
1242 if ( !empty( $fields[
'content'] ) ) {
1243 if ( !( $fields[
'content'] instanceof
Content ) ) {
1244 throw new MWException(
'content field must contain a Content object.' );
1247 if ( !empty( $fields[
'text_id'] ) ) {
1249 "Text already stored in external store (id {$fields['text_id']}), " .
1250 "can't serialize content object"
1256 isset( $fields[
'comment'] )
1259 $commentData = isset( $fields[
'comment_data'] ) ? $fields[
'comment_data'] :
null;
1261 if ( $fields[
'comment'] instanceof
Message ) {
1262 $fields[
'comment'] = CommentStoreComment::newUnsavedComment(
1267 $commentText = trim( strval( $fields[
'comment'] ) );
1268 $fields[
'comment'] = CommentStoreComment::newUnsavedComment(
1279 $revision->setSlot( $mainSlot );
1295 if ( isset( $fields[
'user'] ) && ( $fields[
'user'] instanceof
UserIdentity ) ) {
1296 $user = $fields[
'user'];
1300 isset( $fields[
'user'] ) ? $fields[
'user'] :
null,
1301 isset( $fields[
'user_text'] ) ? $fields[
'user_text'] :
null,
1302 isset( $fields[
'actor'] ) ? $fields[
'actor'] : null
1304 }
catch ( InvalidArgumentException $ex ) {
1310 $record->setUser( $user );
1313 $timestamp = isset( $fields[
'timestamp'] )
1314 ? strval( $fields[
'timestamp'] )
1317 $record->setTimestamp( $timestamp );
1319 if ( isset( $fields[
'page'] ) ) {
1320 $record->setPageId( intval( $fields[
'page'] ) );
1323 if ( isset( $fields[
'id'] ) ) {
1324 $record->setId( intval( $fields[
'id'] ) );
1326 if ( isset( $fields[
'parent_id'] ) ) {
1327 $record->setParentId( intval( $fields[
'parent_id'] ) );
1330 if ( isset( $fields[
'sha1'] ) ) {
1331 $record->setSha1( $fields[
'sha1'] );
1333 if ( isset( $fields[
'size'] ) ) {
1334 $record->setSize( intval( $fields[
'size'] ) );
1337 if ( isset( $fields[
'minor_edit'] ) ) {
1338 $record->setMinorEdit( intval( $fields[
'minor_edit'] ) !== 0 );
1340 if ( isset( $fields[
'deleted'] ) ) {
1341 $record->setVisibility( intval( $fields[
'deleted'] ) );
1344 if ( isset( $fields[
'comment'] ) ) {
1345 Assert::parameterType(
1346 CommentStoreComment::class,
1350 $record->setComment( $fields[
'comment'] );
1388 $conds = [
'rev_page' => intval( $pageid ),
'page_id' => intval( $pageid ) ];
1390 $conds[
'rev_id'] = intval( $id );
1392 $conds[] =
'rev_id=page_latest';
1415 $matchId = intval( $id );
1417 $matchId =
'page_latest';
1424 'page_namespace' => $title->getNamespace(),
1425 'page_title' => $title->getDBkey()
1450 'rev_timestamp' => $db->
timestamp( $timestamp ),
1451 'page_namespace' => $title->getNamespace(),
1452 'page_title' => $title->getDBkey()
1484 && !( $flags & self::READ_LATEST )
1485 && $lb->getServerCount() > 1
1486 && $lb->hasOrMadeRecentMasterChanges()
1488 $flags = self::READ_LATEST;
1537 if ( $dbWiki === $storeWiki ) {
1542 $storeWiki = $storeWiki ?:
wfWikiID();
1545 if ( $dbWiki === $storeWiki ) {
1550 $storeWiki = str_replace(
'?h',
'-', $storeWiki );
1551 $dbWiki = str_replace(
'?h',
'-', $dbWiki );
1553 if ( $dbWiki === $storeWiki ) {
1557 throw new MWException(
"RevisionStore for $storeWiki "
1558 .
"cannot be used with a DB connection for $dbWiki" );
1578 if ( ( $flags & self::READ_LOCKING ) == self::READ_LOCKING ) {
1616 $ret[
'tables'][] =
'revision';
1617 $ret[
'fields'] = array_merge(
$ret[
'fields'], [
1629 $commentQuery = $this->commentStore->getJoin(
'rev_comment' );
1630 $ret[
'tables'] = array_merge(
$ret[
'tables'], $commentQuery[
'tables'] );
1631 $ret[
'fields'] = array_merge(
$ret[
'fields'], $commentQuery[
'fields'] );
1632 $ret[
'joins'] = array_merge(
$ret[
'joins'], $commentQuery[
'joins'] );
1634 $actorQuery = $this->actorMigration->getJoin(
'rev_user' );
1635 $ret[
'tables'] = array_merge(
$ret[
'tables'], $actorQuery[
'tables'] );
1636 $ret[
'fields'] = array_merge(
$ret[
'fields'], $actorQuery[
'fields'] );
1637 $ret[
'joins'] = array_merge(
$ret[
'joins'], $actorQuery[
'joins'] );
1639 if ( $this->contentHandlerUseDB ) {
1640 $ret[
'fields'][] =
'rev_content_format';
1641 $ret[
'fields'][] =
'rev_content_model';
1644 if ( in_array(
'page',
$options,
true ) ) {
1645 $ret[
'tables'][] =
'page';
1646 $ret[
'fields'] = array_merge(
$ret[
'fields'], [
1654 $ret[
'joins'][
'page'] = [
'INNER JOIN', [
'page_id = rev_page' ] ];
1657 if ( in_array(
'user',
$options,
true ) ) {
1658 $ret[
'tables'][] =
'user';
1659 $ret[
'fields'] = array_merge(
$ret[
'fields'], [
1662 $u = $actorQuery[
'fields'][
'rev_user'];
1663 $ret[
'joins'][
'user'] = [
'LEFT JOIN', [
"$u != 0",
"user_id = $u" ] ];
1666 if ( in_array(
'text',
$options,
true ) ) {
1667 $ret[
'tables'][] =
'text';
1668 $ret[
'fields'] = array_merge(
$ret[
'fields'], [
1672 $ret[
'joins'][
'text'] = [
'INNER JOIN', [
'rev_text_id=old_id' ] ];
1692 $commentQuery = $this->commentStore->getJoin(
'ar_comment' );
1693 $actorQuery = $this->actorMigration->getJoin(
'ar_user' );
1695 'tables' => [
'archive' ] + $commentQuery[
'tables'] + $actorQuery[
'tables'],
1709 ] + $commentQuery[
'fields'] + $actorQuery[
'fields'],
1710 'joins' => $commentQuery[
'joins'] + $actorQuery[
'joins'],
1713 if ( $this->contentHandlerUseDB ) {
1714 $ret[
'fields'][] =
'ar_content_format';
1715 $ret[
'fields'][] =
'ar_content_model';
1756 [
'rev_id',
'rev_len' ],
1757 [
'rev_id' => $revIds ],
1761 foreach (
$res as $row ) {
1762 $revLens[$row->rev_id] = intval( $row->rev_len );
1779 if ( $title ===
null ) {
1782 $prev = $title->getPreviousRevisionID(
$rev->getId() );
1800 if ( $title ===
null ) {
1803 $next = $title->getNextRevisionID(
$rev->getId() );
1824 if (
$rev->getPageId() ===
null ) {
1827 # Use page_latest if ID is not given
1828 if ( !
$rev->getId() ) {
1830 'page',
'page_latest',
1831 [
'page_id' =>
$rev->getPageId() ],
1836 'revision',
'rev_id',
1837 [
'rev_page' =>
$rev->getPageId(),
'rev_id < ' .
$rev->getId() ],
1839 [
'ORDER BY' =>
'rev_id DESC' ]
1842 return intval( $prevId );
1860 $conds = [
'rev_id' => $id ];
1861 $conds[
'rev_page'] = $title->getArticleID();
1862 $timestamp = $db->selectField(
'revision',
'rev_timestamp', $conds, __METHOD__ );
1865 return ( $timestamp !==
false ) ?
wfTimestamp( TS_MW, $timestamp ) :
false;
1881 [
'revCount' =>
'COUNT(*)' ],
1882 [
'rev_page' => $id ],
1886 return intval( $row->revCount );
1901 $id = $title->getArticleID();
1937 'rev_user' =>
$revQuery[
'fields'][
'rev_user'],
1940 'rev_page' => $pageId,
1944 [
'ORDER BY' =>
'rev_timestamp ASC',
'LIMIT' => 50 ],
1947 foreach (
$res as $row ) {
1948 if ( $row->rev_user != $userId ) {
1971 $pageId = $title->getArticleID();
1978 $revId = $title->getLatestRevID();
1983 'No latest revision known for page ' . $title->getPrefixedDBkey()
1984 .
' even though it exists with page ID ' . $pageId
1989 $row = $this->
cache->getWithSetCallback(
1991 $this->
cache->makeGlobalKey(
'revision-row-1.29', $db->getDomainID(), $pageId, $revId ),
1992 WANObjectCache::TTL_WEEK,
1993 function ( $curValue, &$ttl, array &$setOpts ) use ( $db, $pageId, $revId ) {
1994 $setOpts += Database::getCacheSetOptions( $db );
1997 'rev_page' => intval( $pageId ),
1998 'page_id' => intval( $pageId ),
1999 'rev_id' => intval( $revId ),
2003 return $row ?:
false;
wfWarn( $msg, $callerOffset=1, $level=E_USER_NOTICE)
Send a warning either to the debug log or in a PHP error depending on $wgDevelopmentWarnings.
wfTimestampNow()
Convenience function; returns MediaWiki timestamp for the present time.
wfBacktrace( $raw=null)
Get a debug backtrace as a string.
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
wfWikiID()
Get an ASCII string identifying this wiki This is used as a prefix in memcached keys.
This class handles the logic for the actor table migration.
A content handler knows how do deal with a specific type of content on a wiki page.
Helper class for DAO classes.
A collection of public static functions to play with IP address and IP ranges.
Exception thrown when an unregistered content model is requested.
The Message class provides methods which fulfil two basic services:
Utility class for creating new RC entries.
static newFromConds( $conds, $fname=__METHOD__, $dbType=DB_REPLICA)
Find the first recent change matching some specific conditions.
Represents a title within MediaWiki.
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
static newFromAnyId( $userId, $userName, $actorId)
Static factory method for creation from an ID, name, and/or actor ID.
Multi-datacenter aware caching interface.
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
when a variable name is used in a function
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped & $options
namespace and then decline to actually register it file or subcat img or subcat $title
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses & $ret
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable modifiable after all normalizations have been except for the $wgMaxImageArea check set to true or false to override the $wgMaxImageArea check result gives extension the possibility to transform it themselves $handler
presenting them properly to the user as errors is done by the caller return true use this to change the list i e etc $rev
processing should stop and the error should be shown to the user * false
returning false will NOT prevent logging $e
Base interface for content objects.
getContentHandler()
Convenience method that returns the ContentHandler singleton for handling the content model that this...
getModel()
Returns the ID of the content model used by this Content object.
getDefaultFormat()
Convenience method that returns the default serialization format for the content model that this Cont...
isValid()
Returns whether the content is valid.
Interface for database access objects.
you have access to all of the normal MediaWiki so you can get a DB use the cache