MediaWiki  master
RevisionRecord.php
Go to the documentation of this file.
1 <?php
23 namespace MediaWiki\Revision;
24 
26 use Content;
27 use InvalidArgumentException;
31 use MWException;
32 use Title;
33 use User;
34 use Wikimedia\Assert\Assert;
35 use Wikimedia\NonSerializable\NonSerializableTrait;
36 
46 abstract class RevisionRecord {
47  use NonSerializableTrait;
48 
49  // RevisionRecord deletion constants
50  public const DELETED_TEXT = 1;
51  public const DELETED_COMMENT = 2;
52  public const DELETED_USER = 4;
53  public const DELETED_RESTRICTED = 8;
54  public const SUPPRESSED_USER = self::DELETED_USER | self::DELETED_RESTRICTED; // convenience
55  public const SUPPRESSED_ALL = self::DELETED_TEXT | self::DELETED_COMMENT | self::DELETED_USER |
56  self::DELETED_RESTRICTED; // convenience
57 
58  // Audience options for accessors
59  public const FOR_PUBLIC = 1;
60  public const FOR_THIS_USER = 2;
61  public const RAW = 3;
62 
64  protected $mWiki = false;
66  protected $mId;
68  protected $mPageId;
70  protected $mUser;
72  protected $mMinorEdit = false;
74  protected $mTimestamp;
76  protected $mDeleted = 0;
78  protected $mSize;
80  protected $mSha1;
82  protected $mParentId;
84  protected $mComment;
85 
87  protected $mTitle; // TODO: we only need the title for permission checks!
88 
90  protected $mSlots;
91 
102  public function __construct( Title $title, RevisionSlots $slots, $dbDomain = false ) {
103  Assert::parameterType( 'string|boolean', $dbDomain, '$dbDomain' );
104 
105  $this->mTitle = $title;
106  $this->mSlots = $slots;
107  $this->mWiki = $dbDomain;
108 
109  // XXX: this is a sensible default, but we may not have a Title object here in the future.
110  $this->mPageId = $title->getArticleID();
111  }
112 
119  public function hasSameContent( RevisionRecord $rec ) {
120  if ( $rec === $this ) {
121  return true;
122  }
123 
124  if ( $this->getId() !== null && $this->getId() === $rec->getId() ) {
125  return true;
126  }
127 
128  // check size before hash, since size is quicker to compute
129  if ( $this->getSize() !== $rec->getSize() ) {
130  return false;
131  }
132 
133  // instead of checking the hash, we could also check the content addresses of all slots.
134 
135  if ( $this->getSha1() === $rec->getSha1() ) {
136  return true;
137  }
138 
139  return false;
140  }
141 
159  public function getContent( $role, $audience = self::FOR_PUBLIC, User $user = null ) {
160  // XXX: throwing an exception would be nicer, but would a further
161  // departure from the signature of Revision::getContent(), and thus
162  // more complex and error prone refactoring.
163  if ( !$this->audienceCan( self::DELETED_TEXT, $audience, $user ) ) {
164  return null;
165  }
166 
167  $content = $this->getSlot( $role, $audience, $user )->getContent();
168  return $content->copy();
169  }
170 
183  public function getSlot( $role, $audience = self::FOR_PUBLIC, User $user = null ) {
184  $slot = $this->mSlots->getSlot( $role );
185 
186  if ( !$this->audienceCan( self::DELETED_TEXT, $audience, $user ) ) {
187  return SlotRecord::newWithSuppressedContent( $slot );
188  }
189 
190  return $slot;
191  }
192 
200  public function hasSlot( $role ) {
201  return $this->mSlots->hasSlot( $role );
202  }
203 
210  public function getSlotRoles() {
211  return $this->mSlots->getSlotRoles();
212  }
213 
225  public function getSlots() {
226  return $this->mSlots;
227  }
228 
243  public function getOriginalSlots() {
244  return new RevisionSlots( $this->mSlots->getOriginalSlots() );
245  }
246 
258  public function getInheritedSlots() {
259  return new RevisionSlots( $this->mSlots->getInheritedSlots() );
260  }
261 
271  public function getId() {
272  return $this->mId;
273  }
274 
287  public function getParentId() {
288  return $this->mParentId;
289  }
290 
301  abstract public function getSize();
302 
314  abstract public function getSha1();
315 
323  public function getPageId() {
324  return $this->mPageId;
325  }
326 
332  public function getWikiId() {
333  return $this->mWiki;
334  }
335 
343  public function getPageAsLinkTarget() {
344  return $this->mTitle;
345  }
346 
363  public function getUser( $audience = self::FOR_PUBLIC, User $user = null ) {
364  if ( !$this->audienceCan( self::DELETED_USER, $audience, $user ) ) {
365  return null;
366  } else {
367  return $this->mUser;
368  }
369  }
370 
388  public function getComment( $audience = self::FOR_PUBLIC, User $user = null ) {
389  if ( !$this->audienceCan( self::DELETED_COMMENT, $audience, $user ) ) {
390  return null;
391  } else {
392  return $this->mComment;
393  }
394  }
395 
401  public function isMinor() {
402  return (bool)$this->mMinorEdit;
403  }
404 
412  public function isDeleted( $field ) {
413  return ( $this->getVisibility() & $field ) == $field;
414  }
415 
423  public function getVisibility() {
424  return (int)$this->mDeleted;
425  }
426 
434  public function getTimestamp() {
435  return $this->mTimestamp;
436  }
437 
455  public function audienceCan( $field, $audience, User $user = null ) {
456  if ( $audience == self::FOR_PUBLIC && $this->isDeleted( $field ) ) {
457  return false;
458  } elseif ( $audience == self::FOR_THIS_USER ) {
459  if ( !$user ) {
460  throw new InvalidArgumentException(
461  'A User object must be given when checking FOR_THIS_USER audience.'
462  );
463  }
464 
465  if ( !$this->userCan( $field, $user ) ) {
466  return false;
467  }
468  }
469 
470  return true;
471  }
472 
485  protected function userCan( $field, User $user ) {
486  // TODO: use callback for permission checks, so we don't need to know a Title object!
487  return self::userCanBitfield( $this->getVisibility(), $field, $user, $this->mTitle );
488  }
489 
506  public static function userCanBitfield( $bitfield, $field, User $user, Title $title = null ) {
507  if ( $bitfield & $field ) { // aspect is deleted
508  if ( $bitfield & self::DELETED_RESTRICTED ) {
509  $permissions = [ 'suppressrevision', 'viewsuppressed' ];
510  } elseif ( $field & self::DELETED_TEXT ) {
511  $permissions = [ 'deletedtext' ];
512  } else {
513  $permissions = [ 'deletedhistory' ];
514  }
515 
516  // XXX: How can we avoid global scope here?
517  // Perhaps the audience check should be done in a callback.
518  $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
519  $permissionlist = implode( ', ', $permissions );
520  if ( $title === null ) {
521  wfDebug( "Checking for $permissionlist due to $field match on $bitfield" );
522  foreach ( $permissions as $perm ) {
523  if ( $permissionManager->userHasRight( $user, $perm ) ) {
524  return true;
525  }
526  }
527  return false;
528  } else {
529  $text = $title->getPrefixedText();
530  wfDebug( "Checking for $permissionlist on $text due to $field match on $bitfield" );
531 
532  foreach ( $permissions as $perm ) {
533  if ( $permissionManager->userCan( $perm, $user, $title ) ) {
534  return true;
535  }
536  }
537  return false;
538  }
539  } else {
540  return true;
541  }
542  }
543 
555  public function isReadyForInsertion() {
556  // NOTE: don't check getSize() and getSha1(), since that may cause the full content to
557  // be loaded in order to calculate the values. Just assume these methods will not return
558  // null if mSlots is not empty.
559 
560  // NOTE: getId() and getPageId() may return null before a revision is saved, so don't
561  // check them.
562 
563  return $this->getTimestamp() !== null
564  && $this->getComment( self::RAW ) !== null
565  && $this->getUser( self::RAW ) !== null
566  && $this->mSlots->getSlotRoles() !== [];
567  }
568 
574  public function isCurrent() {
575  return false;
576  }
577 
578 }
579 
584 class_alias( RevisionRecord::class, 'MediaWiki\Storage\RevisionRecord' );
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:46
Revision\RevisionRecord\$mWiki
string false $mWiki
Wiki ID; false means the current wiki.
Definition: RevisionRecord.php:64
Revision\RevisionRecord\$mTitle
Title $mTitle
Definition: RevisionRecord.php:87
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:165
Revision\RevisionRecord\getOriginalSlots
getOriginalSlots()
Returns the slots that originate in this revision.
Definition: RevisionRecord.php:243
Revision\RevisionRecord\isDeleted
isDeleted( $field)
MCR migration note: this replaces Revision::isDeleted.
Definition: RevisionRecord.php:412
Revision\RevisionRecord\$mSlots
RevisionSlots $mSlots
Definition: RevisionRecord.php:90
Revision\RevisionRecord\$mComment
CommentStoreComment null $mComment
Definition: RevisionRecord.php:84
Revision\RevisionRecord\getTimestamp
getTimestamp()
MCR migration note: this replaces Revision::getTimestamp.
Definition: RevisionRecord.php:434
Revision\RevisionRecord\$mMinorEdit
bool $mMinorEdit
Definition: RevisionRecord.php:72
Revision\RevisionRecord\audienceCan
audienceCan( $field, $audience, User $user=null)
Check that the given audience has access to the given field.
Definition: RevisionRecord.php:455
Revision\RevisionRecord\getSlot
getSlot( $role, $audience=self::FOR_PUBLIC, User $user=null)
Returns meta-data for the given slot.
Definition: RevisionRecord.php:183
Revision\RevisionRecord\hasSlot
hasSlot( $role)
Returns whether the given slot is defined in this revision.
Definition: RevisionRecord.php:200
Revision\RevisionRecord\userCan
userCan( $field, User $user)
Determine if the current user is allowed to view a particular field of this revision,...
Definition: RevisionRecord.php:485
MediaWiki\User\UserIdentity
Interface for objects representing user identity.
Definition: UserIdentity.php:32
Revision\RevisionRecord\$mUser
UserIdentity null $mUser
Definition: RevisionRecord.php:70
MediaWiki\Revision
Definition: ContributionsLookup.php:3
Revision\RevisionRecord\getInheritedSlots
getInheritedSlots()
Returns slots inherited from some previous revision.
Definition: RevisionRecord.php:258
MediaWiki\MediaWikiServices\getInstance
static getInstance()
Returns the global default instance of the top level service locator.
Definition: MediaWikiServices.php:197
Revision\RevisionRecord\$mTimestamp
string null $mTimestamp
Definition: RevisionRecord.php:74
MWException
MediaWiki exception.
Definition: MWException.php:29
Revision\RevisionRecord\getSize
getSize()
Returns the nominal size of this revision, in bogo-bytes.
Revision\RevisionRecord\getSha1
getSha1()
Returns the base36 sha1 of this revision.
Revision\RevisionRecord\getUser
getUser( $audience=self::FOR_PUBLIC, User $user=null)
Fetch revision's author's user identity, if it's available to the specified audience.
Definition: RevisionRecord.php:363
Revision\RevisionRecord\isMinor
isMinor()
MCR migration note: this replaces Revision::isMinor.
Definition: RevisionRecord.php:401
Revision\RevisionRecord\isReadyForInsertion
isReadyForInsertion()
Returns whether this RevisionRecord is ready for insertion, that is, whether it contains all informat...
Definition: RevisionRecord.php:555
Revision\RevisionRecord\userCanBitfield
static userCanBitfield( $bitfield, $field, User $user, Title $title=null)
Determine if the current user is allowed to view a particular field of this revision,...
Definition: RevisionRecord.php:506
Revision\RevisionRecord\RAW
const RAW
Definition: RevisionRecord.php:61
$title
$title
Definition: testCompression.php:38
Revision\RevisionRecord\getSlotRoles
getSlotRoles()
Returns the slot names (roles) of all slots present in this revision.
Definition: RevisionRecord.php:210
Revision\RevisionRecord\$mParentId
int null $mParentId
Definition: RevisionRecord.php:82
wfDebug
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:910
Revision\RevisionRecord\getPageId
getPageId()
Get the page ID.
Definition: RevisionRecord.php:323
Revision\SlotRecord\newWithSuppressedContent
static newWithSuppressedContent(SlotRecord $slot)
Returns a new SlotRecord just like the given $slot, except that calling getContent() will fail with a...
Definition: SlotRecord.php:65
Revision\RevisionRecord\getId
getId()
Get revision ID.
Definition: RevisionRecord.php:271
Revision\RevisionRecord\getParentId
getParentId()
Get parent revision ID (the original previous page revision).
Definition: RevisionRecord.php:287
Revision\RevisionRecord\SUPPRESSED_USER
const SUPPRESSED_USER
Definition: RevisionRecord.php:54
$content
$content
Definition: router.php:76
Revision\RevisionRecord\__construct
__construct(Title $title, RevisionSlots $slots, $dbDomain=false)
Definition: RevisionRecord.php:102
Revision\RevisionRecord\DELETED_USER
const DELETED_USER
Definition: RevisionRecord.php:52
Revision\RevisionRecord\getComment
getComment( $audience=self::FOR_PUBLIC, User $user=null)
Fetch revision comment, if it's available to the specified audience.
Definition: RevisionRecord.php:388
Revision\RevisionRecord\$mSize
int null $mSize
Definition: RevisionRecord.php:78
Revision\RevisionRecord\getSlots
getSlots()
Returns the slots defined for this revision.
Definition: RevisionRecord.php:225
Content
Base interface for content objects.
Definition: Content.php:35
Revision\RevisionRecord\FOR_PUBLIC
const FOR_PUBLIC
Definition: RevisionRecord.php:59
Revision\RevisionRecord\$mPageId
int $mPageId
Definition: RevisionRecord.php:68
Revision\RevisionRecord\getVisibility
getVisibility()
Get the deletion bitfield of the revision.
Definition: RevisionRecord.php:423
Revision\RevisionRecord\getPageAsLinkTarget
getPageAsLinkTarget()
Returns the title of the page this revision is associated with as a LinkTarget object.
Definition: RevisionRecord.php:343
Title
Represents a title within MediaWiki.
Definition: Title.php:41
Revision\RevisionRecord\DELETED_COMMENT
const DELETED_COMMENT
Definition: RevisionRecord.php:51
Revision\RevisionRecord\DELETED_TEXT
const DELETED_TEXT
Definition: RevisionRecord.php:50
Revision\RevisionSlots
Value object representing the set of slots belonging to a revision.
Definition: RevisionSlots.php:41
Revision\RevisionRecord\getContent
getContent( $role, $audience=self::FOR_PUBLIC, User $user=null)
Returns the Content of the given slot of this revision.
Definition: RevisionRecord.php:159
Revision\RevisionRecord\hasSameContent
hasSameContent(RevisionRecord $rec)
Definition: RevisionRecord.php:119
Revision\RevisionRecord\isCurrent
isCurrent()
Checks whether the revision record is a stored current revision.
Definition: RevisionRecord.php:574
Revision\RevisionRecord\SUPPRESSED_ALL
const SUPPRESSED_ALL
Definition: RevisionRecord.php:55
Revision\RevisionRecord\DELETED_RESTRICTED
const DELETED_RESTRICTED
Definition: RevisionRecord.php:53
MediaWiki\Linker\LinkTarget
Definition: LinkTarget.php:26
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:56
Revision\RevisionRecord\FOR_THIS_USER
const FOR_THIS_USER
Definition: RevisionRecord.php:60
Revision\RevisionRecord\$mSha1
string null $mSha1
Definition: RevisionRecord.php:80
Revision\RevisionRecord\getWikiId
getWikiId()
Get the ID of the wiki this revision belongs to.
Definition: RevisionRecord.php:332
Revision\RevisionRecord\$mDeleted
int $mDeleted
using the DELETED_XXX and SUPPRESSED_XXX flags
Definition: RevisionRecord.php:76
Revision\RevisionRecord\$mId
int null $mId
Definition: RevisionRecord.php:66
CommentStoreComment
Value object for a comment stored by CommentStore.
Definition: CommentStoreComment.php:30