MediaWiki  1.34.0
ContentHandler.php
Go to the documentation of this file.
1 <?php
2 
30 use Wikimedia\Assert\Assert;
35 
55 abstract class ContentHandler {
85  public static function getContentText( Content $content = null ) {
87 
88  if ( is_null( $content ) ) {
89  return '';
90  }
91 
92  if ( $content instanceof TextContent ) {
93  return $content->getText();
94  }
95 
96  wfDebugLog( 'ContentHandler', 'Accessing ' . $content->getModel() . ' content as text!' );
97 
98  if ( $wgContentHandlerTextFallback == 'fail' ) {
99  throw new MWException(
100  "Attempt to get text from Content with model " .
101  $content->getModel()
102  );
103  }
104 
105  if ( $wgContentHandlerTextFallback == 'serialize' ) {
106  return $content->serialize();
107  }
108 
109  return null;
110  }
111 
135  public static function makeContent( $text, Title $title = null,
136  $modelId = null, $format = null ) {
137  if ( is_null( $modelId ) ) {
138  if ( is_null( $title ) ) {
139  throw new MWException( "Must provide a Title object or a content model ID." );
140  }
141 
142  $modelId = $title->getContentModel();
143  }
144 
145  $handler = self::getForModelID( $modelId );
146 
147  return $handler->unserializeContent( $text, $format );
148  }
149 
186  public static function getDefaultModelFor( Title $title ) {
187  $slotRoleregistry = MediaWikiServices::getInstance()->getSlotRoleRegistry();
188  $mainSlotHandler = $slotRoleregistry->getRoleHandler( 'main' );
189  return $mainSlotHandler->getDefaultModel( $title );
190  }
191 
201  public static function getForTitle( Title $title ) {
202  $modelId = $title->getContentModel();
203 
204  return self::getForModelID( $modelId );
205  }
206 
217  public static function getForContent( Content $content ) {
218  $modelId = $content->getModel();
219 
220  return self::getForModelID( $modelId );
221  }
222 
226  protected static $handlers;
227 
254  public static function getForModelID( $modelId ) {
255  global $wgContentHandlers;
256 
257  if ( isset( self::$handlers[$modelId] ) ) {
258  return self::$handlers[$modelId];
259  }
260 
261  if ( empty( $wgContentHandlers[$modelId] ) ) {
262  $handler = null;
263 
264  Hooks::run( 'ContentHandlerForModelID', [ $modelId, &$handler ] );
265 
266  if ( $handler === null ) {
267  throw new MWUnknownContentModelException( $modelId );
268  }
269 
270  if ( !( $handler instanceof ContentHandler ) ) {
271  throw new MWException( "ContentHandlerForModelID must supply a ContentHandler instance" );
272  }
273  } else {
274  $classOrCallback = $wgContentHandlers[$modelId];
275 
276  if ( is_callable( $classOrCallback ) ) {
277  $handler = call_user_func( $classOrCallback, $modelId );
278  } else {
279  $handler = new $classOrCallback( $modelId );
280  }
281 
282  if ( !( $handler instanceof ContentHandler ) ) {
283  throw new MWException(
284  var_export( $classOrCallback, true ) . " from \$wgContentHandlers is not " .
285  "compatible with ContentHandler"
286  );
287  }
288  }
289 
290  wfDebugLog( 'ContentHandler', 'Created handler for ' . $modelId
291  . ': ' . get_class( $handler ) );
292 
293  self::$handlers[$modelId] = $handler;
294 
295  return self::$handlers[$modelId];
296  }
297 
301  public static function cleanupHandlersCache() {
302  self::$handlers = [];
303  }
304 
318  public static function getLocalizedName( $name, Language $lang = null ) {
319  // Messages: content-model-wikitext, content-model-text,
320  // content-model-javascript, content-model-css
321  $key = "content-model-$name";
322 
323  $msg = wfMessage( $key );
324  if ( $lang ) {
325  $msg->inLanguage( $lang );
326  }
327 
328  return $msg->exists() ? $msg->plain() : $name;
329  }
330 
331  public static function getContentModels() {
332  global $wgContentHandlers;
333 
334  $models = array_keys( $wgContentHandlers );
335  Hooks::run( 'GetContentModels', [ &$models ] );
336  return $models;
337  }
338 
339  public static function getAllContentFormats() {
340  global $wgContentHandlers;
341 
342  $formats = [];
343 
344  foreach ( $wgContentHandlers as $model => $class ) {
345  $handler = self::getForModelID( $model );
346  $formats = array_merge( $formats, $handler->getSupportedFormats() );
347  }
348 
349  $formats = array_unique( $formats );
350 
351  return $formats;
352  }
353 
354  // ------------------------------------------------------------------------
355 
359  protected $mModelID;
360 
365 
375  public function __construct( $modelId, $formats ) {
376  $this->mModelID = $modelId;
377  $this->mSupportedFormats = $formats;
378  }
379 
390  abstract public function serializeContent( Content $content, $format = null );
391 
402  public function exportTransform( $blob, $format = null ) {
403  return $blob;
404  }
405 
416  abstract public function unserializeContent( $blob, $format = null );
417 
429  public function importTransform( $blob, $format = null ) {
430  return $blob;
431  }
432 
441  abstract public function makeEmptyContent();
442 
460  public function makeRedirectContent( Title $destination, $text = '' ) {
461  return null;
462  }
463 
472  public function getModelID() {
473  return $this->mModelID;
474  }
475 
484  protected function checkModelID( $model_id ) {
485  if ( $model_id !== $this->mModelID ) {
486  throw new MWException( "Bad content model: " .
487  "expected {$this->mModelID} " .
488  "but got $model_id." );
489  }
490  }
491 
501  public function getSupportedFormats() {
503  }
504 
516  public function getDefaultFormat() {
517  return $this->mSupportedFormats[0];
518  }
519 
533  public function isSupportedFormat( $format ) {
534  if ( !$format ) {
535  return true; // this means "use the default"
536  }
537 
538  return in_array( $format, $this->mSupportedFormats );
539  }
540 
548  protected function checkFormat( $format ) {
549  if ( !$this->isSupportedFormat( $format ) ) {
550  throw new MWException(
551  "Format $format is not supported for content model "
552  . $this->getModelID()
553  );
554  }
555  }
556 
572  public function getActionOverrides() {
573  return [];
574  }
575 
603  public function createDifferenceEngine( IContextSource $context, $old = 0, $new = 0,
604  $rcid = 0, // FIXME: Deprecated, no longer used
605  $refreshCache = false, $unhide = false
606  ) {
607  $diffEngineClass = $this->getDiffEngineClass();
608  $differenceEngine = new $diffEngineClass( $context, $old, $new, $rcid, $refreshCache, $unhide );
609  Hooks::run( 'GetDifferenceEngine', [ $context, $old, $new, $refreshCache, $unhide,
610  &$differenceEngine ] );
611  return $differenceEngine;
612  }
613 
620  final public function getSlotDiffRenderer( IContextSource $context ) {
621  $slotDiffRenderer = $this->getSlotDiffRendererInternal( $context );
622  if ( get_class( $slotDiffRenderer ) === TextSlotDiffRenderer::class ) {
623  // To keep B/C, when SlotDiffRenderer is not overridden for a given content type
624  // but DifferenceEngine is, use that instead.
625  $differenceEngine = $this->createDifferenceEngine( $context );
626  if ( get_class( $differenceEngine ) !== DifferenceEngine::class ) {
627  // TODO turn this into a deprecation warning in a later release
628  LoggerFactory::getInstance( 'diff' )->info(
629  'Falling back to DifferenceEngineSlotDiffRenderer', [
630  'modelID' => $this->getModelID(),
631  'DifferenceEngine' => get_class( $differenceEngine ),
632  ] );
633  $slotDiffRenderer = new DifferenceEngineSlotDiffRenderer( $differenceEngine );
634  }
635  }
636  Hooks::run( 'GetSlotDiffRenderer', [ $this, &$slotDiffRenderer, $context ] );
637  return $slotDiffRenderer;
638  }
639 
646  $contentLanguage = MediaWikiServices::getInstance()->getContentLanguage();
647  $statsdDataFactory = MediaWikiServices::getInstance()->getStatsdDataFactory();
648  $slotDiffRenderer = new TextSlotDiffRenderer();
649  $slotDiffRenderer->setStatsdDataFactory( $statsdDataFactory );
650  // XXX using the page language would be better, but it's unclear how that should be injected
651  $slotDiffRenderer->setLanguage( $contentLanguage );
652 
653  $engine = DifferenceEngine::getEngine();
654  if ( $engine === 'php' ) {
655  $slotDiffRenderer->setEngine( TextSlotDiffRenderer::ENGINE_PHP );
656  } elseif ( $engine === 'wikidiff2' ) {
657  $slotDiffRenderer->setEngine( TextSlotDiffRenderer::ENGINE_WIKIDIFF2 );
658  } else {
659  $slotDiffRenderer->setEngine( TextSlotDiffRenderer::ENGINE_EXTERNAL, $engine );
660  }
661 
662  return $slotDiffRenderer;
663  }
664 
684  public function getPageLanguage( Title $title, Content $content = null ) {
685  global $wgLang;
686  $pageLang = MediaWikiServices::getInstance()->getContentLanguage();
687 
688  if ( $title->inNamespace( NS_MEDIAWIKI ) ) {
689  // Parse mediawiki messages with correct target language
690  list( /* $unused */, $lang ) = MessageCache::singleton()->figureMessage( $title->getText() );
691  $pageLang = Language::factory( $lang );
692  }
693 
694  // Simplify hook handlers by only passing objects of one type, in case nothing
695  // else has unstubbed the StubUserLang object by now.
697 
698  Hooks::run( 'PageContentLanguage', [ $title, &$pageLang, $wgLang ] );
699 
700  return wfGetLangObj( $pageLang );
701  }
702 
723  public function getPageViewLanguage( Title $title, Content $content = null ) {
724  $pageLang = $this->getPageLanguage( $title, $content );
725 
726  if ( $title->getNamespace() !== NS_MEDIAWIKI ) {
727  // If the user chooses a variant, the content is actually
728  // in a language whose code is the variant code.
729  $variant = $pageLang->getPreferredVariant();
730  if ( $pageLang->getCode() !== $variant ) {
731  $pageLang = Language::factory( $variant );
732  }
733  }
734 
735  return $pageLang;
736  }
737 
756  public function canBeUsedOn( Title $title ) {
757  $ok = true;
758 
759  Hooks::run( 'ContentModelCanBeUsedOn', [ $this->getModelID(), $title, &$ok ] );
760 
761  return $ok;
762  }
763 
771  protected function getDiffEngineClass() {
772  return DifferenceEngine::class;
773  }
774 
789  public function merge3( Content $oldContent, Content $myContent, Content $yourContent ) {
790  return false;
791  }
792 
804  private function getChangeType(
805  Content $oldContent = null,
806  Content $newContent = null,
807  $flags = 0
808  ) {
809  $oldTarget = $oldContent !== null ? $oldContent->getRedirectTarget() : null;
810  $newTarget = $newContent !== null ? $newContent->getRedirectTarget() : null;
811 
812  // We check for the type of change in the given edit, and return string key accordingly
813 
814  // Blanking of a page
815  if ( $oldContent && $oldContent->getSize() > 0 &&
816  $newContent && $newContent->getSize() === 0
817  ) {
818  return 'blank';
819  }
820 
821  // Redirects
822  if ( $newTarget ) {
823  if ( !$oldTarget ) {
824  // New redirect page (by creating new page or by changing content page)
825  return 'new-redirect';
826  } elseif ( !$newTarget->equals( $oldTarget ) ||
827  $oldTarget->getFragment() !== $newTarget->getFragment()
828  ) {
829  // Redirect target changed
830  return 'changed-redirect-target';
831  }
832  } elseif ( $oldTarget ) {
833  // Changing an existing redirect into a non-redirect
834  return 'removed-redirect';
835  }
836 
837  // New page created
838  if ( $flags & EDIT_NEW && $newContent ) {
839  if ( $newContent->getSize() === 0 ) {
840  // New blank page
841  return 'newblank';
842  } else {
843  return 'newpage';
844  }
845  }
846 
847  // Removing more than 90% of the page
848  if ( $oldContent && $newContent && $oldContent->getSize() > 10 * $newContent->getSize() ) {
849  return 'replace';
850  }
851 
852  // Content model changed
853  if ( $oldContent && $newContent && $oldContent->getModel() !== $newContent->getModel() ) {
854  return 'contentmodelchange';
855  }
856 
857  return null;
858  }
859 
871  public function getAutosummary(
872  Content $oldContent = null,
873  Content $newContent = null,
874  $flags = 0
875  ) {
876  $changeType = $this->getChangeType( $oldContent, $newContent, $flags );
877 
878  // There's no applicable auto-summary for our case, so our auto-summary is empty.
879  if ( !$changeType ) {
880  return '';
881  }
882 
883  // Decide what kind of auto-summary is needed.
884  switch ( $changeType ) {
885  case 'new-redirect':
886  $newTarget = $newContent->getRedirectTarget();
887  $truncatedtext = $newContent->getTextForSummary(
888  250
889  - strlen( wfMessage( 'autoredircomment' )->inContentLanguage()->text() )
890  - strlen( $newTarget->getFullText() )
891  );
892 
893  return wfMessage( 'autoredircomment', $newTarget->getFullText() )
894  ->plaintextParams( $truncatedtext )->inContentLanguage()->text();
895  case 'changed-redirect-target':
896  $oldTarget = $oldContent->getRedirectTarget();
897  $newTarget = $newContent->getRedirectTarget();
898 
899  $truncatedtext = $newContent->getTextForSummary(
900  250
901  - strlen( wfMessage( 'autosumm-changed-redirect-target' )
902  ->inContentLanguage()->text() )
903  - strlen( $oldTarget->getFullText() )
904  - strlen( $newTarget->getFullText() )
905  );
906 
907  return wfMessage( 'autosumm-changed-redirect-target',
908  $oldTarget->getFullText(),
909  $newTarget->getFullText() )
910  ->rawParams( $truncatedtext )->inContentLanguage()->text();
911  case 'removed-redirect':
912  $oldTarget = $oldContent->getRedirectTarget();
913  $truncatedtext = $newContent->getTextForSummary(
914  250
915  - strlen( wfMessage( 'autosumm-removed-redirect' )
916  ->inContentLanguage()->text() )
917  - strlen( $oldTarget->getFullText() ) );
918 
919  return wfMessage( 'autosumm-removed-redirect', $oldTarget->getFullText() )
920  ->rawParams( $truncatedtext )->inContentLanguage()->text();
921  case 'newpage':
922  // If they're making a new article, give its text, truncated, in the summary.
923  $truncatedtext = $newContent->getTextForSummary(
924  200 - strlen( wfMessage( 'autosumm-new' )->inContentLanguage()->text() ) );
925 
926  return wfMessage( 'autosumm-new' )->rawParams( $truncatedtext )
927  ->inContentLanguage()->text();
928  case 'blank':
929  return wfMessage( 'autosumm-blank' )->inContentLanguage()->text();
930  case 'replace':
931  $truncatedtext = $newContent->getTextForSummary(
932  200 - strlen( wfMessage( 'autosumm-replace' )->inContentLanguage()->text() ) );
933 
934  return wfMessage( 'autosumm-replace' )->rawParams( $truncatedtext )
935  ->inContentLanguage()->text();
936  case 'newblank':
937  return wfMessage( 'autosumm-newblank' )->inContentLanguage()->text();
938  default:
939  return '';
940  }
941  }
942 
954  public function getChangeTag(
955  Content $oldContent = null,
956  Content $newContent = null,
957  $flags = 0
958  ) {
959  $changeType = $this->getChangeType( $oldContent, $newContent, $flags );
960 
961  // There's no applicable tag for this change.
962  if ( !$changeType ) {
963  return null;
964  }
965 
966  // Core tags use the same keys as ones returned from $this->getChangeType()
967  // but prefixed with pseudo namespace 'mw-', so we add the prefix before checking
968  // if this type of change should be tagged
969  $tag = 'mw-' . $changeType;
970 
971  // Not all change types are tagged, so we check against the list of defined tags.
972  if ( in_array( $tag, ChangeTags::getSoftwareTags() ) ) {
973  return $tag;
974  }
975 
976  return null;
977  }
978 
994  public function getAutoDeleteReason( Title $title, &$hasHistory ) {
995  $dbr = wfGetDB( DB_REPLICA );
996 
997  // Get the last revision
998  $rev = Revision::newFromTitle( $title );
999 
1000  if ( is_null( $rev ) ) {
1001  return false;
1002  }
1003 
1004  // Get the article's contents
1005  $content = $rev->getContent();
1006  $blank = false;
1007 
1008  // If the page is blank, use the text from the previous revision,
1009  // which can only be blank if there's a move/import/protect dummy
1010  // revision involved
1011  if ( !$content || $content->isEmpty() ) {
1012  $prev = $rev->getPrevious();
1013 
1014  if ( $prev ) {
1015  $rev = $prev;
1016  $content = $rev->getContent();
1017  $blank = true;
1018  }
1019  }
1020 
1021  $this->checkModelID( $rev->getContentModel() );
1022 
1023  // Find out if there was only one contributor
1024  // Only scan the last 20 revisions
1026  $res = $dbr->select(
1027  $revQuery['tables'],
1028  [ 'rev_user_text' => $revQuery['fields']['rev_user_text'] ],
1029  [
1030  'rev_page' => $title->getArticleID(),
1031  $dbr->bitAnd( 'rev_deleted', RevisionRecord::DELETED_USER ) . ' = 0'
1032  ],
1033  __METHOD__,
1034  [ 'LIMIT' => 20 ],
1035  $revQuery['joins']
1036  );
1037 
1038  if ( $res === false ) {
1039  // This page has no revisions, which is very weird
1040  return false;
1041  }
1042 
1043  $hasHistory = ( $res->numRows() > 1 );
1044  $row = $dbr->fetchObject( $res );
1045 
1046  if ( $row ) { // $row is false if the only contributor is hidden
1047  $onlyAuthor = $row->rev_user_text;
1048  // Try to find a second contributor
1049  foreach ( $res as $row ) {
1050  if ( $row->rev_user_text != $onlyAuthor ) { // T24999
1051  $onlyAuthor = false;
1052  break;
1053  }
1054  }
1055  } else {
1056  $onlyAuthor = false;
1057  }
1058 
1059  // Generate the summary with a '$1' placeholder
1060  if ( $blank ) {
1061  // The current revision is blank and the one before is also
1062  // blank. It's just not our lucky day
1063  $reason = wfMessage( 'exbeforeblank', '$1' )->inContentLanguage()->text();
1064  } else {
1065  if ( $onlyAuthor ) {
1066  $reason = wfMessage(
1067  'excontentauthor',
1068  '$1',
1069  $onlyAuthor
1070  )->inContentLanguage()->text();
1071  } else {
1072  $reason = wfMessage( 'excontent', '$1' )->inContentLanguage()->text();
1073  }
1074  }
1075 
1076  if ( $reason == '-' ) {
1077  // Allow these UI messages to be blanked out cleanly
1078  return '';
1079  }
1080 
1081  // Max content length = max comment length - length of the comment (excl. $1)
1082  $maxLength = CommentStore::COMMENT_CHARACTER_LIMIT - ( strlen( $reason ) - 2 );
1083  $text = $content ? $content->getTextForSummary( $maxLength ) : '';
1084 
1085  // Now replace the '$1' placeholder
1086  $reason = str_replace( '$1', $text, $reason );
1087 
1088  return $reason;
1089  }
1090 
1107  public function getUndoContent( $current, $undo, $undoafter, $undoIsLatest = false ) {
1108  Assert::parameterType( Revision::class . '|' . Content::class, $current, '$current' );
1109  if ( $current instanceof Content ) {
1110  Assert::parameter( $undo instanceof Content, '$undo',
1111  'Must be Content when $current is Content' );
1112  Assert::parameter( $undoafter instanceof Content, '$undoafter',
1113  'Must be Content when $current is Content' );
1114  $cur_content = $current;
1115  $undo_content = $undo;
1116  $undoafter_content = $undoafter;
1117  } else {
1118  Assert::parameter( $undo instanceof Revision, '$undo',
1119  'Must be Revision when $current is Revision' );
1120  Assert::parameter( $undoafter instanceof Revision, '$undoafter',
1121  'Must be Revision when $current is Revision' );
1122 
1123  $cur_content = $current->getContent();
1124 
1125  if ( empty( $cur_content ) ) {
1126  return false; // no page
1127  }
1128 
1129  $undo_content = $undo->getContent();
1130  $undoafter_content = $undoafter->getContent();
1131 
1132  if ( !$undo_content || !$undoafter_content ) {
1133  return false; // no content to undo
1134  }
1135 
1136  $undoIsLatest = $current->getId() === $undo->getId();
1137  }
1138 
1139  try {
1140  $this->checkModelID( $cur_content->getModel() );
1141  $this->checkModelID( $undo_content->getModel() );
1142  if ( !$undoIsLatest ) {
1143  // If we are undoing the most recent revision,
1144  // its ok to revert content model changes. However
1145  // if we are undoing a revision in the middle, then
1146  // doing that will be confusing.
1147  $this->checkModelID( $undoafter_content->getModel() );
1148  }
1149  } catch ( MWException $e ) {
1150  // If the revisions have different content models
1151  // just return false
1152  return false;
1153  }
1154 
1155  if ( $cur_content->equals( $undo_content ) ) {
1156  // No use doing a merge if it's just a straight revert.
1157  return $undoafter_content;
1158  }
1159 
1160  $undone_content = $this->merge3( $undo_content, $undoafter_content, $cur_content );
1161 
1162  return $undone_content;
1163  }
1164 
1181  public function makeParserOptions( $context ) {
1182  wfDeprecated( __METHOD__, '1.32' );
1184  }
1185 
1194  public function isParserCacheSupported() {
1195  return false;
1196  }
1197 
1207  public function supportsSections() {
1208  return false;
1209  }
1210 
1217  public function supportsCategories() {
1218  return true;
1219  }
1220 
1230  public function supportsRedirects() {
1231  return false;
1232  }
1233 
1239  public function supportsDirectEditing() {
1240  return false;
1241  }
1242 
1248  public function supportsDirectApiEditing() {
1249  return $this->supportsDirectEditing();
1250  }
1251 
1262  public function getFieldsForSearchIndex( SearchEngine $engine ) {
1263  $fields = [];
1264  $fields['category'] = $engine->makeSearchFieldMapping(
1265  'category',
1267  );
1268  $fields['category']->setFlag( SearchIndexField::FLAG_CASEFOLD );
1269 
1270  $fields['external_link'] = $engine->makeSearchFieldMapping(
1271  'external_link',
1273  );
1274 
1275  $fields['outgoing_link'] = $engine->makeSearchFieldMapping(
1276  'outgoing_link',
1278  );
1279 
1280  $fields['template'] = $engine->makeSearchFieldMapping(
1281  'template',
1283  );
1284  $fields['template']->setFlag( SearchIndexField::FLAG_CASEFOLD );
1285 
1286  $fields['content_model'] = $engine->makeSearchFieldMapping(
1287  'content_model',
1289  );
1290 
1291  return $fields;
1292  }
1293 
1303  protected function addSearchField( &$fields, SearchEngine $engine, $name, $type ) {
1304  $fields[$name] = $engine->makeSearchFieldMapping( $name, $type );
1305  return $fields;
1306  }
1307 
1319  public function getDataForSearchIndex(
1320  WikiPage $page,
1322  SearchEngine $engine
1323  ) {
1324  $fieldData = [];
1325  $content = $page->getContent();
1326 
1327  if ( $content ) {
1328  $searchDataExtractor = new ParserOutputSearchDataExtractor();
1329 
1330  $fieldData['category'] = $searchDataExtractor->getCategories( $output );
1331  $fieldData['external_link'] = $searchDataExtractor->getExternalLinks( $output );
1332  $fieldData['outgoing_link'] = $searchDataExtractor->getOutgoingLinks( $output );
1333  $fieldData['template'] = $searchDataExtractor->getTemplates( $output );
1334 
1335  $text = $content->getTextForSearchIndex();
1336 
1337  $fieldData['text'] = $text;
1338  $fieldData['source_text'] = $text;
1339  $fieldData['text_bytes'] = $content->getSize();
1340  $fieldData['content_model'] = $content->getModel();
1341  }
1342 
1343  Hooks::run( 'SearchDataForIndex', [ &$fieldData, $this, $page, $output, $engine ] );
1344  return $fieldData;
1345  }
1346 
1356  public function getParserOutputForIndexing( WikiPage $page, ParserCache $cache = null ) {
1357  // TODO: MCR: ContentHandler should be called per slot, not for the whole page.
1358  // See T190066.
1359  $parserOptions = $page->makeParserOptions( 'canonical' );
1360  if ( $cache ) {
1361  $parserOutput = $cache->get( $page, $parserOptions );
1362  }
1363 
1364  if ( empty( $parserOutput ) ) {
1365  $renderer = MediaWikiServices::getInstance()->getRevisionRenderer();
1366  $parserOutput =
1367  $renderer->getRenderedRevision(
1368  $page->getRevision()->getRevisionRecord(),
1369  $parserOptions
1370  )->getRevisionParserOutput();
1371  if ( $cache ) {
1372  $cache->save( $parserOutput, $page, $parserOptions );
1373  }
1374  }
1375  return $parserOutput;
1376  }
1377 
1408  public function getSecondaryDataUpdates(
1409  Title $title,
1410  Content $content,
1411  $role,
1412  SlotRenderingProvider $slotOutput
1413  ) {
1414  return [];
1415  }
1416 
1445  public function getDeletionUpdates( Title $title, $role ) {
1446  return [];
1447  }
1448 
1449 }
SearchIndexField\INDEX_TYPE_KEYWORD
const INDEX_TYPE_KEYWORD
KEYWORD fields are indexed without any processing, so are appropriate for e.g.
Definition: SearchIndexField.php:23
ContentHandler\getSecondaryDataUpdates
getSecondaryDataUpdates(Title $title, Content $content, $role, SlotRenderingProvider $slotOutput)
Returns a list of DeferrableUpdate objects for recording information about the given Content in some ...
Definition: ContentHandler.php:1408
ContentHandler\getSlotDiffRenderer
getSlotDiffRenderer(IContextSource $context)
Get an appropriate SlotDiffRenderer for this content model.
Definition: ContentHandler.php:620
ContentHandler
A content handler knows how do deal with a specific type of content on a wiki page.
Definition: ContentHandler.php:55
ContentHandler\getForModelID
static getForModelID( $modelId)
Returns the ContentHandler singleton for the given model ID.
Definition: ContentHandler.php:254
Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:46
ContentHandler\getAllContentFormats
static getAllContentFormats()
Definition: ContentHandler.php:339
DifferenceEngine\getEngine
static getEngine()
Process DiffEngine config and get a sane, usable engine.
Definition: DifferenceEngine.php:1296
ParserOutput
Definition: ParserOutput.php:25
ContentHandler\supportsDirectEditing
supportsDirectEditing()
Return true if this content model supports direct editing, such as via EditPage.
Definition: ContentHandler.php:1239
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:117
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:33
ContentHandler\makeParserOptions
makeParserOptions( $context)
Get parser options suitable for rendering and caching the article.
Definition: ContentHandler.php:1181
ContentHandler\getSlotDiffRendererInternal
getSlotDiffRendererInternal(IContextSource $context)
Return the SlotDiffRenderer appropriate for this content handler.
Definition: ContentHandler.php:645
ContentHandler\getActionOverrides
getActionOverrides()
Returns overrides for action handlers.
Definition: ContentHandler.php:572
ContentHandler\checkModelID
checkModelID( $model_id)
Definition: ContentHandler.php:484
ContentHandler\getAutoDeleteReason
getAutoDeleteReason(Title $title, &$hasHistory)
Auto-generates a deletion reason.
Definition: ContentHandler.php:994
ContentHandler\unserializeContent
unserializeContent( $blob, $format=null)
Unserializes a Content object of the type supported by this ContentHandler.
ContentHandler\getPageViewLanguage
getPageViewLanguage(Title $title, Content $content=null)
Get the language in which the content of this page is written when viewed by user.
Definition: ContentHandler.php:723
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:47
ContentHandler\$mSupportedFormats
string[] $mSupportedFormats
Definition: ContentHandler.php:364
WikiPage\makeParserOptions
makeParserOptions( $context)
Get parser options suitable for rendering the primary article wikitext.
Definition: WikiPage.php:1961
WikiPage\getRevision
getRevision()
Get the latest revision.
Definition: WikiPage.php:787
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1264
ContentHandler\getAutosummary
getAutosummary(Content $oldContent=null, Content $newContent=null, $flags=0)
Return an applicable auto-summary if one exists for the given edit.
Definition: ContentHandler.php:871
TextSlotDiffRenderer\ENGINE_EXTERNAL
const ENGINE_EXTERNAL
Use an external executable.
Definition: TextSlotDiffRenderer.php:46
ContentHandler\getDeletionUpdates
getDeletionUpdates(Title $title, $role)
Returns a list of DeferrableUpdate objects for removing information about content in some secondary d...
Definition: ContentHandler.php:1445
ContentHandler\getForTitle
static getForTitle(Title $title)
Returns the appropriate ContentHandler singleton for the given title.
Definition: ContentHandler.php:201
TextSlotDiffRenderer\ENGINE_WIKIDIFF2
const ENGINE_WIKIDIFF2
Use the wikidiff2 PHP module.
Definition: TextSlotDiffRenderer.php:43
SearchIndexField\FLAG_CASEFOLD
const FLAG_CASEFOLD
Generic field flags.
Definition: SearchIndexField.php:44
ContentHandler\getChangeTag
getChangeTag(Content $oldContent=null, Content $newContent=null, $flags=0)
Return an applicable tag if one exists for the given edit or return null.
Definition: ContentHandler.php:954
$res
$res
Definition: testCompression.php:52
ContentHandler\serializeContent
serializeContent(Content $content, $format=null)
Serializes a Content object of the type supported by this ContentHandler.
$revQuery
$revQuery
Definition: testCompression.php:51
wfDebugLog
wfDebugLog( $logGroup, $text, $dest='all', array $context=[])
Send a line to a supplementary debug log file, if configured, or main debug log if not.
Definition: GlobalFunctions.php:1007
ContentHandler\supportsSections
supportsSections()
Returns true if this content model supports sections.
Definition: ContentHandler.php:1207
Revision\SlotRenderingProvider
A lazy provider of ParserOutput objects for a revision's individual slots.
Definition: SlotRenderingProvider.php:17
ContentHandler\isSupportedFormat
isSupportedFormat( $format)
Returns true if $format is a serialization format supported by this ContentHandler,...
Definition: ContentHandler.php:533
$dbr
$dbr
Definition: testCompression.php:50
ContentHandler\createDifferenceEngine
createDifferenceEngine(IContextSource $context, $old=0, $new=0, $rcid=0, $refreshCache=false, $unhide=false)
Factory for creating an appropriate DifferenceEngine for this content model.
Definition: ContentHandler.php:603
ContentHandler\canBeUsedOn
canBeUsedOn(Title $title)
Determines whether the content type handled by this ContentHandler can be used for the main slot of t...
Definition: ContentHandler.php:756
Revision
Definition: Revision.php:40
MediaWiki\Search\ParserOutputSearchDataExtractor
Extracts data from ParserOutput for indexing in the search engine.
Definition: ParserOutputSearchDataExtractor.php:29
ContentHandler\importTransform
importTransform( $blob, $format=null)
Apply import transformation (per default, returns $blob unchanged).
Definition: ContentHandler.php:429
ContentHandler\supportsRedirects
supportsRedirects()
Returns true if this content model supports redirects.
Definition: ContentHandler.php:1230
Revision\newFromTitle
static newFromTitle(LinkTarget $linkTarget, $id=0, $flags=0)
Load either the current, or a specified, revision that's attached to a given link target.
Definition: Revision.php:138
$wgContentHandlerTextFallback
$wgContentHandlerTextFallback
How to react if a plain text version of a non-text Content object is requested using ContentHandler::...
Definition: DefaultSettings.php:8626
ContentHandler\getDefaultModelFor
static getDefaultModelFor(Title $title)
Returns the name of the default content model to be used for the page with the given title.
Definition: ContentHandler.php:186
Revision\getQueryInfo
static getQueryInfo( $options=[])
Return the tables, fields, and join conditions to be selected to create a new revision object.
Definition: Revision.php:315
MWException
MediaWiki exception.
Definition: MWException.php:26
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Throws a warning that $function is deprecated.
Definition: GlobalFunctions.php:1044
$wgContentHandlers
$wgContentHandlers
Plugins for page content model handling.
Definition: DefaultSettings.php:1063
MediaWiki\Logger\LoggerFactory
PSR-3 logger instance factory.
Definition: LoggerFactory.php:45
ContentHandler\getContentModels
static getContentModels()
Definition: ContentHandler.php:331
$blob
$blob
Definition: testCompression.php:65
ContentHandler\supportsDirectApiEditing
supportsDirectApiEditing()
Whether or not this content model supports direct editing via ApiEditPage.
Definition: ContentHandler.php:1248
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2575
$wgLang
$wgLang
Definition: Setup.php:881
wfGetLangObj
wfGetLangObj( $langcode=false)
Return a Language object from $langcode.
Definition: GlobalFunctions.php:1215
TextSlotDiffRenderer\ENGINE_PHP
const ENGINE_PHP
Use the PHP diff implementation (DiffEngine).
Definition: TextSlotDiffRenderer.php:40
ContentHandler\merge3
merge3(Content $oldContent, Content $myContent, Content $yourContent)
Attempts to merge differences between three versions.
Definition: ContentHandler.php:789
ChangeTags\getSoftwareTags
static getSoftwareTags( $all=false)
Loads defined core tags, checks for invalid types (if not array), and filters for supported and enabl...
Definition: ChangeTags.php:58
ContentHandler\makeEmptyContent
makeEmptyContent()
Creates an empty Content object of the type supported by this ContentHandler.
$title
$title
Definition: testCompression.php:34
ContentHandler\getDataForSearchIndex
getDataForSearchIndex(WikiPage $page, ParserOutput $output, SearchEngine $engine)
Return fields to be indexed by search engine as representation of this document.
Definition: ContentHandler.php:1319
$output
$output
Definition: SyntaxHighlight.php:335
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
MessageCache\singleton
static singleton()
Get the singleton instance of this class.
Definition: MessageCache.php:114
ContentHandler\makeContent
static makeContent( $text, Title $title=null, $modelId=null, $format=null)
Convenience function for creating a Content object from a given textual representation.
Definition: ContentHandler.php:135
ContentHandler\getPageLanguage
getPageLanguage(Title $title, Content $content=null)
Get the language in which the content of the given page is written.
Definition: ContentHandler.php:684
ContentHandler\isParserCacheSupported
isParserCacheSupported()
Returns true for content models that support caching using the ParserCache mechanism.
Definition: ContentHandler.php:1194
$content
$content
Definition: router.php:78
ContentHandler\getDefaultFormat
getDefaultFormat()
The format used for serialization/deserialization by default by this ContentHandler.
Definition: ContentHandler.php:516
ContentHandler\getChangeType
getChangeType(Content $oldContent=null, Content $newContent=null, $flags=0)
Return type of change if one exists for the given edit.
Definition: ContentHandler.php:804
ContentHandler\cleanupHandlersCache
static cleanupHandlersCache()
Clean up handlers cache.
Definition: ContentHandler.php:301
ContentHandler\getLocalizedName
static getLocalizedName( $name, Language $lang=null)
Returns the localized name for a given content model.
Definition: ContentHandler.php:318
ContentHandler\getDiffEngineClass
getDiffEngineClass()
Returns the name of the diff engine to use.
Definition: ContentHandler.php:771
ContentHandler\supportsCategories
supportsCategories()
Returns true if this content model supports categories.
Definition: ContentHandler.php:1217
ParserOptions\newCanonical
static newCanonical( $context=null, $userLang=null)
Creates a "canonical" ParserOptions object.
Definition: ParserOptions.php:1073
ContentHandler\$mModelID
string $mModelID
Definition: ContentHandler.php:359
TextContent
Content object implementation for representing flat text.
Definition: TextContent.php:37
SearchEngine
Contain a class for special pages.
Definition: SearchEngine.php:34
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:53
$context
$context
Definition: load.php:45
ContentHandler\exportTransform
exportTransform( $blob, $format=null)
Applies transformations on export (returns the blob unchanged per default).
Definition: ContentHandler.php:402
Content
Base interface for content objects.
Definition: Content.php:34
ContentHandler\getFieldsForSearchIndex
getFieldsForSearchIndex(SearchEngine $engine)
Get fields definition for search index.
Definition: ContentHandler.php:1262
CommentStore\COMMENT_CHARACTER_LIMIT
const COMMENT_CHARACTER_LIMIT
Maximum length of a comment in UTF-8 characters.
Definition: CommentStore.php:37
EDIT_NEW
const EDIT_NEW
Definition: Defines.php:132
SearchEngine\makeSearchFieldMapping
makeSearchFieldMapping( $name, $type)
Create a search field definition.
Definition: SearchEngine.php:744
Title
Represents a title within MediaWiki.
Definition: Title.php:42
ContentHandler\makeRedirectContent
makeRedirectContent(Title $destination, $text='')
Creates a new Content object that acts as a redirect to the given page, or null if redirects are not ...
Definition: ContentHandler.php:460
ContentHandler\checkFormat
checkFormat( $format)
Convenient for checking whether a format provided as a parameter is actually supported.
Definition: ContentHandler.php:548
ContentHandler\getContentText
static getContentText(Content $content=null)
Convenience function for getting flat text from a Content object.
Definition: ContentHandler.php:85
$cache
$cache
Definition: mcc.php:33
ContentHandler\$handlers
static array $handlers
A Cache of ContentHandler instances by model id.
Definition: ContentHandler.php:226
DifferenceEngineSlotDiffRenderer
B/C adapter for turning a DifferenceEngine into a SlotDiffRenderer.
Definition: DifferenceEngineSlotDiffRenderer.php:31
ParserCache
Definition: ParserCache.php:30
MWUnknownContentModelException
Exception thrown when an unregistered content model is requested.
Definition: MWUnknownContentModelException.php:10
ContentHandler\addSearchField
addSearchField(&$fields, SearchEngine $engine, $name, $type)
Add new field definition to array.
Definition: ContentHandler.php:1303
Language\factory
static factory( $code)
Get a cached or new language object for a given language code.
Definition: Language.php:217
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:68
ContentHandler\__construct
__construct( $modelId, $formats)
Constructor, initializing the ContentHandler instance with its model ID and a list of supported forma...
Definition: ContentHandler.php:375
StubObject\unstub
static unstub(&$obj)
Unstubs an object, if it is a stub object.
Definition: StubObject.php:93
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:200
ContentHandler\getUndoContent
getUndoContent( $current, $undo, $undoafter, $undoIsLatest=false)
Get the Content object that needs to be saved in order to undo all revisions between $undo and $undoa...
Definition: ContentHandler.php:1107
ContentHandler\getSupportedFormats
getSupportedFormats()
Returns a list of serialization formats supported by the serializeContent() and unserializeContent() ...
Definition: ContentHandler.php:501
WikiPage\getContent
getContent( $audience=RevisionRecord::FOR_PUBLIC, User $user=null)
Get the content of the current revision.
Definition: WikiPage.php:820
Language
Internationalisation code.
Definition: Language.php:37
ContentHandler\getModelID
getModelID()
Returns the model id that identifies the content model this ContentHandler can handle.
Definition: ContentHandler.php:472
ContentHandler\getForContent
static getForContent(Content $content)
Returns the appropriate ContentHandler singleton for the given Content object.
Definition: ContentHandler.php:217
ContentHandler\getParserOutputForIndexing
getParserOutputForIndexing(WikiPage $page, ParserCache $cache=null)
Produce page output suitable for indexing.
Definition: ContentHandler.php:1356
TextSlotDiffRenderer
Renders a slot diff by doing a text diff on the native representation.
Definition: TextSlotDiffRenderer.php:37
SearchIndexField\INDEX_TYPE_TEXT
const INDEX_TYPE_TEXT
TEXT fields are suitable for natural language and may be subject to analysis such as stemming.
Definition: SearchIndexField.php:18
$type
$type
Definition: testCompression.php:48