MediaWiki  master
ContentHandler.php
Go to the documentation of this file.
1 <?php
2 
31 use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
38 
60 abstract class ContentHandler {
61  use ProtectedHookAccessorTrait;
62 
95  public static function getContentText( Content $content = null ) {
97 
98  if ( $content === null ) {
99  return '';
100  }
101 
102  if ( $content instanceof TextContent ) {
103  return $content->getText();
104  }
105 
106  wfDebugLog( 'ContentHandler', 'Accessing ' . $content->getModel() . ' content as text!' );
107 
108  if ( $wgContentHandlerTextFallback == 'fail' ) {
109  throw new MWException(
110  "Attempt to get text from Content with model " .
111  $content->getModel()
112  );
113  }
114 
115  if ( $wgContentHandlerTextFallback == 'serialize' ) {
116  return $content->serialize();
117  }
118 
119  return null;
120  }
121 
146  public static function makeContent( $text, Title $title = null,
147  $modelId = null, $format = null ) {
148  if ( $modelId === null ) {
149  if ( $title === null ) {
150  throw new MWException( "Must provide a Title object or a content model ID." );
151  }
152 
153  $modelId = $title->getContentModel();
154  }
155 
156  return MediaWikiServices::getInstance()
157  ->getContentHandlerFactory()
158  ->getContentHandler( $modelId )
159  ->unserializeContent( $text, $format );
160  }
161 
198  public static function getDefaultModelFor( Title $title ) {
199  $slotRoleregistry = MediaWikiServices::getInstance()->getSlotRoleRegistry();
200  $mainSlotHandler = $slotRoleregistry->getRoleHandler( 'main' );
201  return $mainSlotHandler->getDefaultModel( $title );
202  }
203 
218  public static function getForTitle( Title $title ) {
219  wfDeprecated( __METHOD__, '1.35' );
220  return MediaWikiServices::getInstance()
221  ->getContentHandlerFactory()
222  ->getContentHandler( $title->getContentModel() );
223  }
224 
240  public static function getForContent( Content $content ) {
241  return MediaWikiServices::getInstance()
242  ->getContentHandlerFactory()
243  ->getContentHandler( $content->getModel() );
244  }
245 
275  public static function getForModelID( $modelId ) {
276  return MediaWikiServices::getInstance()
277  ->getContentHandlerFactory()
278  ->getContentHandler( $modelId );
279  }
280 
288  public static function cleanupHandlersCache() {
289  wfDeprecated( __METHOD__, '1.35' );
290  // No-op: no longer needed, since the instance cache is in the
291  // ContentHandlerFactory service, and services get reset between tests
292  }
293 
307  public static function getLocalizedName( $name, Language $lang = null ) {
308  // Messages: content-model-wikitext, content-model-text,
309  // content-model-javascript, content-model-css
310  $key = "content-model-$name";
311 
312  $msg = wfMessage( $key );
313  if ( $lang ) {
314  $msg->inLanguage( $lang );
315  }
316 
317  return $msg->exists() ? $msg->plain() : $name;
318  }
319 
328  public static function getContentModels() {
329  return MediaWikiServices::getInstance()->getContentHandlerFactory()->getContentModels();
330  }
331 
340  public static function getAllContentFormats() {
341  return MediaWikiServices::getInstance()->getContentHandlerFactory()->getAllContentFormats();
342  }
343 
344  // ------------------------------------------------------------------------
345 
349  protected $mModelID;
350 
355 
367  public function __construct( $modelId, $formats ) {
368  $this->mModelID = $modelId;
369  $this->mSupportedFormats = $formats;
370  }
371 
384  abstract public function serializeContent( Content $content, $format = null );
385 
398  public function exportTransform( $blob, $format = null ) {
399  return $blob;
400  }
401 
414  abstract public function unserializeContent( $blob, $format = null );
415 
428  public function importTransform( $blob, $format = null ) {
429  return $blob;
430  }
431 
441  abstract public function makeEmptyContent();
442 
461  public function makeRedirectContent( Title $destination, $text = '' ) {
462  return null;
463  }
464 
473  public function getModelID() {
474  return $this->mModelID;
475  }
476 
485  protected function checkModelID( $model_id ) {
486  if ( $model_id !== $this->mModelID ) {
487  throw new MWException( "Bad content model: " .
488  "expected {$this->mModelID} " .
489  "but got $model_id." );
490  }
491  }
492 
503  public function getSupportedFormats() {
505  }
506 
519  public function getDefaultFormat() {
520  return $this->mSupportedFormats[0];
521  }
522 
537  public function isSupportedFormat( $format ) {
538  if ( !$format ) {
539  return true; // this means "use the default"
540  }
541 
542  return in_array( $format, $this->mSupportedFormats );
543  }
544 
552  protected function checkFormat( $format ) {
553  if ( !$this->isSupportedFormat( $format ) ) {
554  throw new MWException(
555  "Format $format is not supported for content model "
556  . $this->getModelID()
557  );
558  }
559  }
560 
577  public function getActionOverrides() {
578  return [];
579  }
580 
608  public function createDifferenceEngine( IContextSource $context, $old = 0, $new = 0,
609  $rcid = 0, // FIXME: Deprecated, no longer used
610  $refreshCache = false, $unhide = false
611  ) {
612  $diffEngineClass = $this->getDiffEngineClass();
613  $differenceEngine = new $diffEngineClass( $context, $old, $new, $rcid, $refreshCache, $unhide );
614  $this->getHookRunner()->onGetDifferenceEngine(
615  $context, $old, $new, $refreshCache, $unhide, $differenceEngine );
616  return $differenceEngine;
617  }
618 
629  final public function getSlotDiffRenderer( IContextSource $context, array $options = [] ) {
630  $slotDiffRenderer = $this->getSlotDiffRendererWithOptions( $context, $options );
631  if ( get_class( $slotDiffRenderer ) === TextSlotDiffRenderer::class ) {
632  // To keep B/C, when SlotDiffRenderer is not overridden for a given content type
633  // but DifferenceEngine is, use that instead.
634  $differenceEngine = $this->createDifferenceEngine( $context );
635  if ( get_class( $differenceEngine ) !== DifferenceEngine::class ) {
636  // TODO turn this into a deprecation warning in a later release
637  LoggerFactory::getInstance( 'diff' )->info(
638  'Falling back to DifferenceEngineSlotDiffRenderer', [
639  'modelID' => $this->getModelID(),
640  'DifferenceEngine' => get_class( $differenceEngine ),
641  ] );
642  $slotDiffRenderer = new DifferenceEngineSlotDiffRenderer( $differenceEngine );
643  }
644  }
645  $this->getHookRunner()->onGetSlotDiffRenderer( $this, $slotDiffRenderer, $context );
646  return $slotDiffRenderer;
647  }
648 
655  protected function getSlotDiffRendererInternal( IContextSource $context ) {
656  return null;
657  }
658 
668  protected function getSlotDiffRendererWithOptions( IContextSource $context, $options = [] ) {
669  $internalRenderer = $this->getSlotDiffRendererInternal( $context );
670  // `getSlotDiffRendererInternal` has been overriden by a class using the deprecated method.
671  // Options will not work so exit early!
672  if ( $internalRenderer !== null ) {
673  return $internalRenderer;
674  }
675 
676  $contentLanguage = MediaWikiServices::getInstance()->getContentLanguage();
677  $statsdDataFactory = MediaWikiServices::getInstance()->getStatsdDataFactory();
678  $slotDiffRenderer = new TextSlotDiffRenderer();
679  $slotDiffRenderer->setStatsdDataFactory( $statsdDataFactory );
680  // XXX using the page language would be better, but it's unclear how that should be injected
681  $slotDiffRenderer->setLanguage( $contentLanguage );
682 
683  $inline = ( $options['diff-type'] ?? '' ) === 'inline';
684  $engine = DifferenceEngine::getEngine();
685 
686  if ( $engine === 'php' ) {
687  $slotDiffRenderer->setEngine( TextSlotDiffRenderer::ENGINE_PHP );
688  } elseif ( $engine === 'wikidiff2' ) {
689  if ( $inline ) {
690  $slotDiffRenderer->setEngine( TextSlotDiffRenderer::ENGINE_WIKIDIFF2_INLINE );
691  } else {
692  $slotDiffRenderer->setEngine( TextSlotDiffRenderer::ENGINE_WIKIDIFF2 );
693  }
694  } else {
695  $slotDiffRenderer->setEngine( TextSlotDiffRenderer::ENGINE_EXTERNAL, $engine );
696  }
697 
698  return $slotDiffRenderer;
699  }
700 
721  public function getPageLanguage( Title $title, Content $content = null ) {
722  global $wgLang;
723  $services = MediaWikiServices::getInstance();
724  $pageLang = $services->getContentLanguage();
725 
726  if ( $title->inNamespace( NS_MEDIAWIKI ) ) {
727  // Parse mediawiki messages with correct target language
728  list( /* $unused */, $lang ) = $services->getMessageCache()->figureMessage( $title->getText() );
729  $pageLang = $services->getLanguageFactory()->getLanguage( $lang );
730  }
731 
732  // Simplify hook handlers by only passing objects of one type, in case nothing
733  // else has unstubbed the StubUserLang object by now.
735 
736  $this->getHookRunner()->onPageContentLanguage( $title, $pageLang, $wgLang );
737 
738  return wfGetLangObj( $pageLang );
739  }
740 
762  public function getPageViewLanguage( Title $title, Content $content = null ) {
763  $pageLang = $this->getPageLanguage( $title, $content );
764 
765  if ( $title->getNamespace() !== NS_MEDIAWIKI ) {
766  // If the user chooses a variant, the content is actually
767  // in a language whose code is the variant code.
768  $variant = $this->getLanguageConverter( $pageLang )->getPreferredVariant();
769  if ( $pageLang->getCode() !== $variant ) {
770  $pageLang = MediaWikiServices::getInstance()->getLanguageFactory()
771  ->getLanguage( $variant );
772  }
773  }
774 
775  return $pageLang;
776  }
777 
798  public function canBeUsedOn( Title $title ) {
799  $ok = true;
800 
801  $this->getHookRunner()->onContentModelCanBeUsedOn( $this->getModelID(), $title, $ok );
802 
803  return $ok;
804  }
805 
814  protected function getDiffEngineClass() {
815  return DifferenceEngine::class;
816  }
817 
833  public function merge3( Content $oldContent, Content $myContent, Content $yourContent ) {
834  return false;
835  }
836 
842  private function getLanguageConverter( $language ): ILanguageConverter {
843  return MediaWikiServices::getInstance()->getLanguageConverterFactory()
844  ->getLanguageConverter( $language );
845  }
846 
859  private function getChangeType(
860  Content $oldContent = null,
861  Content $newContent = null,
862  $flags = 0
863  ) {
864  $oldTarget = $oldContent !== null ? $oldContent->getRedirectTarget() : null;
865  $newTarget = $newContent !== null ? $newContent->getRedirectTarget() : null;
866 
867  // We check for the type of change in the given edit, and return string key accordingly
868 
869  // Blanking of a page
870  if ( $oldContent && $oldContent->getSize() > 0 &&
871  $newContent && $newContent->getSize() === 0
872  ) {
873  return 'blank';
874  }
875 
876  // Redirects
877  if ( $newTarget ) {
878  if ( !$oldTarget ) {
879  // New redirect page (by creating new page or by changing content page)
880  return 'new-redirect';
881  } elseif ( !$newTarget->equals( $oldTarget ) ||
882  $oldTarget->getFragment() !== $newTarget->getFragment()
883  ) {
884  // Redirect target changed
885  return 'changed-redirect-target';
886  }
887  } elseif ( $oldTarget ) {
888  // Changing an existing redirect into a non-redirect
889  return 'removed-redirect';
890  }
891 
892  // New page created
893  if ( $flags & EDIT_NEW && $newContent ) {
894  if ( $newContent->getSize() === 0 ) {
895  // New blank page
896  return 'newblank';
897  } else {
898  return 'newpage';
899  }
900  }
901 
902  // Removing more than 90% of the page
903  if ( $oldContent && $newContent && $oldContent->getSize() > 10 * $newContent->getSize() ) {
904  return 'replace';
905  }
906 
907  // Content model changed
908  if ( $oldContent && $newContent && $oldContent->getModel() !== $newContent->getModel() ) {
909  return 'contentmodelchange';
910  }
911 
912  return null;
913  }
914 
927  public function getAutosummary(
928  Content $oldContent = null,
929  Content $newContent = null,
930  $flags = 0
931  ) {
932  $changeType = $this->getChangeType( $oldContent, $newContent, $flags );
933 
934  // There's no applicable auto-summary for our case, so our auto-summary is empty.
935  if ( !$changeType ) {
936  return '';
937  }
938 
939  // Set the maximum auto-summary length to the general maximum summary length
940  // T221617
942 
943  // Decide what kind of auto-summary is needed.
944  switch ( $changeType ) {
945  case 'new-redirect':
946  $newTarget = $newContent->getRedirectTarget();
947  $truncatedtext = $newContent->getTextForSummary(
948  $summaryLimit
949  - strlen( wfMessage( 'autoredircomment' )->inContentLanguage()->text() )
950  - strlen( $newTarget->getFullText() )
951  );
952 
953  return wfMessage( 'autoredircomment', $newTarget->getFullText() )
954  ->plaintextParams( $truncatedtext )->inContentLanguage()->text();
955  case 'changed-redirect-target':
956  $oldTarget = $oldContent->getRedirectTarget();
957  $newTarget = $newContent->getRedirectTarget();
958 
959  $truncatedtext = $newContent->getTextForSummary(
960  $summaryLimit
961  - strlen( wfMessage( 'autosumm-changed-redirect-target' )
962  ->inContentLanguage()->text() )
963  - strlen( $oldTarget->getFullText() )
964  - strlen( $newTarget->getFullText() )
965  );
966 
967  return wfMessage( 'autosumm-changed-redirect-target',
968  $oldTarget->getFullText(),
969  $newTarget->getFullText() )
970  ->rawParams( $truncatedtext )->inContentLanguage()->text();
971  case 'removed-redirect':
972  $oldTarget = $oldContent->getRedirectTarget();
973  $truncatedtext = $newContent->getTextForSummary(
974  $summaryLimit
975  - strlen( wfMessage( 'autosumm-removed-redirect' )
976  ->inContentLanguage()->text() )
977  - strlen( $oldTarget->getFullText() ) );
978 
979  return wfMessage( 'autosumm-removed-redirect', $oldTarget->getFullText() )
980  ->rawParams( $truncatedtext )->inContentLanguage()->text();
981  case 'newpage':
982  // If they're making a new article, give its text, truncated, in the summary.
983  $truncatedtext = $newContent->getTextForSummary(
984  $summaryLimit - strlen( wfMessage( 'autosumm-new' )->inContentLanguage()->text() ) );
985 
986  return wfMessage( 'autosumm-new' )->rawParams( $truncatedtext )
987  ->inContentLanguage()->text();
988  case 'blank':
989  return wfMessage( 'autosumm-blank' )->inContentLanguage()->text();
990  case 'replace':
991  $truncatedtext = $newContent->getTextForSummary(
992  $summaryLimit - strlen( wfMessage( 'autosumm-replace' )->inContentLanguage()->text() ) );
993 
994  return wfMessage( 'autosumm-replace' )->rawParams( $truncatedtext )
995  ->inContentLanguage()->text();
996  case 'newblank':
997  return wfMessage( 'autosumm-newblank' )->inContentLanguage()->text();
998  default:
999  return '';
1000  }
1001  }
1002 
1015  public function getChangeTag(
1016  Content $oldContent = null,
1017  Content $newContent = null,
1018  $flags = 0
1019  ) {
1020  $changeType = $this->getChangeType( $oldContent, $newContent, $flags );
1021 
1022  // There's no applicable tag for this change.
1023  if ( !$changeType ) {
1024  return null;
1025  }
1026 
1027  // Core tags use the same keys as ones returned from $this->getChangeType()
1028  // but prefixed with pseudo namespace 'mw-', so we add the prefix before checking
1029  // if this type of change should be tagged
1030  $tag = 'mw-' . $changeType;
1031 
1032  // Not all change types are tagged, so we check against the list of defined tags.
1033  if ( in_array( $tag, ChangeTags::getSoftwareTags() ) ) {
1034  return $tag;
1035  }
1036 
1037  return null;
1038  }
1039 
1055  public function getAutoDeleteReason( Title $title, &$hasHistory ) {
1056  $dbr = wfGetDB( DB_REPLICA );
1057  $revStore = MediaWikiServices::getInstance()->getRevisionStore();
1058 
1059  // Get the last revision
1060  $revRecord = $revStore->getRevisionByTitle( $title );
1061 
1062  if ( $revRecord === null ) {
1063  return false;
1064  }
1065 
1066  // Get the article's contents
1067  $content = $revRecord->getContent( SlotRecord::MAIN );
1068  $blank = false;
1069 
1070  // If the page is blank, use the text from the previous revision,
1071  // which can only be blank if there's a move/import/protect dummy
1072  // revision involved
1073  if ( !$content || $content->isEmpty() ) {
1074  $prev = $revStore->getPreviousRevision( $revRecord );
1075 
1076  if ( $prev ) {
1077  $revRecord = $prev;
1078  $content = $prev->getContent( SlotRecord::MAIN );
1079  $blank = true;
1080  }
1081  }
1082 
1083  $this->checkModelID( $revRecord->getSlot( SlotRecord::MAIN )->getModel() );
1084 
1085  // Find out if there was only one contributor
1086  // Only scan the last 20 revisions
1087  $revQuery = $revStore->getQueryInfo();
1088  $res = $dbr->select(
1089  $revQuery['tables'],
1090  [ 'rev_user_text' => $revQuery['fields']['rev_user_text'] ],
1091  [
1092  'rev_page' => $title->getArticleID(),
1093  $dbr->bitAnd( 'rev_deleted', RevisionRecord::DELETED_USER ) . ' = 0'
1094  ],
1095  __METHOD__,
1096  [ 'LIMIT' => 20 ],
1097  $revQuery['joins']
1098  );
1099 
1100  if ( $res === false ) {
1101  // This page has no revisions, which is very weird
1102  return false;
1103  }
1104 
1105  $hasHistory = ( $res->numRows() > 1 );
1106  $row = $dbr->fetchObject( $res );
1107 
1108  if ( $row ) { // $row is false if the only contributor is hidden
1109  $onlyAuthor = $row->rev_user_text;
1110  // Try to find a second contributor
1111  foreach ( $res as $row ) {
1112  if ( $row->rev_user_text != $onlyAuthor ) { // T24999
1113  $onlyAuthor = false;
1114  break;
1115  }
1116  }
1117  } else {
1118  $onlyAuthor = false;
1119  }
1120 
1121  // Generate the summary with a '$1' placeholder
1122  if ( $blank ) {
1123  // The current revision is blank and the one before is also
1124  // blank. It's just not our lucky day
1125  $reason = wfMessage( 'exbeforeblank', '$1' )->inContentLanguage()->text();
1126  } else {
1127  if ( $onlyAuthor ) {
1128  $reason = wfMessage(
1129  'excontentauthor',
1130  '$1',
1131  $onlyAuthor
1132  )->inContentLanguage()->text();
1133  } else {
1134  $reason = wfMessage( 'excontent', '$1' )->inContentLanguage()->text();
1135  }
1136  }
1137 
1138  if ( $reason == '-' ) {
1139  // Allow these UI messages to be blanked out cleanly
1140  return '';
1141  }
1142 
1143  // Max content length = max comment length - length of the comment (excl. $1)
1144  $maxLength = CommentStore::COMMENT_CHARACTER_LIMIT - ( strlen( $reason ) - 2 );
1145  $text = $content ? $content->getTextForSummary( $maxLength ) : '';
1146 
1147  // Now replace the '$1' placeholder
1148  $reason = str_replace( '$1', $text, $reason );
1149 
1150  return $reason;
1151  }
1152 
1170  public function getUndoContent(
1171  Content $currentContent,
1172  Content $undoContent,
1173  Content $undoAfterContent,
1174  $undoIsLatest = false
1175  ) {
1176  try {
1177  $this->checkModelID( $currentContent->getModel() );
1178  $this->checkModelID( $undoContent->getModel() );
1179  if ( !$undoIsLatest ) {
1180  // If we are undoing the most recent revision,
1181  // its ok to revert content model changes. However
1182  // if we are undoing a revision in the middle, then
1183  // doing that will be confusing.
1184  $this->checkModelID( $undoAfterContent->getModel() );
1185  }
1186  } catch ( MWException $e ) {
1187  // If the revisions have different content models
1188  // just return false
1189  return false;
1190  }
1191 
1192  if ( $currentContent->equals( $undoContent ) ) {
1193  // No use doing a merge if it's just a straight revert.
1194  return $undoAfterContent;
1195  }
1196 
1197  $undone_content = $this->merge3( $undoContent, $undoAfterContent, $currentContent );
1198 
1199  return $undone_content;
1200  }
1201 
1211  public function isParserCacheSupported() {
1212  return false;
1213  }
1214 
1226  public function supportsSections() {
1227  return false;
1228  }
1229 
1238  public function supportsCategories() {
1239  return true;
1240  }
1241 
1253  public function supportsRedirects() {
1254  return false;
1255  }
1256 
1264  public function supportsDirectEditing() {
1265  return false;
1266  }
1267 
1276  public function generateHTMLOnEdit(): bool {
1277  return true;
1278  }
1279 
1287  public function supportsDirectApiEditing() {
1288  return $this->supportsDirectEditing();
1289  }
1290 
1303  public function getFieldsForSearchIndex( SearchEngine $engine ) {
1304  $fields = [];
1305  $fields['category'] = $engine->makeSearchFieldMapping(
1306  'category',
1308  );
1309  $fields['category']->setFlag( SearchIndexField::FLAG_CASEFOLD );
1310 
1311  $fields['external_link'] = $engine->makeSearchFieldMapping(
1312  'external_link',
1314  );
1315 
1316  $fields['outgoing_link'] = $engine->makeSearchFieldMapping(
1317  'outgoing_link',
1319  );
1320 
1321  $fields['template'] = $engine->makeSearchFieldMapping(
1322  'template',
1324  );
1325  $fields['template']->setFlag( SearchIndexField::FLAG_CASEFOLD );
1326 
1327  $fields['content_model'] = $engine->makeSearchFieldMapping(
1328  'content_model',
1330  );
1331 
1332  return $fields;
1333  }
1334 
1344  protected function addSearchField( &$fields, SearchEngine $engine, $name, $type ) {
1345  $fields[$name] = $engine->makeSearchFieldMapping( $name, $type );
1346  return $fields;
1347  }
1348 
1372  public function getDataForSearchIndex(
1373  WikiPage $page,
1374  ParserOutput $output,
1375  SearchEngine $engine
1376  ) {
1377  $fieldData = [];
1378  $content = $page->getContent();
1379 
1380  if ( $content ) {
1381  $searchDataExtractor = new ParserOutputSearchDataExtractor();
1382 
1383  $fieldData['category'] = $searchDataExtractor->getCategories( $output );
1384  $fieldData['external_link'] = $searchDataExtractor->getExternalLinks( $output );
1385  $fieldData['outgoing_link'] = $searchDataExtractor->getOutgoingLinks( $output );
1386  $fieldData['template'] = $searchDataExtractor->getTemplates( $output );
1387 
1388  $text = $content->getTextForSearchIndex();
1389 
1390  $fieldData['text'] = $text;
1391  $fieldData['source_text'] = $text;
1392  $fieldData['text_bytes'] = $content->getSize();
1393  $fieldData['content_model'] = $content->getModel();
1394  }
1395 
1396  $this->getHookRunner()->onSearchDataForIndex( $fieldData, $this, $page, $output, $engine );
1397  return $fieldData;
1398  }
1399 
1417  public function getParserOutputForIndexing( WikiPage $page, ParserCache $cache = null ) {
1418  // TODO: MCR: ContentHandler should be called per slot, not for the whole page.
1419  // See T190066.
1420  $parserOptions = $page->makeParserOptions( 'canonical' );
1421  if ( $cache ) {
1422  $parserOutput = $cache->get( $page, $parserOptions );
1423  }
1424 
1425  if ( empty( $parserOutput ) ) {
1426  $renderer = MediaWikiServices::getInstance()->getRevisionRenderer();
1427  $revisionRecord = $this->latestRevision( $page );
1428  $parserOutput =
1429  $renderer->getRenderedRevision(
1430  $revisionRecord,
1431  $parserOptions
1432  )->getRevisionParserOutput( [
1433  // subclasses may want to add the following here:
1434  // 'generate-html' => false,
1435  ] );
1436  if ( $cache ) {
1437  $cache->save( $parserOutput, $page, $parserOptions );
1438  }
1439  }
1440  return $parserOutput;
1441  }
1442 
1451  protected function latestRevision( WikiPage $page ): RevisionRecord {
1452  $revRecord = $page->getRevisionRecord();
1453  if ( $revRecord == null ) {
1454  // If the content represents a brand new page it's possible
1455  // we need to fetch it from the primary.
1456  $page->loadPageData( WikiPage::READ_LATEST );
1457  $revRecord = $page->getRevisionRecord();
1458  if ( $revRecord == null ) {
1459  $text = $page->getTitle()->getPrefixedText();
1460  throw new MWException(
1461  "No revision could be loaded for page: $text" );
1462  }
1463  }
1464 
1465  return $revRecord;
1466  }
1467 
1500  public function getSecondaryDataUpdates(
1501  Title $title,
1502  Content $content,
1503  $role,
1504  SlotRenderingProvider $slotOutput
1505  ) {
1506  return [];
1507  }
1508 
1539  public function getDeletionUpdates( Title $title, $role ) {
1540  return [];
1541  }
1542 
1557  public function preSaveTransform(
1558  Content $content,
1559  PreSaveTransformParams $pstParams
1560  ): Content {
1561  $shouldCallDeprecatedMethod = $this->shouldCallDeprecatedContentTransformMethod(
1562  $content,
1563  $pstParams
1564  );
1565 
1566  if ( !$shouldCallDeprecatedMethod ) {
1567  return $content;
1568  }
1569 
1570  return $this->callDeprecatedContentPST(
1571  $content,
1572  $pstParams
1573  );
1574  }
1575 
1590  public function preloadTransform(
1591  Content $content,
1592  PreloadTransformParams $pltParams
1593  ): Content {
1594  $shouldCallDeprecatedMethod = $this->shouldCallDeprecatedContentTransformMethod(
1595  $content,
1596  $pltParams
1597  );
1598 
1599  if ( !$shouldCallDeprecatedMethod ) {
1600  return $content;
1601  }
1602 
1603  return $this->callDeprecatedContentPLT(
1604  $content,
1605  $pltParams
1606  );
1607  }
1608 
1618  Content $content,
1619  $params
1620  ): bool {
1621  $method = $params instanceof PreSaveTransformParams
1622  ? "preSaveTransform"
1623  : "preloadTransform";
1625  $content,
1626  AbstractContent::class,
1627  $method,
1628  '1.37'
1629  );
1630  }
1631 
1640  protected function callDeprecatedContentPST(
1641  Content $content,
1642  PreSaveTransformParams $params
1643  ): Content {
1644  $services = MediaWikiServices::getInstance();
1645  $legacyUser = $services->getUserFactory()->newFromUserIdentity( $params->getUser() );
1646  $legacyTitle = $services->getTitleFactory()->castFromPageReference( $params->getPage() );
1647 
1648  return $content->preSaveTransform(
1649  $legacyTitle,
1650  $legacyUser,
1651  $params->getParserOptions()
1652  );
1653  }
1654 
1663  protected function callDeprecatedContentPLT(
1664  Content $content,
1665  PreloadTransformParams $params
1666  ): Content {
1667  $services = MediaWikiServices::getInstance();
1668  $legacyTitle = $services->getTitleFactory()->castFromPageReference( $params->getPage() );
1669  return $content->preloadTransform(
1670  $legacyTitle,
1671  $params->getParserOptions(),
1672  $params->getParams()
1673  );
1674  }
1675 }
SearchIndexField\INDEX_TYPE_KEYWORD
const INDEX_TYPE_KEYWORD
KEYWORD fields are indexed without any processing, so are appropriate for e.g.
Definition: SearchIndexField.php:24
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:1500
ContentHandler
A content handler knows how do deal with a specific type of content on a wiki page.
Definition: ContentHandler.php:60
ContentHandler\getForModelID
static getForModelID( $modelId)
Returns the ContentHandler singleton for the given model ID.
Definition: ContentHandler.php:275
ContentHandler\callDeprecatedContentPLT
callDeprecatedContentPLT(Content $content, PreloadTransformParams $params)
If provided content overrides deprecated Content::preloadTransform, call it and return.
Definition: ContentHandler.php:1663
MediaWiki\Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:47
ContentHandler\getAllContentFormats
static getAllContentFormats()
Definition: ContentHandler.php:340
WikiPage\loadPageData
loadPageData( $from='fromdb')
Load the object from a given source by title.
Definition: WikiPage.php:466
DifferenceEngine\getEngine
static getEngine()
Process DiffEngine config and get a sane, usable engine.
Definition: DifferenceEngine.php:1441
WikiPage\getRevisionRecord
getRevisionRecord()
Get the latest revision.
Definition: WikiPage.php:816
ParserOutput
Definition: ParserOutput.php:31
NS_MEDIAWIKI
const NS_MEDIAWIKI
Definition: Defines.php:72
ContentHandler\supportsDirectEditing
supportsDirectEditing()
Return true if this content model supports direct editing, such as via EditPage.
Definition: ContentHandler.php:1264
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:193
$lang
if(!isset( $args[0])) $lang
Definition: testCompression.php:37
ContentHandler\getSlotDiffRendererInternal
getSlotDiffRendererInternal(IContextSource $context)
Return the SlotDiffRenderer appropriate for this content handler.
Definition: ContentHandler.php:655
ContentHandler\preloadTransform
preloadTransform(Content $content, PreloadTransformParams $pltParams)
Returns a $content object with preload transformations applied (or the same object if no transformati...
Definition: ContentHandler.php:1590
ContentHandler\getActionOverrides
getActionOverrides()
Returns overrides for action handlers.
Definition: ContentHandler.php:577
ContentHandler\checkModelID
checkModelID( $model_id)
Definition: ContentHandler.php:485
ContentHandler\getAutoDeleteReason
getAutoDeleteReason(Title $title, &$hasHistory)
Auto-generates a deletion reason.
Definition: ContentHandler.php:1055
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:762
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:60
ContentHandler\$mSupportedFormats
string[] $mSupportedFormats
Definition: ContentHandler.php:354
WikiPage\makeParserOptions
makeParserOptions( $context)
Get parser options suitable for rendering the primary article wikitext.
Definition: WikiPage.php:1996
wfMessage
wfMessage( $key,... $params)
This is the function for getting translated interface messages.
Definition: GlobalFunctions.php:1182
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:927
TextSlotDiffRenderer\ENGINE_EXTERNAL
const ENGINE_EXTERNAL
Use an external executable.
Definition: TextSlotDiffRenderer.php:50
ContentHandler\getDeletionUpdates
getDeletionUpdates(Title $title, $role)
Returns a list of DeferrableUpdate objects for removing information about content in some secondary d...
Definition: ContentHandler.php:1539
ContentHandler\getForTitle
static getForTitle(Title $title)
Returns the appropriate ContentHandler singleton for the given title.
Definition: ContentHandler.php:218
MediaWiki\Content\Transform\PreloadTransformParams
Definition: PreloadTransformParams.php:11
TextSlotDiffRenderer\ENGINE_WIKIDIFF2
const ENGINE_WIKIDIFF2
Use the wikidiff2 PHP module.
Definition: TextSlotDiffRenderer.php:44
SearchIndexField\FLAG_CASEFOLD
const FLAG_CASEFOLD
Generic field flags.
Definition: SearchIndexField.php:45
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:1015
MediaWiki\Content\Transform\PreSaveTransformParams\getUser
getUser()
$res
$res
Definition: testCompression.php:57
ContentHandler\serializeContent
serializeContent(Content $content, $format=null)
Serializes a Content object of the type supported by this ContentHandler.
ContentHandler\getUndoContent
getUndoContent(Content $currentContent, Content $undoContent, Content $undoAfterContent, $undoIsLatest=false)
Get the Content object that needs to be saved in order to undo all changes between $undo and $undoaft...
Definition: ContentHandler.php:1170
$revQuery
$revQuery
Definition: testCompression.php:56
$wgLang
$wgLang
Definition: Setup.php:831
ContentHandler\shouldCallDeprecatedContentTransformMethod
shouldCallDeprecatedContentTransformMethod(Content $content, $params)
Check if we need to provide content overrides deprecated Content method.
Definition: ContentHandler.php:1617
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:958
ContentHandler\supportsSections
supportsSections()
Returns true if this content model supports sections.
Definition: ContentHandler.php:1226
MediaWiki\Revision\SlotRenderingProvider
A lazy provider of ParserOutput objects for a revision's individual slots.
Definition: SlotRenderingProvider.php:12
ContentHandler\isSupportedFormat
isSupportedFormat( $format)
Returns true if $format is a serialization format supported by this ContentHandler,...
Definition: ContentHandler.php:537
MediaWiki\Content\Transform\PreloadTransformParams\getPage
getPage()
$dbr
$dbr
Definition: testCompression.php:54
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:608
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:798
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:428
ContentHandler\supportsRedirects
supportsRedirects()
Returns true if this content model supports redirects.
Definition: ContentHandler.php:1253
MediaWiki\Content\Transform\PreSaveTransformParams
Definition: PreSaveTransformParams.php:12
$wgContentHandlerTextFallback
$wgContentHandlerTextFallback
How to react if a plain text version of a non-text Content object is requested using ContentHandler::...
Definition: DefaultSettings.php:2459
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:198
MWException
MediaWiki exception.
Definition: MWException.php:29
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that a deprecated feature was used.
Definition: GlobalFunctions.php:997
MediaWiki\Logger\LoggerFactory
PSR-3 logger instance factory.
Definition: LoggerFactory.php:45
ContentHandler\generateHTMLOnEdit
generateHTMLOnEdit()
Whether an edit on the content should trigger an HTML render and ParserCache entry.
Definition: ContentHandler.php:1276
ContentHandler\getLanguageConverter
getLanguageConverter( $language)
Shorthand for getting a Language Converter for specific language.
Definition: ContentHandler.php:842
ContentHandler\getContentModels
static getContentModels()
Definition: ContentHandler.php:328
$blob
$blob
Definition: testCompression.php:70
ContentHandler\supportsDirectApiEditing
supportsDirectApiEditing()
Whether or not this content model supports direct editing via ApiEditPage.
Definition: ContentHandler.php:1287
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2225
WikiPage\getTitle
getTitle()
Get the title object of the article.
Definition: WikiPage.php:311
ContentHandler\getSlotDiffRendererWithOptions
getSlotDiffRendererWithOptions(IContextSource $context, $options=[])
Return the SlotDiffRenderer appropriate for this content handler.
Definition: ContentHandler.php:668
EDIT_NEW
const EDIT_NEW
Definition: Defines.php:125
wfGetLangObj
wfGetLangObj( $langcode=false)
Return a Language object from $langcode.
Definition: GlobalFunctions.php:1132
TextSlotDiffRenderer\ENGINE_PHP
const ENGINE_PHP
Use the PHP diff implementation (DiffEngine).
Definition: TextSlotDiffRenderer.php:41
ContentHandler\merge3
merge3(Content $oldContent, Content $myContent, Content $yourContent)
Attempts to merge differences between three versions.
Definition: ContentHandler.php:833
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:158
ContentHandler\makeEmptyContent
makeEmptyContent()
Creates an empty Content object of the type supported by this ContentHandler.
$title
$title
Definition: testCompression.php:38
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:1372
TextSlotDiffRenderer\ENGINE_WIKIDIFF2_INLINE
const ENGINE_WIKIDIFF2_INLINE
Use the wikidiff2 PHP module.
Definition: TextSlotDiffRenderer.php:47
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
$revStore
$revStore
Definition: testCompression.php:55
ContentHandler\getSlotDiffRenderer
getSlotDiffRenderer(IContextSource $context, array $options=[])
Get an appropriate SlotDiffRenderer for this content model.
Definition: ContentHandler.php:629
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:146
ContentHandler\getPageLanguage
getPageLanguage(Title $title, Content $content=null)
Get the language in which the content of the given page is written.
Definition: ContentHandler.php:721
ContentHandler\isParserCacheSupported
isParserCacheSupported()
Returns true for content models that support caching using the ParserCache mechanism.
Definition: ContentHandler.php:1211
Content\equals
equals(Content $that=null)
Returns true if this Content objects is conceptually equivalent to the given Content object.
$content
$content
Definition: router.php:76
ContentHandler\getDefaultFormat
getDefaultFormat()
The format used for serialization/deserialization by default by this ContentHandler.
Definition: ContentHandler.php:519
ILanguageConverter
The shared interface for all language converters.
Definition: ILanguageConverter.php:29
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:859
WikiPage\getContent
getContent( $audience=RevisionRecord::FOR_PUBLIC, Authority $performer=null)
Get the content of the current revision.
Definition: WikiPage.php:837
ContentHandler\cleanupHandlersCache
static cleanupHandlersCache()
Definition: ContentHandler.php:288
ContentHandler\getLocalizedName
static getLocalizedName( $name, Language $lang=null)
Returns the localized name for a given content model.
Definition: ContentHandler.php:307
ContentHandler\latestRevision
latestRevision(WikiPage $page)
Get the latest revision of the given $page, fetching it from the primary if necessary.
Definition: ContentHandler.php:1451
ContentHandler\getDiffEngineClass
getDiffEngineClass()
Returns the name of the diff engine to use.
Definition: ContentHandler.php:814
ContentHandler\supportsCategories
supportsCategories()
Returns true if this content model supports categories.
Definition: ContentHandler.php:1238
ContentHandler\$mModelID
string $mModelID
Definition: ContentHandler.php:349
ContentHandler\preSaveTransform
preSaveTransform(Content $content, PreSaveTransformParams $pstParams)
Returns a $content object with pre-save transformations applied (or the same object if no transformat...
Definition: ContentHandler.php:1557
MediaWiki\Content\Transform\PreloadTransformParams\getParams
getParams()
TextContent
Content object implementation for representing flat text.
Definition: TextContent.php:39
SearchEngine
Contain a class for special pages.
Definition: SearchEngine.php:37
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:58
ContentHandler\exportTransform
exportTransform( $blob, $format=null)
Applies transformations on export (returns the blob unchanged per default).
Definition: ContentHandler.php:398
Content
Base interface for content objects.
Definition: Content.php:35
ContentHandler\getFieldsForSearchIndex
getFieldsForSearchIndex(SearchEngine $engine)
Get fields definition for search index.
Definition: ContentHandler.php:1303
CommentStore\COMMENT_CHARACTER_LIMIT
const COMMENT_CHARACTER_LIMIT
Maximum length of a comment in UTF-8 characters.
Definition: CommentStore.php:48
SearchEngine\makeSearchFieldMapping
makeSearchFieldMapping( $name, $type)
Create a search field definition.
Definition: SearchEngine.php:776
Title
Represents a title within MediaWiki.
Definition: Title.php:48
MWDebug\detectDeprecatedOverride
static detectDeprecatedOverride( $instance, $class, $method, $version=false, $component=false, $callerOffset=2)
Show a warning if $method declared in $class is overridden in $instance.
Definition: MWDebug.php:255
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:461
ContentHandler\checkFormat
checkFormat( $format)
Convenient for checking whether a format provided as a parameter is actually supported.
Definition: ContentHandler.php:552
ContentHandler\getContentText
static getContentText(Content $content=null)
Convenience function for getting flat text from a Content object.
Definition: ContentHandler.php:95
$cache
$cache
Definition: mcc.php:33
MediaWiki\Content\Transform\PreloadTransformParams\getParserOptions
getParserOptions()
DifferenceEngineSlotDiffRenderer
B/C adapter for turning a DifferenceEngine into a SlotDiffRenderer.
Definition: DifferenceEngineSlotDiffRenderer.php:32
ParserCache
Cache for ParserOutput objects corresponding to the latest page revisions.
Definition: ParserCache.php:63
Content\getModel
getModel()
Returns the ID of the content model used by this Content object.
MediaWiki\Content\Transform\PreSaveTransformParams\getParserOptions
getParserOptions()
ContentHandler\addSearchField
addSearchField(&$fields, SearchEngine $engine, $name, $type)
Add new field definition to array.
Definition: ContentHandler.php:1344
ContentHandler\callDeprecatedContentPST
callDeprecatedContentPST(Content $content, PreSaveTransformParams $params)
Provided content overrides deprecated Content::preSaveTransform, call it and return.
Definition: ContentHandler.php:1640
ContentHandler\__construct
__construct( $modelId, $formats)
Constructor, initializing the ContentHandler instance with its model ID and a list of supported forma...
Definition: ContentHandler.php:367
StubObject\unstub
static unstub(&$obj)
Unstubs an object, if it is a stub object.
Definition: StubObject.php:101
ContentHandler\getSupportedFormats
getSupportedFormats()
Returns a list of serialization formats supported by the serializeContent() and unserializeContent() ...
Definition: ContentHandler.php:503
Language
Internationalisation code See https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation for more...
Definition: Language.php:42
ContentHandler\getModelID
getModelID()
Returns the model id that identifies the content model this ContentHandler can handle.
Definition: ContentHandler.php:473
MediaWiki\Revision\SlotRecord
Value object representing a content slot associated with a page revision.
Definition: SlotRecord.php:40
ContentHandler\getForContent
static getForContent(Content $content)
Returns the appropriate ContentHandler singleton for the given Content object.
Definition: ContentHandler.php:240
ContentHandler\getParserOutputForIndexing
getParserOutputForIndexing(WikiPage $page, ParserCache $cache=null)
Produce page output suitable for indexing.
Definition: ContentHandler.php:1417
MediaWiki\Content\Transform\PreSaveTransformParams\getPage
getPage()
TextSlotDiffRenderer
Renders a slot diff by doing a text diff on the native representation.
Definition: TextSlotDiffRenderer.php:38
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:19
$type
$type
Definition: testCompression.php:52