MediaWiki  master
RevisionRenderer.php
Go to the documentation of this file.
1 <?php
23 namespace MediaWiki\Revision;
24 
25 use Html;
26 use InvalidArgumentException;
29 use ParserOptions;
30 use ParserOutput;
31 use Psr\Log\LoggerInterface;
32 use Psr\Log\NullLogger;
34 
46 
49 
51  private $loadBalancer;
52 
54  private $roleRegistery;
55 
58 
60  private $dbDomain;
61 
68  public function __construct(
70  SlotRoleRegistry $roleRegistry,
72  $dbDomain = false
73  ) {
74  $this->loadBalancer = $loadBalancer;
75  $this->roleRegistery = $roleRegistry;
76  $this->contentRenderer = $contentRenderer;
77  $this->dbDomain = $dbDomain;
78  $this->saveParseLogger = new NullLogger();
79  }
80 
84  public function setLogger( LoggerInterface $saveParseLogger ) {
85  $this->saveParseLogger = $saveParseLogger;
86  }
87 
108  public function getRenderedRevision(
109  RevisionRecord $rev,
110  ParserOptions $options = null,
111  Authority $forPerformer = null,
112  array $hints = []
113  ) {
114  if ( $rev->getWikiId() !== $this->dbDomain ) {
115  throw new InvalidArgumentException( 'Mismatching wiki ID ' . $rev->getWikiId() );
116  }
117 
118  $audience = $hints['audience']
120 
121  if ( !$rev->audienceCan( RevisionRecord::DELETED_TEXT, $audience, $forPerformer ) ) {
122  // Returning null here is awkward, but consistent with the signature of
123  // RevisionRecord::getContent().
124  return null;
125  }
126 
127  if ( !$options ) {
128  $options = ParserOptions::newCanonical(
129  $forPerformer ? $forPerformer->getUser() : 'canonical'
130  );
131  }
132 
133  $usePrimary = $hints['use-master'] ?? false;
134 
135  $dbIndex = $usePrimary
136  ? DB_PRIMARY // use latest values
137  : DB_REPLICA; // T154554
138 
139  $options->setSpeculativeRevIdCallback( function () use ( $dbIndex ) {
140  return $this->getSpeculativeRevId( $dbIndex );
141  } );
142  $options->setSpeculativePageIdCallback( function () use ( $dbIndex ) {
143  return $this->getSpeculativePageId( $dbIndex );
144  } );
145 
146  if ( !$rev->getId() && $rev->getTimestamp() ) {
147  // This is an unsaved revision with an already determined timestamp.
148  // Make the "current" time used during parsing match that of the revision.
149  // Any REVISION* parser variables will match up if the revision is saved.
150  $options->setTimestamp( $rev->getTimestamp() );
151  }
152 
153  $renderedRevision = new RenderedRevision(
154  $rev,
155  $options,
156  $this->contentRenderer,
157  function ( RenderedRevision $rrev, array $hints ) {
158  return $this->combineSlotOutput( $rrev, $hints );
159  },
160  $audience,
161  $forPerformer
162  );
163 
164  $renderedRevision->setSaveParseLogger( $this->saveParseLogger );
165 
166  if ( isset( $hints['known-revision-output'] ) ) {
167  $renderedRevision->setRevisionParserOutput( $hints['known-revision-output'] );
168  }
169 
170  return $renderedRevision;
171  }
172 
173  private function getSpeculativeRevId( $dbIndex ) {
174  // Use a separate primary DB connection in order to see the latest data, by avoiding
175  // stale data from REPEATABLE-READ snapshots.
177 
178  $db = $this->loadBalancer->getConnectionRef( $dbIndex, [], $this->dbDomain, $flags );
179 
180  return 1 + (int)$db->selectField(
181  'revision',
182  'MAX(rev_id)',
183  [],
184  __METHOD__
185  );
186  }
187 
188  private function getSpeculativePageId( $dbIndex ) {
189  // Use a separate primary DB connection in order to see the latest data, by avoiding
190  // stale data from REPEATABLE-READ snapshots.
192 
193  $db = $this->loadBalancer->getConnectionRef( $dbIndex, [], $this->dbDomain, $flags );
194 
195  return 1 + (int)$db->selectField(
196  'page',
197  'MAX(page_id)',
198  [],
199  __METHOD__
200  );
201  }
202 
213  private function combineSlotOutput( RenderedRevision $rrev, array $hints = [] ) {
214  $revision = $rrev->getRevision();
215  $slots = $revision->getSlots()->getSlots();
216 
217  $withHtml = $hints['generate-html'] ?? true;
218 
219  // short circuit if there is only the main slot
220  if ( array_keys( $slots ) === [ SlotRecord::MAIN ] ) {
221  return $rrev->getSlotParserOutput( SlotRecord::MAIN, $hints );
222  }
223 
224  // move main slot to front
225  if ( isset( $slots[SlotRecord::MAIN] ) ) {
226  $slots = [ SlotRecord::MAIN => $slots[SlotRecord::MAIN] ] + $slots;
227  }
228 
229  $combinedOutput = new ParserOutput( null );
230  $slotOutput = [];
231 
232  $options = $rrev->getOptions();
233  $options->registerWatcher( [ $combinedOutput, 'recordOption' ] );
234 
235  foreach ( $slots as $role => $slot ) {
236  $out = $rrev->getSlotParserOutput( $role, $hints );
237  $slotOutput[$role] = $out;
238 
239  // XXX: should the SlotRoleHandler be able to intervene here?
240  $combinedOutput->mergeInternalMetaDataFrom( $out );
241  $combinedOutput->mergeTrackingMetaDataFrom( $out );
242  }
243 
244  if ( $withHtml ) {
245  $html = '';
246  $first = true;
248  foreach ( $slotOutput as $role => $out ) {
249  $roleHandler = $this->roleRegistery->getRoleHandler( $role );
250 
251  // TODO: put more fancy layout logic here, see T200915.
252  $layout = $roleHandler->getOutputLayoutHints();
253  $display = $layout['display'] ?? 'section';
254 
255  if ( $display === 'none' ) {
256  continue;
257  }
258 
259  if ( $first ) {
260  // skip header for the first slot
261  $first = false;
262  } else {
263  // NOTE: this placeholder is hydrated by ParserOutput::getText().
264  $headText = Html::element( 'mw:slotheader', [], $role );
265  $html .= Html::rawElement( 'h1', [ 'class' => 'mw-slot-header' ], $headText );
266  }
267 
268  // XXX: do we want to put a wrapper div around the output?
269  // Do we want to let $roleHandler do that?
270  $html .= $out->getRawText();
271  $combinedOutput->mergeHtmlMetaDataFrom( $out );
272  }
273 
274  $combinedOutput->setText( $html );
275  }
276 
277  $options->registerWatcher( null );
278  return $combinedOutput;
279  }
280 
281 }
MediaWiki\Revision\RenderedRevision\getOptions
getOptions()
Definition: RenderedRevision.php:160
ParserOptions
Set options of the Parser.
Definition: ParserOptions.php:45
MediaWiki\Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:47
ParserOutput
Definition: ParserOutput.php:35
MediaWiki\Revision\RevisionRenderer\$dbDomain
string bool $dbDomain
Definition: RevisionRenderer.php:60
MediaWiki\Revision\RevisionRenderer\$roleRegistery
SlotRoleRegistry $roleRegistery
Definition: RevisionRenderer.php:54
MediaWiki\Revision
Definition: ArchivedRevisionLookup.php:21
MediaWiki\Revision\RevisionRecord\DELETED_TEXT
const DELETED_TEXT
Definition: RevisionRecord.php:53
MediaWiki\Revision\SlotRecord\MAIN
const MAIN
Definition: SlotRecord.php:43
MediaWiki\Revision\RevisionRenderer\getSpeculativePageId
getSpeculativePageId( $dbIndex)
Definition: RevisionRenderer.php:188
MediaWiki\Revision\RevisionRenderer\getRenderedRevision
getRenderedRevision(RevisionRecord $rev, ParserOptions $options=null, Authority $forPerformer=null, array $hints=[])
Definition: RevisionRenderer.php:108
ParserOptions\newCanonical
static newCanonical( $context, $userLang=null)
Creates a "canonical" ParserOptions object.
Definition: ParserOptions.php:1089
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
MediaWiki\Content\Renderer\ContentRenderer
A service to render content.
Definition: ContentRenderer.php:15
MediaWiki\Revision\RenderedRevision\getRevision
getRevision()
Definition: RenderedRevision.php:153
MediaWiki\Revision\RevisionRenderer\combineSlotOutput
combineSlotOutput(RenderedRevision $rrev, array $hints=[])
This implements the layout for combining the output of multiple slots.
Definition: RevisionRenderer.php:213
MediaWiki\Revision\RevisionRecord\audienceCan
audienceCan( $field, $audience, Authority $performer=null)
Check that the given audience has access to the given field.
Definition: RevisionRecord.php:479
MediaWiki\Revision\RevisionRenderer\$loadBalancer
ILoadBalancer $loadBalancer
Definition: RevisionRenderer.php:51
MediaWiki\Permissions\Authority
This interface represents the authority associated the current execution context, such as a web reque...
Definition: Authority.php:37
MediaWiki\Revision\RevisionRenderer
The RevisionRenderer service provides access to rendered output for revisions.
Definition: RevisionRenderer.php:45
MediaWiki\Revision\RevisionRenderer\$saveParseLogger
LoggerInterface $saveParseLogger
Definition: RevisionRenderer.php:48
DB_PRIMARY
const DB_PRIMARY
Definition: defines.php:27
MediaWiki\Revision\RenderedRevision\getSlotParserOutput
getSlotParserOutput( $role, array $hints=[])
Definition: RenderedRevision.php:224
Wikimedia\Rdbms\ILoadBalancer\CONN_TRX_AUTOCOMMIT
const CONN_TRX_AUTOCOMMIT
DB handle should have DBO_TRX disabled and the caller will leave it as such.
Definition: ILoadBalancer.php:104
MediaWiki\Revision\RenderedRevision\setSaveParseLogger
setSaveParseLogger(LoggerInterface $saveParseLogger)
Definition: RenderedRevision.php:139
MediaWiki\Revision\RevisionRecord\getWikiId
getWikiId()
Get the ID of the wiki this revision belongs to.
Definition: RevisionRecord.php:345
MediaWiki\Revision\RevisionRecord\getId
getId( $wikiId=self::LOCAL)
Get revision ID.
Definition: RevisionRecord.php:279
MediaWiki\Revision\RevisionRenderer\__construct
__construct(ILoadBalancer $loadBalancer, SlotRoleRegistry $roleRegistry, ContentRenderer $contentRenderer, $dbDomain=false)
Definition: RevisionRenderer.php:68
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:213
MediaWiki\Revision\RevisionRecord\getTimestamp
getTimestamp()
MCR migration note: this replaced Revision::getTimestamp.
Definition: RevisionRecord.php:459
MediaWiki\Revision\RevisionRecord\FOR_THIS_USER
const FOR_THIS_USER
Definition: RevisionRecord.php:63
MediaWiki\Revision\SlotRoleRegistry
A registry service for SlotRoleHandlers, used to define which slot roles are available on which page.
Definition: SlotRoleRegistry.php:48
MediaWiki\Revision\RevisionRenderer\setLogger
setLogger(LoggerInterface $saveParseLogger)
Definition: RevisionRenderer.php:84
MediaWiki\Revision\RenderedRevision
RenderedRevision represents the rendered representation of a revision.
Definition: RenderedRevision.php:45
Html\element
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:235
MediaWiki\Revision\RevisionRenderer\getSpeculativeRevId
getSpeculativeRevId( $dbIndex)
Definition: RevisionRenderer.php:173
MediaWiki\Revision\RevisionRecord\FOR_PUBLIC
const FOR_PUBLIC
Definition: RevisionRecord.php:62
MediaWiki\Revision\RevisionRenderer\$contentRenderer
ContentRenderer $contentRenderer
Definition: RevisionRenderer.php:57
Wikimedia\Rdbms\ILoadBalancer
Database cluster connection, tracking, load balancing, and transaction manager interface.
Definition: ILoadBalancer.php:81
Html
This class is a collection of static functions that serve two purposes:
Definition: Html.php:50