MediaWiki  master
RevisionRenderer.php
Go to the documentation of this file.
1 <?php
23 namespace MediaWiki\Revision;
24 
25 use Html;
26 use InvalidArgumentException;
28 use ParserOptions;
29 use ParserOutput;
30 use Psr\Log\LoggerInterface;
31 use Psr\Log\NullLogger;
32 use Title;
34 
46 
49 
51  private $loadBalancer;
52 
54  private $roleRegistery;
55 
57  private $dbDomain;
58 
64  public function __construct(
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  Authority $forPerformer = 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, $forPerformer ) ) {
116  // Returning null here is awkward, but consistent with the signature of
117  // RevisionRecord::getContent().
118  return null;
119  }
120 
121  if ( !$options ) {
122  $options = ParserOptions::newCanonical(
123  $forPerformer ? $forPerformer->getUser() : 'canonical'
124  );
125  }
126 
127  $usePrimary = $hints['use-master'] ?? false;
128 
129  $dbIndex = $usePrimary
130  ? DB_PRIMARY // use latest values
131  : DB_REPLICA; // T154554
132 
133  $options->setSpeculativeRevIdCallback( function () use ( $dbIndex ) {
134  return $this->getSpeculativeRevId( $dbIndex );
135  } );
136  $options->setSpeculativePageIdCallback( function () use ( $dbIndex ) {
137  return $this->getSpeculativePageId( $dbIndex );
138  } );
139 
140  if ( !$rev->getId() && $rev->getTimestamp() ) {
141  // This is an unsaved revision with an already determined timestamp.
142  // Make the "current" time used during parsing match that of the revision.
143  // Any REVISION* parser variables will match up if the revision is saved.
144  $options->setTimestamp( $rev->getTimestamp() );
145  }
146 
148 
149  $renderedRevision = new RenderedRevision(
150  $title,
151  $rev,
152  $options,
153  function ( RenderedRevision $rrev, array $hints ) {
154  return $this->combineSlotOutput( $rrev, $hints );
155  },
156  $audience,
157  $forPerformer
158  );
159 
160  $renderedRevision->setSaveParseLogger( $this->saveParseLogger );
161 
162  if ( isset( $hints['known-revision-output'] ) ) {
163  $renderedRevision->setRevisionParserOutput( $hints['known-revision-output'] );
164  }
165 
166  return $renderedRevision;
167  }
168 
169  private function getSpeculativeRevId( $dbIndex ) {
170  // Use a separate primary DB connection in order to see the latest data, by avoiding
171  // stale data from REPEATABLE-READ snapshots.
173 
174  $db = $this->loadBalancer->getConnectionRef( $dbIndex, [], $this->dbDomain, $flags );
175 
176  return 1 + (int)$db->selectField(
177  'revision',
178  'MAX(rev_id)',
179  [],
180  __METHOD__
181  );
182  }
183 
184  private function getSpeculativePageId( $dbIndex ) {
185  // Use a separate primary DB connection in order to see the latest data, by avoiding
186  // stale data from REPEATABLE-READ snapshots.
188 
189  $db = $this->loadBalancer->getConnectionRef( $dbIndex, [], $this->dbDomain, $flags );
190 
191  return 1 + (int)$db->selectField(
192  'page',
193  'MAX(page_id)',
194  [],
195  __METHOD__
196  );
197  }
198 
209  private function combineSlotOutput( RenderedRevision $rrev, array $hints = [] ) {
210  $revision = $rrev->getRevision();
211  $slots = $revision->getSlots()->getSlots();
212 
213  $withHtml = $hints['generate-html'] ?? true;
214 
215  // short circuit if there is only the main slot
216  if ( array_keys( $slots ) === [ SlotRecord::MAIN ] ) {
217  return $rrev->getSlotParserOutput( SlotRecord::MAIN, $hints );
218  }
219 
220  // move main slot to front
221  if ( isset( $slots[SlotRecord::MAIN] ) ) {
222  $slots = [ SlotRecord::MAIN => $slots[SlotRecord::MAIN] ] + $slots;
223  }
224 
225  $combinedOutput = new ParserOutput( null );
226  $slotOutput = [];
227 
228  $options = $rrev->getOptions();
229  $options->registerWatcher( [ $combinedOutput, 'recordOption' ] );
230 
231  foreach ( $slots as $role => $slot ) {
232  $out = $rrev->getSlotParserOutput( $role, $hints );
233  $slotOutput[$role] = $out;
234 
235  // XXX: should the SlotRoleHandler be able to intervene here?
236  $combinedOutput->mergeInternalMetaDataFrom( $out );
237  $combinedOutput->mergeTrackingMetaDataFrom( $out );
238  }
239 
240  if ( $withHtml ) {
241  $html = '';
242  $first = true;
244  foreach ( $slotOutput as $role => $out ) {
245  $roleHandler = $this->roleRegistery->getRoleHandler( $role );
246 
247  // TODO: put more fancy layout logic here, see T200915.
248  $layout = $roleHandler->getOutputLayoutHints();
249  $display = $layout['display'] ?? 'section';
250 
251  if ( $display === 'none' ) {
252  continue;
253  }
254 
255  if ( $first ) {
256  // skip header for the first slot
257  $first = false;
258  } else {
259  // NOTE: this placeholder is hydrated by ParserOutput::getText().
260  $headText = Html::element( 'mw:slotheader', [], $role );
261  $html .= Html::rawElement( 'h1', [ 'class' => 'mw-slot-header' ], $headText );
262  }
263 
264  // XXX: do we want to put a wrapper div around the output?
265  // Do we want to let $roleHandler do that?
266  $html .= $out->getRawText();
267  $combinedOutput->mergeHtmlMetaDataFrom( $out );
268  }
269 
270  $combinedOutput->setText( $html );
271  }
272 
273  $options->registerWatcher( null );
274  return $combinedOutput;
275  }
276 
277 }
MediaWiki\Revision\RenderedRevision\getOptions
getOptions()
Definition: RenderedRevision.php:157
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:31
MediaWiki\Revision\RevisionRenderer\$dbDomain
string bool $dbDomain
Definition: RevisionRenderer.php:57
MediaWiki\Revision\RevisionRenderer\$roleRegistery
SlotRoleRegistry $roleRegistery
Definition: RevisionRenderer.php:54
MediaWiki\Revision
Definition: ContributionsLookup.php:3
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:184
MediaWiki\Revision\RevisionRenderer\getRenderedRevision
getRenderedRevision(RevisionRecord $rev, ParserOptions $options=null, Authority $forPerformer=null, array $hints=[])
Definition: RevisionRenderer.php:102
ParserOptions\newCanonical
static newCanonical( $context, $userLang=null)
Creates a "canonical" ParserOptions object.
Definition: ParserOptions.php:1134
$title
$title
Definition: testCompression.php:38
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
MediaWiki\Revision\RenderedRevision\getRevision
getRevision()
Definition: RenderedRevision.php:150
MediaWiki\Revision\RevisionRenderer\combineSlotOutput
combineSlotOutput(RenderedRevision $rrev, array $hints=[])
This implements the layout for combining the output of multiple slots.
Definition: RevisionRenderer.php:209
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
MediaWiki\Revision\RevisionRecord\getPageAsLinkTarget
getPageAsLinkTarget()
Returns the title of the page this revision is associated with as a LinkTarget object.
Definition: RevisionRecord.php:355
DB_PRIMARY
const DB_PRIMARY
Definition: defines.php:27
MediaWiki\Revision\RenderedRevision\getSlotParserOutput
getSlotParserOutput( $role, array $hints=[])
Definition: RenderedRevision.php:221
Title\newFromLinkTarget
static newFromLinkTarget(LinkTarget $linkTarget, $forceClone='')
Returns a Title given a LinkTarget.
Definition: Title.php:295
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:136
MediaWiki\Revision\RevisionRecord\getWikiId
getWikiId()
Get the ID of the wiki this revision belongs to.
Definition: RevisionRecord.php:345
Title
Represents a title within MediaWiki.
Definition: Title.php:48
MediaWiki\Revision\RevisionRecord\getId
getId( $wikiId=self::LOCAL)
Get revision ID.
Definition: RevisionRecord.php:279
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:210
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:78
MediaWiki\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:232
MediaWiki\Revision\RevisionRenderer\getSpeculativeRevId
getSpeculativeRevId( $dbIndex)
Definition: RevisionRenderer.php:169
MediaWiki\Revision\RevisionRecord\FOR_PUBLIC
const FOR_PUBLIC
Definition: RevisionRecord.php:62
MediaWiki\Revision\RevisionRenderer\__construct
__construct(ILoadBalancer $loadBalancer, SlotRoleRegistry $roleRegistry, $dbDomain=false)
Definition: RevisionRenderer.php:64
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:49