MediaWiki  master
RevisionRenderer.php
Go to the documentation of this file.
1 <?php
23 namespace MediaWiki\Revision;
24 
25 use Html;
26 use InvalidArgumentException;
27 use ParserOptions;
28 use ParserOutput;
29 use Psr\Log\LoggerInterface;
30 use Psr\Log\NullLogger;
31 use Title;
32 use User;
34 
46 
49 
51  private $loadBalancer;
52 
54  private $roleRegistery;
55 
57  private $dbDomain;
58 
64  public function __construct(
65  ILoadBalancer $loadBalancer,
66  SlotRoleRegistry $roleRegistry,
67  $dbDomain = false
68  ) {
69  $this->loadBalancer = $loadBalancer;
70  $this->roleRegistery = $roleRegistry;
71  $this->dbDomain = $dbDomain;
72  $this->saveParseLogger = new NullLogger();
73  }
74 
78  public function setLogger( LoggerInterface $saveParseLogger ) {
79  $this->saveParseLogger = $saveParseLogger;
80  }
81 
102  public function getRenderedRevision(
103  RevisionRecord $rev,
104  ParserOptions $options = null,
105  User $forUser = null,
106  array $hints = []
107  ) {
108  if ( $rev->getWikiId() !== $this->dbDomain ) {
109  throw new InvalidArgumentException( 'Mismatching wiki ID ' . $rev->getWikiId() );
110  }
111 
112  $audience = $hints['audience']
114 
115  if ( !$rev->audienceCan( RevisionRecord::DELETED_TEXT, $audience, $forUser ) ) {
116  // Returning null here is awkward, but consist with the signature of
117  // Revision::getContent() and RevisionRecord::getContent().
118  return null;
119  }
120 
121  if ( !$options ) {
122  $options = ParserOptions::newCanonical( $forUser ?: 'canonical' );
123  }
124 
125  $useMaster = $hints['use-master'] ?? false;
126 
127  $dbIndex = $useMaster
128  ? DB_MASTER // use latest values
129  : DB_REPLICA; // T154554
130 
131  $options->setSpeculativeRevIdCallback( function () use ( $dbIndex ) {
132  return $this->getSpeculativeRevId( $dbIndex );
133  } );
134  $options->setSpeculativePageIdCallback( function () use ( $dbIndex ) {
135  return $this->getSpeculativePageId( $dbIndex );
136  } );
137 
138  if ( !$rev->getId() && $rev->getTimestamp() ) {
139  // This is an unsaved revision with an already determined timestamp.
140  // Make the "current" time used during parsing match that of the revision.
141  // Any REVISION* parser variables will match up if the revision is saved.
142  $options->setTimestamp( $rev->getTimestamp() );
143  }
144 
146 
147  $renderedRevision = new RenderedRevision(
148  $title,
149  $rev,
150  $options,
151  function ( RenderedRevision $rrev, array $hints ) {
152  return $this->combineSlotOutput( $rrev, $hints );
153  },
154  $audience,
155  $forUser
156  );
157 
158  $renderedRevision->setSaveParseLogger( $this->saveParseLogger );
159 
160  if ( isset( $hints['known-revision-output'] ) ) {
161  $renderedRevision->setRevisionParserOutput( $hints['known-revision-output'] );
162  }
163 
164  return $renderedRevision;
165  }
166 
167  private function getSpeculativeRevId( $dbIndex ) {
168  // Use a separate master connection in order to see the latest data, by avoiding
169  // stale data from REPEATABLE-READ snapshots.
170  $flags = ILoadBalancer::CONN_TRX_AUTOCOMMIT;
171 
172  $db = $this->loadBalancer->getConnectionRef( $dbIndex, [], $this->dbDomain, $flags );
173 
174  return 1 + (int)$db->selectField(
175  'revision',
176  'MAX(rev_id)',
177  [],
178  __METHOD__
179  );
180  }
181 
182  private function getSpeculativePageId( $dbIndex ) {
183  // Use a separate master connection in order to see the latest data, by avoiding
184  // stale data from REPEATABLE-READ snapshots.
185  $flags = ILoadBalancer::CONN_TRX_AUTOCOMMIT;
186 
187  $db = $this->loadBalancer->getConnectionRef( $dbIndex, [], $this->dbDomain, $flags );
188 
189  return 1 + (int)$db->selectField(
190  'page',
191  'MAX(page_id)',
192  [],
193  __METHOD__
194  );
195  }
196 
207  private function combineSlotOutput( RenderedRevision $rrev, array $hints = [] ) {
208  $revision = $rrev->getRevision();
209  $slots = $revision->getSlots()->getSlots();
210 
211  $withHtml = $hints['generate-html'] ?? true;
212 
213  // short circuit if there is only the main slot
214  if ( array_keys( $slots ) === [ SlotRecord::MAIN ] ) {
215  return $rrev->getSlotParserOutput( SlotRecord::MAIN );
216  }
217 
218  // move main slot to front
219  if ( isset( $slots[SlotRecord::MAIN] ) ) {
220  $slots = [ SlotRecord::MAIN => $slots[SlotRecord::MAIN] ] + $slots;
221  }
222 
223  $combinedOutput = new ParserOutput( null );
224  $slotOutput = [];
225 
226  $options = $rrev->getOptions();
227  $options->registerWatcher( [ $combinedOutput, 'recordOption' ] );
228 
229  foreach ( $slots as $role => $slot ) {
230  $out = $rrev->getSlotParserOutput( $role, $hints );
231  $slotOutput[$role] = $out;
232 
233  // XXX: should the SlotRoleHandler be able to intervene here?
234  $combinedOutput->mergeInternalMetaDataFrom( $out );
235  $combinedOutput->mergeTrackingMetaDataFrom( $out );
236  }
237 
238  if ( $withHtml ) {
239  $html = '';
240  $first = true;
242  foreach ( $slotOutput as $role => $out ) {
243  $roleHandler = $this->roleRegistery->getRoleHandler( $role );
244 
245  // TODO: put more fancy layout logic here, see T200915.
246  $layout = $roleHandler->getOutputLayoutHints();
247  $display = $layout['display'] ?? 'section';
248 
249  if ( $display === 'none' ) {
250  continue;
251  }
252 
253  if ( $first ) {
254  // skip header for the first slot
255  $first = false;
256  } else {
257  // NOTE: this placeholder is hydrated by ParserOutput::getText().
258  $headText = Html::element( 'mw:slotheader', [], $role );
259  $html .= Html::rawElement( 'h1', [ 'class' => 'mw-slot-header' ], $headText );
260  }
261 
262  // XXX: do we want to put a wrapper div around the output?
263  // Do we want to let $roleHandler do that?
264  $html .= $out->getRawText();
265  $combinedOutput->mergeHtmlMetaDataFrom( $out );
266  }
267 
268  $combinedOutput->setText( $html );
269  }
270 
271  $options->registerWatcher( null );
272  return $combinedOutput;
273  }
274 
275 }
ParserOptions
Set options of the Parser.
Definition: ParserOptions.php:44
Revision\RevisionRenderer\getSpeculativePageId
getSpeculativePageId( $dbIndex)
Definition: RevisionRenderer.php:182
Revision\RenderedRevision\getRevision
getRevision()
Definition: RenderedRevision.php:150
Revision\RevisionRenderer\$roleRegistery
SlotRoleRegistry $roleRegistery
Definition: RevisionRenderer.php:54
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:46
ParserOutput
Definition: ParserOutput.php:25
Revision\RevisionRenderer\$dbDomain
string bool $dbDomain
Definition: RevisionRenderer.php:57
Revision\RenderedRevision\setSaveParseLogger
setSaveParseLogger(LoggerInterface $saveParseLogger)
Definition: RenderedRevision.php:136
Revision\RevisionRecord\getTimestamp
getTimestamp()
MCR migration note: this replaces Revision::getTimestamp.
Definition: RevisionRecord.php:442
Revision\RevisionRecord\audienceCan
audienceCan( $field, $audience, User $user=null)
Check that the given audience has access to the given field.
Definition: RevisionRecord.php:463
Revision\RevisionRenderer\__construct
__construct(ILoadBalancer $loadBalancer, SlotRoleRegistry $roleRegistry, $dbDomain=false)
Definition: RevisionRenderer.php:64
MediaWiki\Revision
Definition: ContributionsLookup.php:3
Revision\RevisionRenderer\getRenderedRevision
getRenderedRevision(RevisionRecord $rev, ParserOptions $options=null, User $forUser=null, array $hints=[])
Definition: RevisionRenderer.php:102
Revision\RenderedRevision\getSlotParserOutput
getSlotParserOutput( $role, array $hints=[])
Definition: RenderedRevision.php:221
Revision\RevisionRenderer\getSpeculativeRevId
getSpeculativeRevId( $dbIndex)
Definition: RevisionRenderer.php:167
$title
$title
Definition: testCompression.php:38
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
DB_MASTER
const DB_MASTER
Definition: defines.php:26
Revision\RevisionRecord\getId
getId()
Get revision ID.
Definition: RevisionRecord.php:279
Revision\RevisionRenderer
The RevisionRenderer service provides access to rendered output for revisions.
Definition: RevisionRenderer.php:45
Revision\RevisionRenderer\combineSlotOutput
combineSlotOutput(RenderedRevision $rrev, array $hints=[])
This implements the layout for combining the output of multiple slots.
Definition: RevisionRenderer.php:207
ParserOptions\newCanonical
static newCanonical( $context=null, $userLang=null)
Creates a "canonical" ParserOptions object.
Definition: ParserOptions.php:1135
Revision\RevisionRenderer\$saveParseLogger
LoggerInterface $saveParseLogger
Definition: RevisionRenderer.php:48
Revision\SlotRecord\MAIN
const MAIN
Definition: SlotRecord.php:41
Title\newFromLinkTarget
static newFromLinkTarget(LinkTarget $linkTarget, $forceClone='')
Returns a Title given a LinkTarget.
Definition: Title.php:281
Revision\RevisionRecord\FOR_PUBLIC
const FOR_PUBLIC
Definition: RevisionRecord.php:58
Revision\RevisionRecord\getPageAsLinkTarget
getPageAsLinkTarget()
Returns the title of the page this revision is associated with as a LinkTarget object.
Definition: RevisionRecord.php:351
Title
Represents a title within MediaWiki.
Definition: Title.php:42
Revision\RevisionRecord\DELETED_TEXT
const DELETED_TEXT
Definition: RevisionRecord.php:49
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:209
Revision\RevisionRenderer\$loadBalancer
ILoadBalancer $loadBalancer
Definition: RevisionRenderer.php:51
Revision\SlotRoleRegistry
A registry service for SlotRoleHandlers, used to define which slot roles are available on which page.
Definition: SlotRoleRegistry.php:48
Revision\RenderedRevision
RenderedRevision represents the rendered representation of a revision.
Definition: RenderedRevision.php:43
Html\element
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:231
User
The User object encapsulates all of the user-specific settings (user_id, name, rights,...
Definition: User.php:54
Revision\RevisionRecord\FOR_THIS_USER
const FOR_THIS_USER
Definition: RevisionRecord.php:59
Revision\RevisionRenderer\setLogger
setLogger(LoggerInterface $saveParseLogger)
Definition: RevisionRenderer.php:78
Revision\RevisionRecord\getWikiId
getWikiId()
Get the ID of the wiki this revision belongs to.
Definition: RevisionRecord.php:340
Wikimedia\Rdbms\ILoadBalancer
Database cluster connection, tracking, load balancing, and transaction manager interface.
Definition: ILoadBalancer.php:81
Revision\RenderedRevision\getOptions
getOptions()
Definition: RenderedRevision.php:157
Html
This class is a collection of static functions that serve two purposes:
Definition: Html.php:49