MediaWiki  master
RevisionRecord.php
Go to the documentation of this file.
1 <?php
23 namespace MediaWiki\Revision;
24 
26 use Content;
33 use Title;
34 use User;
36 
46 abstract class RevisionRecord {
47 
48  // RevisionRecord deletion constants
49  const DELETED_TEXT = 1;
50  const DELETED_COMMENT = 2;
51  const DELETED_USER = 4;
52  const DELETED_RESTRICTED = 8;
53  const SUPPRESSED_USER = self::DELETED_USER | self::DELETED_RESTRICTED; // convenience
54  const SUPPRESSED_ALL = self::DELETED_TEXT | self::DELETED_COMMENT | self::DELETED_USER |
55  self::DELETED_RESTRICTED; // convenience
56 
57  // Audience options for accessors
58  const FOR_PUBLIC = 1;
59  const FOR_THIS_USER = 2;
60  const RAW = 3;
61 
63  protected $mWiki = false;
65  protected $mId;
67  protected $mPageId;
69  protected $mUser;
71  protected $mMinorEdit = false;
73  protected $mTimestamp;
75  protected $mDeleted = 0;
77  protected $mSize;
79  protected $mSha1;
81  protected $mParentId;
83  protected $mComment;
84 
86  protected $mTitle; // TODO: we only need the title for permission checks!
87 
89  protected $mSlots;
90 
102  function __construct( Title $title, RevisionSlots $slots, $wikiId = false ) {
103  Assert::parameterType( 'string|boolean', $wikiId, '$wikiId' );
104 
105  $this->mTitle = $title;
106  $this->mSlots = $slots;
107  $this->mWiki = $wikiId;
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 
118  public function __sleep() {
119  throw new LogicException( __CLASS__ . ' is not serializable.' );
120  }
121 
128  public function hasSameContent( RevisionRecord $rec ) {
129  if ( $rec === $this ) {
130  return true;
131  }
132 
133  if ( $this->getId() !== null && $this->getId() === $rec->getId() ) {
134  return true;
135  }
136 
137  // check size before hash, since size is quicker to compute
138  if ( $this->getSize() !== $rec->getSize() ) {
139  return false;
140  }
141 
142  // instead of checking the hash, we could also check the content addresses of all slots.
143 
144  if ( $this->getSha1() === $rec->getSha1() ) {
145  return true;
146  }
147 
148  return false;
149  }
150 
168  public function getContent( $role, $audience = self::FOR_PUBLIC, User $user = null ) {
169  // XXX: throwing an exception would be nicer, but would a further
170  // departure from the signature of Revision::getContent(), and thus
171  // more complex and error prone refactoring.
172  if ( !$this->audienceCan( self::DELETED_TEXT, $audience, $user ) ) {
173  return null;
174  }
175 
176  $content = $this->getSlot( $role, $audience, $user )->getContent();
177  return $content->copy();
178  }
179 
192  public function getSlot( $role, $audience = self::FOR_PUBLIC, User $user = null ) {
193  $slot = $this->mSlots->getSlot( $role );
194 
195  if ( !$this->audienceCan( self::DELETED_TEXT, $audience, $user ) ) {
196  return SlotRecord::newWithSuppressedContent( $slot );
197  }
198 
199  return $slot;
200  }
201 
209  public function hasSlot( $role ) {
210  return $this->mSlots->hasSlot( $role );
211  }
212 
219  public function getSlotRoles() {
220  return $this->mSlots->getSlotRoles();
221  }
222 
228  public function getSlots() {
229  return $this->mSlots;
230  }
231 
246  public function getOriginalSlots() {
247  return new RevisionSlots( $this->mSlots->getOriginalSlots() );
248  }
249 
261  public function getInheritedSlots() {
262  return new RevisionSlots( $this->mSlots->getInheritedSlots() );
263  }
264 
274  public function getId() {
275  return $this->mId;
276  }
277 
290  public function getParentId() {
291  return $this->mParentId;
292  }
293 
304  abstract public function getSize();
305 
317  abstract public function getSha1();
318 
326  public function getPageId() {
327  return $this->mPageId;
328  }
329 
335  public function getWikiId() {
336  return $this->mWiki;
337  }
338 
346  public function getPageAsLinkTarget() {
347  return $this->mTitle;
348  }
349 
366  public function getUser( $audience = self::FOR_PUBLIC, User $user = null ) {
367  if ( !$this->audienceCan( self::DELETED_USER, $audience, $user ) ) {
368  return null;
369  } else {
370  return $this->mUser;
371  }
372  }
373 
391  public function getComment( $audience = self::FOR_PUBLIC, User $user = null ) {
392  if ( !$this->audienceCan( self::DELETED_COMMENT, $audience, $user ) ) {
393  return null;
394  } else {
395  return $this->mComment;
396  }
397  }
398 
404  public function isMinor() {
405  return (bool)$this->mMinorEdit;
406  }
407 
415  public function isDeleted( $field ) {
416  return ( $this->getVisibility() & $field ) == $field;
417  }
418 
426  public function getVisibility() {
427  return (int)$this->mDeleted;
428  }
429 
437  public function getTimestamp() {
438  return $this->mTimestamp;
439  }
440 
458  public function audienceCan( $field, $audience, User $user = null ) {
459  if ( $audience == self::FOR_PUBLIC && $this->isDeleted( $field ) ) {
460  return false;
461  } elseif ( $audience == self::FOR_THIS_USER ) {
462  if ( !$user ) {
463  throw new InvalidArgumentException(
464  'A User object must be given when checking FOR_THIS_USER audience.'
465  );
466  }
467 
468  if ( !$this->userCan( $field, $user ) ) {
469  return false;
470  }
471  }
472 
473  return true;
474  }
475 
488  protected function userCan( $field, User $user ) {
489  // TODO: use callback for permission checks, so we don't need to know a Title object!
490  return self::userCanBitfield( $this->getVisibility(), $field, $user, $this->mTitle );
491  }
492 
509  public static function userCanBitfield( $bitfield, $field, User $user, Title $title = null ) {
510  if ( $bitfield & $field ) { // aspect is deleted
511  if ( $bitfield & self::DELETED_RESTRICTED ) {
512  $permissions = [ 'suppressrevision', 'viewsuppressed' ];
513  } elseif ( $field & self::DELETED_TEXT ) {
514  $permissions = [ 'deletedtext' ];
515  } else {
516  $permissions = [ 'deletedhistory' ];
517  }
518  $permissionlist = implode( ', ', $permissions );
519  if ( $title === null ) {
520  wfDebug( "Checking for $permissionlist due to $field match on $bitfield\n" );
521  return $user->isAllowedAny( ...$permissions );
522  } else {
523  $text = $title->getPrefixedText();
524  wfDebug( "Checking for $permissionlist on $text due to $field match on $bitfield\n" );
525 
526  $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
527 
528  foreach ( $permissions as $perm ) {
529  if ( $permissionManager->userCan( $perm, $user, $title ) ) {
530  return true;
531  }
532  }
533  return false;
534  }
535  } else {
536  return true;
537  }
538  }
539 
551  public function isReadyForInsertion() {
552  // NOTE: don't check getSize() and getSha1(), since that may cause the full content to
553  // be loaded in order to calculate the values. Just assume these methods will not return
554  // null if mSlots is not empty.
555 
556  // NOTE: getId() and getPageId() may return null before a revision is saved, so don't
557  // check them.
558 
559  return $this->getTimestamp() !== null
560  && $this->getComment( self::RAW ) !== null
561  && $this->getUser( self::RAW ) !== null
562  && $this->mSlots->getSlotRoles() !== [];
563  }
564 
565 }
566 
571 class_alias( RevisionRecord::class, 'MediaWiki\Storage\RevisionRecord' );
__construct(Title $title, RevisionSlots $slots, $wikiId=false)
isMinor()
MCR migration note: this replaces Revision::isMinor.
getArticleID( $flags=0)
Get the article ID for this Title from the link cache, adding it if necessary.
Definition: Title.php:3005
Apache License January AND DISTRIBUTION Definitions License shall mean the terms and conditions for use
isAllowedAny()
Check if user is allowed to access a feature / make an action.
Definition: User.php:3700
getSlots()
Returns the slots defined for this revision.
hasSameContent(RevisionRecord $rec)
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 MediaWikiServices
Definition: injection.txt:23
isDeleted( $field)
MCR migration note: this replaces Revision::isDeleted.
hasSlot( $role)
Returns whether the given slot is defined in this revision.
static getInstance()
Returns the global default instance of the top level service locator.
userCan( $field, User $user)
Determine if the current user is allowed to view a particular field of this revision, if it&#39;s marked as deleted.
getOriginalSlots()
Returns the slots that originate in this revision.
The User object encapsulates all of the user-specific settings (user_id, name, rights, email address, options, last login time).
Definition: User.php:51
Created by PhpStorm.
int $mDeleted
using the DELETED_XXX and SUPPRESSED_XXX flags
static newWithSuppressedContent(SlotRecord $slot)
Returns a new SlotRecord just like the given $slot, except that calling getContent() will fail with a...
Definition: SlotRecord.php:63
getPageAsLinkTarget()
Returns the title of the page this revision is associated with as a LinkTarget object.
getVisibility()
Get the deletion bitfield of the revision.
getSlotRoles()
Returns the slot names (roles) of all slots present in this revision.
wfDebug( $text, $dest='all', array $context=[])
Sends a line to the debug log if enabled or, optionally, to a comment in output.
getContent( $role, $audience=self::FOR_PUBLIC, User $user=null)
Returns the Content of the given slot of 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, if it&#39;s marked as deleted.
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that When $user is not null
Definition: hooks.txt:780
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:925
isReadyForInsertion()
Returns whether this RevisionRecord is ready for insertion, that is, whether it contains all informat...
CommentStoreComment null $mComment
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
Definition: distributors.txt:9
audienceCan( $field, $audience, User $user=null)
Check that the given audience has access to the given field.
getSlot( $role, $audience=self::FOR_PUBLIC, User $user=null)
Returns meta-data for the given slot.
getId()
Get revision ID.
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:35
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
getSha1()
Returns the base36 sha1 of this revision.
getTimestamp()
MCR migration note: this replaces Revision::getTimestamp.
string $mWiki
Wiki ID; false means the current wiki.
getWikiId()
Get the ID of the wiki this revision belongs to.
getSize()
Returns the nominal size of this revision, in bogo-bytes.
Page revision base class.
getUser( $audience=self::FOR_PUBLIC, User $user=null)
Fetch revision&#39;s author&#39;s user identity, if it&#39;s available to the specified audience.
Value object representing the set of slots belonging to a revision.
UserIdentity null $mUser
getInheritedSlots()
Returns slots inherited from some previous revision.
getComment( $audience=self::FOR_PUBLIC, User $user=null)
Fetch revision comment, if it&#39;s available to the specified audience.
$content
Definition: pageupdater.txt:72
getParentId()
Get parent revision ID (the original previous page revision).
return true to allow those checks to and false if checking is done & $user
Definition: hooks.txt:1473
getPageId()
Get the page ID.