MediaWiki REL1_33
RevisionRecord.php
Go to the documentation of this file.
1<?php
23namespace MediaWiki\Revision;
24
27use InvalidArgumentException;
28use LogicException;
33use User;
34use Wikimedia\Assert\Assert;
35
45abstract class RevisionRecord {
46
47 // RevisionRecord deletion constants
48 const DELETED_TEXT = 1;
49 const DELETED_COMMENT = 2;
50 const DELETED_USER = 4;
52 const SUPPRESSED_USER = self::DELETED_USER | self::DELETED_RESTRICTED; // convenience
53 const SUPPRESSED_ALL = self::DELETED_TEXT | self::DELETED_COMMENT | self::DELETED_USER |
54 self::DELETED_RESTRICTED; // convenience
55
56 // Audience options for accessors
57 const FOR_PUBLIC = 1;
58 const FOR_THIS_USER = 2;
59 const RAW = 3;
60
62 protected $mWiki = false;
64 protected $mId;
66 protected $mPageId;
68 protected $mUser;
70 protected $mMinorEdit = false;
72 protected $mTimestamp;
74 protected $mDeleted = 0;
76 protected $mSize;
78 protected $mSha1;
80 protected $mParentId;
82 protected $mComment;
83
85 protected $mTitle; // TODO: we only need the title for permission checks!
86
88 protected $mSlots;
89
101 function __construct( Title $title, RevisionSlots $slots, $wikiId = false ) {
102 Assert::parameterType( 'string|boolean', $wikiId, '$wikiId' );
103
104 $this->mTitle = $title;
105 $this->mSlots = $slots;
106 $this->mWiki = $wikiId;
107
108 // XXX: this is a sensible default, but we may not have a Title object here in the future.
109 $this->mPageId = $title->getArticleID();
110 }
111
117 public function __sleep() {
118 throw new LogicException( __CLASS__ . ' is not serializable.' );
119 }
120
127 public function hasSameContent( RevisionRecord $rec ) {
128 if ( $rec === $this ) {
129 return true;
130 }
131
132 if ( $this->getId() !== null && $this->getId() === $rec->getId() ) {
133 return true;
134 }
135
136 // check size before hash, since size is quicker to compute
137 if ( $this->getSize() !== $rec->getSize() ) {
138 return false;
139 }
140
141 // instead of checking the hash, we could also check the content addresses of all slots.
142
143 if ( $this->getSha1() === $rec->getSha1() ) {
144 return true;
145 }
146
147 return false;
148 }
149
167 public function getContent( $role, $audience = self::FOR_PUBLIC, User $user = null ) {
168 // XXX: throwing an exception would be nicer, but would a further
169 // departure from the signature of Revision::getContent(), and thus
170 // more complex and error prone refactoring.
171 if ( !$this->audienceCan( self::DELETED_TEXT, $audience, $user ) ) {
172 return null;
173 }
174
175 $content = $this->getSlot( $role, $audience, $user )->getContent();
176 return $content->copy();
177 }
178
191 public function getSlot( $role, $audience = self::FOR_PUBLIC, User $user = null ) {
192 $slot = $this->mSlots->getSlot( $role );
193
194 if ( !$this->audienceCan( self::DELETED_TEXT, $audience, $user ) ) {
196 }
197
198 return $slot;
199 }
200
208 public function hasSlot( $role ) {
209 return $this->mSlots->hasSlot( $role );
210 }
211
218 public function getSlotRoles() {
219 return $this->mSlots->getSlotRoles();
220 }
221
227 public function getSlots() {
228 return $this->mSlots;
229 }
230
245 public function getOriginalSlots() {
246 return new RevisionSlots( $this->mSlots->getOriginalSlots() );
247 }
248
260 public function getInheritedSlots() {
261 return new RevisionSlots( $this->mSlots->getInheritedSlots() );
262 }
263
273 public function getId() {
274 return $this->mId;
275 }
276
289 public function getParentId() {
290 return $this->mParentId;
291 }
292
303 abstract public function getSize();
304
316 abstract public function getSha1();
317
325 public function getPageId() {
326 return $this->mPageId;
327 }
328
334 public function getWikiId() {
335 return $this->mWiki;
336 }
337
345 public function getPageAsLinkTarget() {
346 return $this->mTitle;
347 }
348
365 public function getUser( $audience = self::FOR_PUBLIC, User $user = null ) {
366 if ( !$this->audienceCan( self::DELETED_USER, $audience, $user ) ) {
367 return null;
368 } else {
369 return $this->mUser;
370 }
371 }
372
390 public function getComment( $audience = self::FOR_PUBLIC, User $user = null ) {
391 if ( !$this->audienceCan( self::DELETED_COMMENT, $audience, $user ) ) {
392 return null;
393 } else {
394 return $this->mComment;
395 }
396 }
397
403 public function isMinor() {
404 return (bool)$this->mMinorEdit;
405 }
406
414 public function isDeleted( $field ) {
415 return ( $this->getVisibility() & $field ) == $field;
416 }
417
425 public function getVisibility() {
426 return (int)$this->mDeleted;
427 }
428
436 public function getTimestamp() {
437 return $this->mTimestamp;
438 }
439
457 public function audienceCan( $field, $audience, User $user = null ) {
458 if ( $audience == self::FOR_PUBLIC && $this->isDeleted( $field ) ) {
459 return false;
460 } elseif ( $audience == self::FOR_THIS_USER ) {
461 if ( !$user ) {
462 throw new InvalidArgumentException(
463 'A User object must be given when checking FOR_THIS_USER audience.'
464 );
465 }
466
467 if ( !$this->userCan( $field, $user ) ) {
468 return false;
469 }
470 }
471
472 return true;
473 }
474
487 protected function userCan( $field, User $user ) {
488 // TODO: use callback for permission checks, so we don't need to know a Title object!
489 return self::userCanBitfield( $this->getVisibility(), $field, $user, $this->mTitle );
490 }
491
508 public static function userCanBitfield( $bitfield, $field, User $user, Title $title = null ) {
509 if ( $bitfield & $field ) { // aspect is deleted
510 if ( $bitfield & self::DELETED_RESTRICTED ) {
511 $permissions = [ 'suppressrevision', 'viewsuppressed' ];
512 } elseif ( $field & self::DELETED_TEXT ) {
513 $permissions = [ 'deletedtext' ];
514 } else {
515 $permissions = [ 'deletedhistory' ];
516 }
517 $permissionlist = implode( ', ', $permissions );
518 if ( $title === null ) {
519 wfDebug( "Checking for $permissionlist due to $field match on $bitfield\n" );
520 return $user->isAllowedAny( ...$permissions );
521 } else {
522 $text = $title->getPrefixedText();
523 wfDebug( "Checking for $permissionlist on $text due to $field match on $bitfield\n" );
524 foreach ( $permissions as $perm ) {
525 if ( $title->userCan( $perm, $user ) ) {
526 return true;
527 }
528 }
529 return false;
530 }
531 } else {
532 return true;
533 }
534 }
535
547 public function isReadyForInsertion() {
548 // NOTE: don't check getSize() and getSha1(), since that may cause the full content to
549 // be loaded in order to calculate the values. Just assume these methods will not return
550 // null if mSlots is not empty.
551
552 // NOTE: getId() and getPageId() may return null before a revision is saved, so don't
553 //check them.
554
555 return $this->getTimestamp() !== null
556 && $this->getComment( self::RAW ) !== null
557 && $this->getUser( self::RAW ) !== null
558 && $this->mSlots->getSlotRoles() !== [];
559 }
560
561}
562
567class_alias( RevisionRecord::class, 'MediaWiki\Storage\RevisionRecord' );
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
CommentStoreComment represents a comment stored by CommentStore.
MediaWiki exception.
Page revision base class.
getParentId()
Get parent revision ID (the original previous page revision).
getWikiId()
Get the ID of the wiki this revision belongs to.
__construct(Title $title, RevisionSlots $slots, $wikiId=false)
getSize()
Returns the nominal size of this revision, in bogo-bytes.
string $mWiki
Wiki ID; false means the current wiki.
isReadyForInsertion()
Returns whether this RevisionRecord is ready for insertion, that is, whether it contains all informat...
userCan( $field, User $user)
Determine if the current user is allowed to view a particular field of this revision,...
getOriginalSlots()
Returns the slots that originate in this revision.
getComment( $audience=self::FOR_PUBLIC, User $user=null)
Fetch revision comment, if it's available to the specified audience.
int $mDeleted
using the DELETED_XXX and SUPPRESSED_XXX flags
CommentStoreComment null $mComment
getSlotRoles()
Returns the slot names (roles) of all slots present in this revision.
__sleep()
Implemented to defy serialization.
static userCanBitfield( $bitfield, $field, User $user, Title $title=null)
Determine if the current user is allowed to view a particular field of this revision,...
getVisibility()
Get the deletion bitfield of the revision.
getSlots()
Returns the slots defined for this revision.
getContent( $role, $audience=self::FOR_PUBLIC, User $user=null)
Returns the Content of the given slot of this revision.
getInheritedSlots()
Returns slots inherited from some previous revision.
getTimestamp()
MCR migration note: this replaces Revision::getTimestamp.
audienceCan( $field, $audience, User $user=null)
Check that the given audience has access to the given field.
hasSlot( $role)
Returns whether the given slot is defined in this revision.
getSlot( $role, $audience=self::FOR_PUBLIC, User $user=null)
Returns meta-data for the given slot.
getSha1()
Returns the base36 sha1 of this revision.
isMinor()
MCR migration note: this replaces Revision::isMinor.
getPageAsLinkTarget()
Returns the title of the page this revision is associated with as a LinkTarget object.
getUser( $audience=self::FOR_PUBLIC, User $user=null)
Fetch revision's author's user identity, if it's available to the specified audience.
isDeleted( $field)
MCR migration note: this replaces Revision::isDeleted.
Value object representing the set of slots belonging to a revision.
static newWithSuppressedContent(SlotRecord $slot)
Returns a new SlotRecord just like the given $slot, except that calling getContent() will fail with a...
Represents a title within MediaWiki.
Definition Title.php:40
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition User.php:48
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
namespace and then decline to actually register it file or subcat img or subcat $title
Definition hooks.txt:955
return true to allow those checks to and false if checking is done & $user
Definition hooks.txt:1510
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition injection.txt:37
Base interface for content objects.
Definition Content.php:34
Interface for objects representing user identity.
$content