MediaWiki  master
ContentHandler.php
Go to the documentation of this file.
1 <?php
2 
29 use MediaWiki\HookContainer\ProtectedHookAccessorTrait;
36 
58 abstract class ContentHandler {
59  use ProtectedHookAccessorTrait;
60 
93  public static function getContentText( Content $content = null ) {
95 
96  if ( $content === null ) {
97  return '';
98  }
99 
100  if ( $content instanceof TextContent ) {
101  return $content->getText();
102  }
103 
104  wfDebugLog( 'ContentHandler', 'Accessing ' . $content->getModel() . ' content as text!' );
105 
106  if ( $wgContentHandlerTextFallback == 'fail' ) {
107  throw new MWException(
108  "Attempt to get text from Content with model " .
109  $content->getModel()
110  );
111  }
112 
113  if ( $wgContentHandlerTextFallback == 'serialize' ) {
114  return $content->serialize();
115  }
116 
117  return null;
118  }
119 
144  public static function makeContent( $text, Title $title = null,
145  $modelId = null, $format = null ) {
146  if ( $modelId === null ) {
147  if ( $title === null ) {
148  throw new MWException( "Must provide a Title object or a content model ID." );
149  }
150 
151  $modelId = $title->getContentModel();
152  }
153 
154  return MediaWikiServices::getInstance()
155  ->getContentHandlerFactory()
156  ->getContentHandler( $modelId )
157  ->unserializeContent( $text, $format );
158  }
159 
196  public static function getDefaultModelFor( Title $title ) {
197  $slotRoleregistry = MediaWikiServices::getInstance()->getSlotRoleRegistry();
198  $mainSlotHandler = $slotRoleregistry->getRoleHandler( 'main' );
199  return $mainSlotHandler->getDefaultModel( $title );
200  }
201 
215  public static function getForTitle( Title $title ) {
216  return MediaWikiServices::getInstance()
217  ->getContentHandlerFactory()
218  ->getContentHandler( $title->getContentModel() );
219  }
220 
236  public static function getForContent( Content $content ) {
237  return MediaWikiServices::getInstance()
238  ->getContentHandlerFactory()
239  ->getContentHandler( $content->getModel() );
240  }
241 
271  public static function getForModelID( $modelId ) {
272  return MediaWikiServices::getInstance()
273  ->getContentHandlerFactory()
274  ->getContentHandler( $modelId );
275  }
276 
284  public static function cleanupHandlersCache() {
285  wfDeprecated( __METHOD__, '1.35' );
286  // No-op: no longer needed, since the instance cache is in the
287  // ContentHandlerFactory service, and services get reset between tests
288  }
289 
303  public static function getLocalizedName( $name, Language $lang = null ) {
304  // Messages: content-model-wikitext, content-model-text,
305  // content-model-javascript, content-model-css
306  $key = "content-model-$name";
307 
308  $msg = wfMessage( $key );
309  if ( $lang ) {
310  $msg->inLanguage( $lang );
311  }
312 
313  return $msg->exists() ? $msg->plain() : $name;
314  }
315 
324  public static function getContentModels() {
325  return MediaWikiServices::getInstance()->getContentHandlerFactory()->getContentModels();
326  }
327 
336  public static function getAllContentFormats() {
337  return MediaWikiServices::getInstance()->getContentHandlerFactory()->getAllContentFormats();
338  }
339 
340  // ------------------------------------------------------------------------
341 
345  protected $mModelID;
346 
351 
363  public function __construct( $modelId, $formats ) {
364  $this->mModelID = $modelId;
365  $this->mSupportedFormats = $formats;
366  }
367 
380  abstract public function serializeContent( Content $content, $format = null );
381 
394  public function exportTransform( $blob, $format = null ) {
395  return $blob;
396  }
397 
410  abstract public function unserializeContent( $blob, $format = null );
411 
424  public function importTransform( $blob, $format = null ) {
425  return $blob;
426  }
427 
437  abstract public function makeEmptyContent();
438 
457  public function makeRedirectContent( Title $destination, $text = '' ) {
458  return null;
459  }
460 
469  public function getModelID() {
470  return $this->mModelID;
471  }
472 
481  protected function checkModelID( $model_id ) {
482  if ( $model_id !== $this->mModelID ) {
483  throw new MWException( "Bad content model: " .
484  "expected {$this->mModelID} " .
485  "but got $model_id." );
486  }
487  }
488 
499  public function getSupportedFormats() {
501  }
502 
515  public function getDefaultFormat() {
516  return $this->mSupportedFormats[0];
517  }
518 
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 
573  public function getActionOverrides() {
574  return [];
575  }
576 
604  public function createDifferenceEngine( IContextSource $context, $old = 0, $new = 0,
605  $rcid = 0, // FIXME: Deprecated, no longer used
606  $refreshCache = false, $unhide = false
607  ) {
608  $diffEngineClass = $this->getDiffEngineClass();
609  $differenceEngine = new $diffEngineClass( $context, $old, $new, $rcid, $refreshCache, $unhide );
610  $this->getHookRunner()->onGetDifferenceEngine(
611  $context, $old, $new, $refreshCache, $unhide, $differenceEngine );
612  return $differenceEngine;
613  }
614 
625  final public function getSlotDiffRenderer( IContextSource $context, array $options = [] ) {
626  $slotDiffRenderer = $this->getSlotDiffRendererWithOptions( $context, $options );
627  if ( get_class( $slotDiffRenderer ) === TextSlotDiffRenderer::class ) {
628  // To keep B/C, when SlotDiffRenderer is not overridden for a given content type
629  // but DifferenceEngine is, use that instead.
630  $differenceEngine = $this->createDifferenceEngine( $context );
631  if ( get_class( $differenceEngine ) !== DifferenceEngine::class ) {
632  // TODO turn this into a deprecation warning in a later release
633  LoggerFactory::getInstance( 'diff' )->info(
634  'Falling back to DifferenceEngineSlotDiffRenderer', [
635  'modelID' => $this->getModelID(),
636  'DifferenceEngine' => get_class( $differenceEngine ),
637  ] );
638  $slotDiffRenderer = new DifferenceEngineSlotDiffRenderer( $differenceEngine );
639  }
640  }
641  $this->getHookRunner()->onGetSlotDiffRenderer( $this, $slotDiffRenderer, $context );
642  return $slotDiffRenderer;
643  }
644 
651  protected function getSlotDiffRendererInternal( IContextSource $context ) {
652  return null;
653  }
654 
664  protected function getSlotDiffRendererWithOptions( IContextSource $context, $options = [] ) {
665  $internalRenderer = $this->getSlotDiffRendererInternal( $context );
666  // `getSlotDiffRendererInternal` has been overriden by a class using the deprecated method.
667  // Options will not work so exit early!
668  if ( $internalRenderer !== null ) {
669  return $internalRenderer;
670  }
671 
672  $contentLanguage = MediaWikiServices::getInstance()->getContentLanguage();
673  $statsdDataFactory = MediaWikiServices::getInstance()->getStatsdDataFactory();
674  $slotDiffRenderer = new TextSlotDiffRenderer();
675  $slotDiffRenderer->setStatsdDataFactory( $statsdDataFactory );
676  // XXX using the page language would be better, but it's unclear how that should be injected
677  $slotDiffRenderer->setLanguage( $contentLanguage );
678 
679  $inline = ( $options['diff-type'] ?? '' ) === 'inline';
680  $engine = 'wikidiff2';
681  $engine = DifferenceEngine::getEngine();
682 
683  if ( $engine === 'php' ) {
684  $slotDiffRenderer->setEngine( TextSlotDiffRenderer::ENGINE_PHP );
685  } elseif ( $engine === 'wikidiff2' ) {
686  if ( $inline ) {
687  $slotDiffRenderer->setEngine( TextSlotDiffRenderer::ENGINE_WIKIDIFF2_INLINE );
688  } else {
689  $slotDiffRenderer->setEngine( TextSlotDiffRenderer::ENGINE_WIKIDIFF2 );
690  }
691  } else {
692  $slotDiffRenderer->setEngine( TextSlotDiffRenderer::ENGINE_EXTERNAL, $engine );
693  }
694 
695  return $slotDiffRenderer;
696  }
697 
718  public function getPageLanguage( Title $title, Content $content = null ) {
719  global $wgLang;
720  $services = MediaWikiServices::getInstance();
721  $pageLang = $services->getContentLanguage();
722 
723  if ( $title->inNamespace( NS_MEDIAWIKI ) ) {
724  // Parse mediawiki messages with correct target language
725  list( /* $unused */, $lang ) = $services->getMessageCache()->figureMessage( $title->getText() );
726  $pageLang = $services->getLanguageFactory()->getLanguage( $lang );
727  }
728 
729  // Simplify hook handlers by only passing objects of one type, in case nothing
730  // else has unstubbed the StubUserLang object by now.
732 
733  $this->getHookRunner()->onPageContentLanguage( $title, $pageLang, $wgLang );
734 
735  return wfGetLangObj( $pageLang );
736  }
737 
759  public function getPageViewLanguage( Title $title, Content $content = null ) {
760  $pageLang = $this->getPageLanguage( $title, $content );
761 
762  if ( $title->getNamespace() !== NS_MEDIAWIKI ) {
763  // If the user chooses a variant, the content is actually
764  // in a language whose code is the variant code.
765  $variant = $this->getLanguageConverter( $pageLang )->getPreferredVariant();
766  if ( $pageLang->getCode() !== $variant ) {
767  $pageLang = MediaWikiServices::getInstance()->getLanguageFactory()
768  ->getLanguage( $variant );
769  }
770  }
771 
772  return $pageLang;
773  }
774 
795  public function canBeUsedOn( Title $title ) {
796  $ok = true;
797 
798  $this->getHookRunner()->onContentModelCanBeUsedOn( $this->getModelID(), $title, $ok );
799 
800  return $ok;
801  }
802 
811  protected function getDiffEngineClass() {
812  return DifferenceEngine::class;
813  }
814 
830  public function merge3( Content $oldContent, Content $myContent, Content $yourContent ) {
831  return false;
832  }
833 
839  private function getLanguageConverter( $language ) : ILanguageConverter {
840  return MediaWikiServices::getInstance()->getLanguageConverterFactory()
841  ->getLanguageConverter( $language );
842  }
843 
856  private function getChangeType(
857  Content $oldContent = null,
858  Content $newContent = null,
859  $flags = 0
860  ) {
861  $oldTarget = $oldContent !== null ? $oldContent->getRedirectTarget() : null;
862  $newTarget = $newContent !== null ? $newContent->getRedirectTarget() : null;
863 
864  // We check for the type of change in the given edit, and return string key accordingly
865 
866  // Blanking of a page
867  if ( $oldContent && $oldContent->getSize() > 0 &&
868  $newContent && $newContent->getSize() === 0
869  ) {
870  return 'blank';
871  }
872 
873  // Redirects
874  if ( $newTarget ) {
875  if ( !$oldTarget ) {
876  // New redirect page (by creating new page or by changing content page)
877  return 'new-redirect';
878  } elseif ( !$newTarget->equals( $oldTarget ) ||
879  $oldTarget->getFragment() !== $newTarget->getFragment()
880  ) {
881  // Redirect target changed
882  return 'changed-redirect-target';
883  }
884  } elseif ( $oldTarget ) {
885  // Changing an existing redirect into a non-redirect
886  return 'removed-redirect';
887  }
888 
889  // New page created
890  if ( $flags & EDIT_NEW && $newContent ) {
891  if ( $newContent->getSize() === 0 ) {
892  // New blank page
893  return 'newblank';
894  } else {
895  return 'newpage';
896  }
897  }
898 
899  // Removing more than 90% of the page
900  if ( $oldContent && $newContent && $oldContent->getSize() > 10 * $newContent->getSize() ) {
901  return 'replace';
902  }
903 
904  // Content model changed
905  if ( $oldContent && $newContent && $oldContent->getModel() !== $newContent->getModel() ) {
906  return 'contentmodelchange';
907  }
908 
909  return null;
910  }
911 
924  public function getAutosummary(
925  Content $oldContent = null,
926  Content $newContent = null,
927  $flags = 0
928  ) {
929  $changeType = $this->getChangeType( $oldContent, $newContent, $flags );
930 
931  // There's no applicable auto-summary for our case, so our auto-summary is empty.
932  if ( !$changeType ) {
933  return '';
934  }
935 
936  // Decide what kind of auto-summary is needed.
937  switch ( $changeType ) {
938  case 'new-redirect':
939  $newTarget = $newContent->getRedirectTarget();
940  $truncatedtext = $newContent->getTextForSummary(
941  250
942  - strlen( wfMessage( 'autoredircomment' )->inContentLanguage()->text() )
943  - strlen( $newTarget->getFullText() )
944  );
945 
946  return wfMessage( 'autoredircomment', $newTarget->getFullText() )
947  ->plaintextParams( $truncatedtext )->inContentLanguage()->text();
948  case 'changed-redirect-target':
949  $oldTarget = $oldContent->getRedirectTarget();
950  $newTarget = $newContent->getRedirectTarget();
951 
952  $truncatedtext = $newContent->getTextForSummary(
953  250
954  - strlen( wfMessage( 'autosumm-changed-redirect-target' )
955  ->inContentLanguage()->text() )
956  - strlen( $oldTarget->getFullText() )
957  - strlen( $newTarget->getFullText() )
958  );
959 
960  return wfMessage( 'autosumm-changed-redirect-target',
961  $oldTarget->getFullText(),
962  $newTarget->getFullText() )
963  ->rawParams( $truncatedtext )->inContentLanguage()->text();
964  case 'removed-redirect':
965  $oldTarget = $oldContent->getRedirectTarget();
966  $truncatedtext = $newContent->getTextForSummary(
967  250
968  - strlen( wfMessage( 'autosumm-removed-redirect' )
969  ->inContentLanguage()->text() )
970  - strlen( $oldTarget->getFullText() ) );
971 
972  return wfMessage( 'autosumm-removed-redirect', $oldTarget->getFullText() )
973  ->rawParams( $truncatedtext )->inContentLanguage()->text();
974  case 'newpage':
975  // If they're making a new article, give its text, truncated, in the summary.
976  $truncatedtext = $newContent->getTextForSummary(
977  200 - strlen( wfMessage( 'autosumm-new' )->inContentLanguage()->text() ) );
978 
979  return wfMessage( 'autosumm-new' )->rawParams( $truncatedtext )
980  ->inContentLanguage()->text();
981  case 'blank':
982  return wfMessage( 'autosumm-blank' )->inContentLanguage()->text();
983  case 'replace':
984  $truncatedtext = $newContent->getTextForSummary(
985  200 - strlen( wfMessage( 'autosumm-replace' )->inContentLanguage()->text() ) );
986 
987  return wfMessage( 'autosumm-replace' )->rawParams( $truncatedtext )
988  ->inContentLanguage()->text();
989  case 'newblank':
990  return wfMessage( 'autosumm-newblank' )->inContentLanguage()->text();
991  default:
992  return '';
993  }
994  }
995 
1008  public function getChangeTag(
1009  Content $oldContent = null,
1010  Content $newContent = null,
1011  $flags = 0
1012  ) {
1013  $changeType = $this->getChangeType( $oldContent, $newContent, $flags );
1014 
1015  // There's no applicable tag for this change.
1016  if ( !$changeType ) {
1017  return null;
1018  }
1019 
1020  // Core tags use the same keys as ones returned from $this->getChangeType()
1021  // but prefixed with pseudo namespace 'mw-', so we add the prefix before checking
1022  // if this type of change should be tagged
1023  $tag = 'mw-' . $changeType;
1024 
1025  // Not all change types are tagged, so we check against the list of defined tags.
1026  if ( in_array( $tag, ChangeTags::getSoftwareTags() ) ) {
1027  return $tag;
1028  }
1029 
1030  return null;
1031  }
1032 
1048  public function getAutoDeleteReason( Title $title, &$hasHistory ) {
1049  $dbr = wfGetDB( DB_REPLICA );
1050  $revStore = MediaWikiServices::getInstance()->getRevisionStore();
1051 
1052  // Get the last revision
1053  $revRecord = $revStore->getRevisionByTitle( $title );
1054 
1055  if ( $revRecord === null ) {
1056  return false;
1057  }
1058 
1059  // Get the article's contents
1060  $content = $revRecord->getContent( SlotRecord::MAIN );
1061  $blank = false;
1062 
1063  // If the page is blank, use the text from the previous revision,
1064  // which can only be blank if there's a move/import/protect dummy
1065  // revision involved
1066  if ( !$content || $content->isEmpty() ) {
1067  $prev = $revStore->getPreviousRevision( $revRecord );
1068 
1069  if ( $prev ) {
1070  $revRecord = $prev;
1071  $content = $prev->getContent( SlotRecord::MAIN );
1072  $blank = true;
1073  }
1074  }
1075 
1076  $this->checkModelID( $revRecord->getSlot( SlotRecord::MAIN )->getModel() );
1077 
1078  // Find out if there was only one contributor
1079  // Only scan the last 20 revisions
1080  $revQuery = $revStore->getQueryInfo();
1081  $res = $dbr->select(
1082  $revQuery['tables'],
1083  [ 'rev_user_text' => $revQuery['fields']['rev_user_text'] ],
1084  [
1085  'rev_page' => $title->getArticleID(),
1086  $dbr->bitAnd( 'rev_deleted', RevisionRecord::DELETED_USER ) . ' = 0'
1087  ],
1088  __METHOD__,
1089  [ 'LIMIT' => 20 ],
1090  $revQuery['joins']
1091  );
1092 
1093  if ( $res === false ) {
1094  // This page has no revisions, which is very weird
1095  return false;
1096  }
1097 
1098  $hasHistory = ( $res->numRows() > 1 );
1099  $row = $dbr->fetchObject( $res );
1100 
1101  if ( $row ) { // $row is false if the only contributor is hidden
1102  $onlyAuthor = $row->rev_user_text;
1103  // Try to find a second contributor
1104  foreach ( $res as $row ) {
1105  if ( $row->rev_user_text != $onlyAuthor ) { // T24999
1106  $onlyAuthor = false;
1107  break;
1108  }
1109  }
1110  } else {
1111  $onlyAuthor = false;
1112  }
1113 
1114  // Generate the summary with a '$1' placeholder
1115  if ( $blank ) {
1116  // The current revision is blank and the one before is also
1117  // blank. It's just not our lucky day
1118  $reason = wfMessage( 'exbeforeblank', '$1' )->inContentLanguage()->text();
1119  } else {
1120  if ( $onlyAuthor ) {
1121  $reason = wfMessage(
1122  'excontentauthor',
1123  '$1',
1124  $onlyAuthor
1125  )->inContentLanguage()->text();
1126  } else {
1127  $reason = wfMessage( 'excontent', '$1' )->inContentLanguage()->text();
1128  }
1129  }
1130 
1131  if ( $reason == '-' ) {
1132  // Allow these UI messages to be blanked out cleanly
1133  return '';
1134  }
1135 
1136  // Max content length = max comment length - length of the comment (excl. $1)
1137  $maxLength = CommentStore::COMMENT_CHARACTER_LIMIT - ( strlen( $reason ) - 2 );
1138  $text = $content ? $content->getTextForSummary( $maxLength ) : '';
1139 
1140  // Now replace the '$1' placeholder
1141  $reason = str_replace( '$1', $text, $reason );
1142 
1143  return $reason;
1144  }
1145 
1164  public function getUndoContent(
1165  Content $current,
1166  Content $undo,
1167  Content $undoafter,
1168  $undoIsLatest = false
1169  ) {
1170  // TODO clean this up following removal of support for Revision objects
1171  $cur_content = $current;
1172  $undo_content = $undo;
1173  $undoafter_content = $undoafter;
1174 
1175  try {
1176  $this->checkModelID( $cur_content->getModel() );
1177  $this->checkModelID( $undo_content->getModel() );
1178  if ( !$undoIsLatest ) {
1179  // If we are undoing the most recent revision,
1180  // its ok to revert content model changes. However
1181  // if we are undoing a revision in the middle, then
1182  // doing that will be confusing.
1183  $this->checkModelID( $undoafter_content->getModel() );
1184  }
1185  } catch ( MWException $e ) {
1186  // If the revisions have different content models
1187  // just return false
1188  return false;
1189  }
1190 
1191  if ( $cur_content->equals( $undo_content ) ) {
1192  // No use doing a merge if it's just a straight revert.
1193  return $undoafter_content;
1194  }
1195 
1196  $undone_content = $this->merge3( $undo_content, $undoafter_content, $cur_content );
1197 
1198  return $undone_content;
1199  }
1200 
1210  public function isParserCacheSupported() {
1211  return false;
1212  }
1213 
1225  public function supportsSections() {
1226  return false;
1227  }
1228 
1237  public function supportsCategories() {
1238  return true;
1239  }
1240 
1252  public function supportsRedirects() {
1253  return false;
1254  }
1255 
1263  public function supportsDirectEditing() {
1264  return false;
1265  }
1266 
1274  public function supportsDirectApiEditing() {
1275  return $this->supportsDirectEditing();
1276  }
1277 
1290  public function getFieldsForSearchIndex( SearchEngine $engine ) {
1291  $fields = [];
1292  $fields['category'] = $engine->makeSearchFieldMapping(
1293  'category',
1295  );
1296  $fields['category']->setFlag( SearchIndexField::FLAG_CASEFOLD );
1297 
1298  $fields['external_link'] = $engine->makeSearchFieldMapping(
1299  'external_link',
1301  );
1302 
1303  $fields['outgoing_link'] = $engine->makeSearchFieldMapping(
1304  'outgoing_link',
1306  );
1307 
1308  $fields['template'] = $engine->makeSearchFieldMapping(
1309  'template',
1311  );
1312  $fields['template']->setFlag( SearchIndexField::FLAG_CASEFOLD );
1313 
1314  $fields['content_model'] = $engine->makeSearchFieldMapping(
1315  'content_model',
1317  );
1318 
1319  return $fields;
1320  }
1321 
1331  protected function addSearchField( &$fields, SearchEngine $engine, $name, $type ) {
1332  $fields[$name] = $engine->makeSearchFieldMapping( $name, $type );
1333  return $fields;
1334  }
1335 
1359  public function getDataForSearchIndex(
1360  WikiPage $page,
1361  ParserOutput $output,
1362  SearchEngine $engine
1363  ) {
1364  $fieldData = [];
1365  $content = $page->getContent();
1366 
1367  if ( $content ) {
1368  $searchDataExtractor = new ParserOutputSearchDataExtractor();
1369 
1370  $fieldData['category'] = $searchDataExtractor->getCategories( $output );
1371  $fieldData['external_link'] = $searchDataExtractor->getExternalLinks( $output );
1372  $fieldData['outgoing_link'] = $searchDataExtractor->getOutgoingLinks( $output );
1373  $fieldData['template'] = $searchDataExtractor->getTemplates( $output );
1374 
1375  $text = $content->getTextForSearchIndex();
1376 
1377  $fieldData['text'] = $text;
1378  $fieldData['source_text'] = $text;
1379  $fieldData['text_bytes'] = $content->getSize();
1380  $fieldData['content_model'] = $content->getModel();
1381  }
1382 
1383  $this->getHookRunner()->onSearchDataForIndex( $fieldData, $this, $page, $output, $engine );
1384  return $fieldData;
1385  }
1386 
1404  public function getParserOutputForIndexing( WikiPage $page, ParserCache $cache = null ) {
1405  // TODO: MCR: ContentHandler should be called per slot, not for the whole page.
1406  // See T190066.
1407  $parserOptions = $page->makeParserOptions( 'canonical' );
1408  if ( $cache ) {
1409  $parserOutput = $cache->get( $page, $parserOptions );
1410  }
1411 
1412  if ( empty( $parserOutput ) ) {
1413  $renderer = MediaWikiServices::getInstance()->getRevisionRenderer();
1414  $revisionRecord = $this->latestRevision( $page );
1415  $parserOutput =
1416  $renderer->getRenderedRevision(
1417  $revisionRecord,
1418  $parserOptions
1419  )->getRevisionParserOutput( [
1420  // subclasses may want to add the following here:
1421  // 'generate-html' => false,
1422  ] );
1423  if ( $cache ) {
1424  $cache->save( $parserOutput, $page, $parserOptions );
1425  }
1426  }
1427  return $parserOutput;
1428  }
1429 
1438  protected function latestRevision( WikiPage $page ): RevisionRecord {
1439  $revRecord = $page->getRevisionRecord();
1440  if ( $revRecord == null ) {
1441  // If the content represents a brand new page it's possible
1442  // we need to fetch it from the primary.
1443  $page->loadPageData( WikiPage::READ_LATEST );
1444  $revRecord = $page->getRevisionRecord();
1445  if ( $revRecord == null ) {
1446  $text = $page->getTitle()->getPrefixedText();
1447  throw new MWException(
1448  "No revision could be loaded for page: $text" );
1449  }
1450  }
1451 
1452  return $revRecord;
1453  }
1454 
1487  public function getSecondaryDataUpdates(
1488  Title $title,
1489  Content $content,
1490  $role,
1491  SlotRenderingProvider $slotOutput
1492  ) {
1493  return [];
1494  }
1495 
1526  public function getDeletionUpdates( Title $title, $role ) {
1527  return [];
1528  }
1529 
1530 }
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:1487
ContentHandler
A content handler knows how do deal with a specific type of content on a wiki page.
Definition: ContentHandler.php:58
ContentHandler\getForModelID
static getForModelID( $modelId)
Returns the ContentHandler singleton for the given model ID.
Definition: ContentHandler.php:271
MediaWiki\Revision\RevisionRecord
Page revision base class.
Definition: RevisionRecord.php:47
ContentHandler\getAllContentFormats
static getAllContentFormats()
Definition: ContentHandler.php:336
WikiPage\loadPageData
loadPageData( $from='fromdb')
Load the object from a given source by title.
Definition: WikiPage.php:474
DifferenceEngine\getEngine
static getEngine()
Process DiffEngine config and get a sane, usable engine.
Definition: DifferenceEngine.php:1440
WikiPage\getRevisionRecord
getRevisionRecord()
Get the latest revision.
Definition: WikiPage.php:867
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:1263
MediaWiki\MediaWikiServices
MediaWikiServices is the service locator for the application scope of MediaWiki.
Definition: MediaWikiServices.php:180
$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:651
ContentHandler\getActionOverrides
getActionOverrides()
Returns overrides for action handlers.
Definition: ContentHandler.php:573
ContentHandler\checkModelID
checkModelID( $model_id)
Definition: ContentHandler.php:481
ContentHandler\getAutoDeleteReason
getAutoDeleteReason(Title $title, &$hasHistory)
Auto-generates a deletion reason.
Definition: ContentHandler.php:1048
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:759
WikiPage
Class representing a MediaWiki article and history.
Definition: WikiPage.php:62
ContentHandler\$mSupportedFormats
string[] $mSupportedFormats
Definition: ContentHandler.php:350
WikiPage\makeParserOptions
makeParserOptions( $context)
Get parser options suitable for rendering the primary article wikitext.
Definition: WikiPage.php:2122
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:924
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:1526
ContentHandler\getForTitle
static getForTitle(Title $title)
Returns the appropriate ContentHandler singleton for the given title.
Definition: ContentHandler.php:215
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:1008
$res
$res
Definition: testCompression.php:57
ContentHandler\serializeContent
serializeContent(Content $content, $format=null)
Serializes a Content object of the type supported by this ContentHandler.
$revQuery
$revQuery
Definition: testCompression.php:56
$wgLang
$wgLang
Definition: Setup.php:796
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:1225
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:533
$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:604
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:795
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:424
ContentHandler\supportsRedirects
supportsRedirects()
Returns true if this content model supports redirects.
Definition: ContentHandler.php:1252
$wgContentHandlerTextFallback
$wgContentHandlerTextFallback
How to react if a plain text version of a non-text Content object is requested using ContentHandler::...
Definition: DefaultSettings.php:2425
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:196
MWException
MediaWiki exception.
Definition: MWException.php:29
wfDeprecated
wfDeprecated( $function, $version=false, $component=false, $callerOffset=2)
Logs a warning that $function is deprecated.
Definition: GlobalFunctions.php:996
MediaWiki\Logger\LoggerFactory
PSR-3 logger instance factory.
Definition: LoggerFactory.php:45
ContentHandler\getLanguageConverter
getLanguageConverter( $language)
Shorthand for getting a Language Converter for specific language.
Definition: ContentHandler.php:839
ContentHandler\getContentModels
static getContentModels()
Definition: ContentHandler.php:324
$blob
$blob
Definition: testCompression.php:70
ContentHandler\supportsDirectApiEditing
supportsDirectApiEditing()
Whether or not this content model supports direct editing via ApiEditPage.
Definition: ContentHandler.php:1274
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:2306
WikiPage\getTitle
getTitle()
Get the title object of the article.
Definition: WikiPage.php:319
ContentHandler\getSlotDiffRendererWithOptions
getSlotDiffRendererWithOptions(IContextSource $context, $options=[])
Return the SlotDiffRenderer appropriate for this content handler.
Definition: ContentHandler.php:664
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:830
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:1359
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:625
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:144
ContentHandler\getPageLanguage
getPageLanguage(Title $title, Content $content=null)
Get the language in which the content of the given page is written.
Definition: ContentHandler.php:718
ContentHandler\isParserCacheSupported
isParserCacheSupported()
Returns true for content models that support caching using the ParserCache mechanism.
Definition: ContentHandler.php:1210
$content
$content
Definition: router.php:76
ContentHandler\getDefaultFormat
getDefaultFormat()
The format used for serialization/deserialization by default by this ContentHandler.
Definition: ContentHandler.php:515
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:856
WikiPage\getContent
getContent( $audience=RevisionRecord::FOR_PUBLIC, Authority $performer=null)
Get the content of the current revision.
Definition: WikiPage.php:888
ContentHandler\cleanupHandlersCache
static cleanupHandlersCache()
Definition: ContentHandler.php:284
ContentHandler\getLocalizedName
static getLocalizedName( $name, Language $lang=null)
Returns the localized name for a given content model.
Definition: ContentHandler.php:303
ContentHandler\latestRevision
latestRevision(WikiPage $page)
Get the latest revision of the given $page, fetching it from the primary if necessary.
Definition: ContentHandler.php:1438
ContentHandler\getDiffEngineClass
getDiffEngineClass()
Returns the name of the diff engine to use.
Definition: ContentHandler.php:811
ContentHandler\supportsCategories
supportsCategories()
Returns true if this content model supports categories.
Definition: ContentHandler.php:1237
ContentHandler\$mModelID
string $mModelID
Definition: ContentHandler.php:345
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:57
ContentHandler\exportTransform
exportTransform( $blob, $format=null)
Applies transformations on export (returns the blob unchanged per default).
Definition: ContentHandler.php:394
Content
Base interface for content objects.
Definition: Content.php:35
ContentHandler\getFieldsForSearchIndex
getFieldsForSearchIndex(SearchEngine $engine)
Get fields definition for search index.
Definition: ContentHandler.php:1290
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:775
Title
Represents a title within MediaWiki.
Definition: Title.php:49
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:457
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:93
$cache
$cache
Definition: mcc.php:33
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
ContentHandler\addSearchField
addSearchField(&$fields, SearchEngine $engine, $name, $type)
Add new field definition to array.
Definition: ContentHandler.php:1331
ContentHandler\__construct
__construct( $modelId, $formats)
Constructor, initializing the ContentHandler instance with its model ID and a list of supported forma...
Definition: ContentHandler.php:363
ContentHandler\getUndoContent
getUndoContent(Content $current, Content $undo, Content $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:1164
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:499
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:469
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:236
ContentHandler\getParserOutputForIndexing
getParserOutputForIndexing(WikiPage $page, ParserCache $cache=null)
Produce page output suitable for indexing.
Definition: ContentHandler.php:1404
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