27 use Wikimedia\ScopedCallback;
381 # Placeholders for text injection by hooks (must be HTML)
382 # extensions should take care to _append_ to the present value
445 $this->mTitle =
$article->getTitle();
446 $this->context =
$article->getContext();
448 $this->contentModel = $this->mTitle->getContentModel();
451 $this->contentFormat =
$handler->getDefaultFormat();
452 $this->editConflictHelperFactory = [ $this,
'newTextConflictHelper' ];
484 $this->mContextTitle =
$title;
495 if ( is_null( $this->mContextTitle ) ) {
498 __METHOD__ .
' called by ' .
wfGetAllCallers( 5 ) .
' with no title set.'
525 return $this->enableApiEditOverride ===
true ||
536 $this->enableApiEditOverride = $enableOverride;
560 if ( !
Hooks::run(
'AlternateEdit', [ $this ] ) ) {
564 wfDebug( __METHOD__ .
": enter\n" );
566 $request = $this->context->getRequest();
568 if (
$request->getBool(
'redlink' ) && $this->mTitle->exists() ) {
569 $this->context->getOutput()->redirect( $this->mTitle->getFullURL() );
574 $this->firsttime =
false;
579 $this->preview =
true;
583 $this->formtype =
'save';
584 } elseif ( $this->preview ) {
585 $this->formtype =
'preview';
586 } elseif ( $this->
diff ) {
587 $this->formtype =
'diff';
588 }
else { #
First time through
589 $this->firsttime =
true;
591 $this->formtype =
'preview';
593 $this->formtype =
'initial';
599 wfDebug( __METHOD__ .
": User can't edit\n" );
603 $this->context->getUser()->spreadAnyEditBlock();
611 $revision = $this->mArticle->getRevisionFetched();
618 if ( $this->undidRev ) {
620 $prevRev = $undidRevObj ? $undidRevObj->getPrevious() :
null;
622 if ( !$this->undidRev
629 'contentmodelediterror',
630 $revision->getContentModel(),
638 $this->isConflict =
false;
640 # Show applicable editing introductions
641 if ( $this->formtype ==
'initial' || $this->firsttime ) {
645 # Attempt submission here. This will check for edit conflicts,
646 # and redundantly check for locked database, blocked IPs, etc.
647 # that edit() already checked just in case someone tries to sneak
648 # in the back door with a hand-edited submission URL.
650 if (
'save' == $this->formtype ) {
651 $resultDetails =
null;
658 # First time through: get contents, set time for conflict
660 if (
'initial' == $this->formtype || $this->firsttime ) {
666 if ( !$this->mTitle->getArticleID() ) {
667 Hooks::run(
'EditFormPreloadText', [ &$this->textbox1, &$this->mTitle ] );
669 Hooks::run(
'EditFormInitialText', [ $this ] );
682 $user = $this->context->getUser();
683 $permErrors = $this->mTitle->getUserPermissionsErrors(
'edit',
$user, $rigor );
684 # Can this title be created?
685 if ( !$this->mTitle->exists() ) {
686 $permErrors = array_merge(
689 $this->mTitle->getUserPermissionsErrors(
'create',
$user, $rigor ),
694 # Ignore some permissions errors when a user is just previewing/viewing diffs
696 foreach ( $permErrors
as $error ) {
697 if ( ( $this->preview || $this->
diff )
699 $error[0] ==
'blockedtext' ||
700 $error[0] ==
'autoblockedtext' ||
701 $error[0] ==
'systemblockedtext'
726 $out = $this->context->getOutput();
727 if ( $this->context->getRequest()->getBool(
'redlink' ) ) {
731 $out->redirect( $this->mTitle->getFullURL() );
737 # Use the normal message if there's nothing to display
738 if ( $this->firsttime && ( !$content || $content->isEmpty() ) ) {
739 $action = $this->mTitle->exists() ?
'edit' :
740 ( $this->mTitle->isTalkPage() ?
'createtalk' :
'createpage' );
746 $out->formatPermissionsErrorMessage( $permErrors,
'edit' )
756 $out = $this->context->getOutput();
757 Hooks::run(
'EditPage::showReadOnlyForm:initial', [ $this, &
$out ] );
759 $out->setRobotPolicy(
'noindex,nofollow' );
760 $out->setPageTitle( $this->context->msg(
762 $this->getContextTitle()->getPrefixedText()
765 $out->addHTML( $this->editFormPageTop );
766 $out->addHTML( $this->editFormTextTop );
768 if ( $errorMessage !==
'' ) {
769 $out->addWikiText( $errorMessage );
770 $out->addHTML(
"<hr />\n" );
773 # If the user made changes, preserve them when showing the markup
774 # (This happens when a user is blocked during edit, for instance)
775 if ( !$this->firsttime ) {
777 $out->addWikiMsg(
'viewyourtext' );
782 # Serialize using the default format if the content model is not supported
783 # (e.g. for an old revision with a different model)
786 $out->addWikiMsg(
'viewsourcetext' );
789 $out->addHTML( $this->editFormTextBeforeContent );
790 $this->
showTextbox( $text,
'wpTextbox1', [
'readonly' ] );
791 $out->addHTML( $this->editFormTextAfterContent );
795 $out->addModules(
'mediawiki.action.edit.collapsibleFooter' );
797 $out->addHTML( $this->editFormTextBottom );
798 if ( $this->mTitle->exists() ) {
799 $out->returnToMain(
null, $this->mTitle );
809 $config = $this->context->getConfig();
810 $previewOnOpenNamespaces = $config->get(
'PreviewOnOpenNamespaces' );
811 $request = $this->context->getRequest();
812 if ( $config->get(
'RawHtml' ) ) {
818 if (
$request->getVal(
'preview' ) ==
'yes' ) {
821 } elseif (
$request->getVal(
'preview' ) ==
'no' ) {
824 } elseif ( $this->section ==
'new' ) {
827 } elseif ( (
$request->getVal(
'preload' ) !==
null || $this->mTitle->exists() )
828 && $this->context->getUser()->getOption(
'previewonfirst' )
832 } elseif ( !$this->mTitle->exists()
833 && isset( $previewOnOpenNamespaces[$this->mTitle->getNamespace()] )
834 && $previewOnOpenNamespaces[$this->mTitle->getNamespace()]
850 if ( $this->mTitle->isUserConfigPage() ) {
851 $name = $this->mTitle->getSkinFromConfigSubpage();
852 $skins = array_merge(
856 return !in_array(
$name, $skins )
857 && in_array( strtolower(
$name ), $skins );
872 return $contentHandler->supportsSections();
881 # Section edit can come from either the form or a link
882 $this->section =
$request->getVal(
'wpSection',
$request->getVal(
'section' ) );
885 throw new ErrorPageError(
'sectioneditnotsupported-title',
'sectioneditnotsupported-text' );
888 $this->isNew = !$this->mTitle->exists() || $this->section ==
'new';
891 # These fields need to be checked for encoding.
892 # Also remove trailing whitespace, but don't remove _initial_
893 # whitespace from the text boxes. This may be significant formatting.
894 $this->textbox1 = rtrim(
$request->getText(
'wpTextbox1' ) );
895 if ( !
$request->getCheck(
'wpTextbox2' ) ) {
905 $this->unicodeCheck =
$request->getText(
'wpUnicodeCheck' );
907 $this->summary =
$request->getText(
'wpSummary' );
909 # If the summary consists of a heading, e.g. '==Foobar==', extract the title from the
910 # header syntax, e.g. 'Foobar'. This is mainly an issue when we are using wpSummary for
912 $this->summary = preg_replace(
'/^\s*=+\s*(.*?)\s*=+\s*$/',
'$1', $this->summary );
914 # Treat sectiontitle the same way as summary.
915 # Note that wpSectionTitle is not yet a part of the actual edit form, as wpSummary is
916 # currently doing double duty as both edit summary and section title. Right now this
917 # is just to allow API edits to work around this limitation, but this should be
918 # incorporated into the actual edit form when EditPage is rewritten (Bugs 18654, 26312).
919 $this->sectiontitle =
$request->getText(
'wpSectionTitle' );
920 $this->sectiontitle = preg_replace(
'/^\s*=+\s*(.*?)\s*=+\s*$/',
'$1', $this->sectiontitle );
922 $this->edittime =
$request->getVal(
'wpEdittime' );
923 $this->editRevId =
$request->getIntOrNull(
'editRevId' );
924 $this->starttime =
$request->getVal(
'wpStarttime' );
931 $this->scrolltop =
$request->getIntOrNull(
'wpScrolltop' );
933 if ( $this->textbox1 ===
'' &&
$request->getVal(
'wpTextbox1' ) === null ) {
937 $this->incompleteForm =
true;
945 $this->incompleteForm = ( !
$request->getVal(
'wpUltimateParam' )
946 && is_null( $this->edittime ) );
948 if ( $this->incompleteForm ) {
949 # If the form is incomplete, force to preview.
950 wfDebug( __METHOD__ .
": Form data appears to be incomplete\n" );
951 wfDebug(
"POST DATA: " . var_export( $_POST,
true ) .
"\n" );
952 $this->preview =
true;
954 $this->preview =
$request->getCheck(
'wpPreview' );
962 # Some browsers will not report any submit button
963 # if the user hits enter in the comment box.
964 # The unmarked state will be assumed to be a save,
965 # if the form seems otherwise complete.
966 wfDebug( __METHOD__ .
": Passed token check.\n" );
967 } elseif ( $this->
diff ) {
968 # Failed token check, but only requested "Show Changes".
969 wfDebug( __METHOD__ .
": Failed token check; Show Changes requested.\n" );
971 # Page might be a hack attempt posted from
972 # an external site. Preview instead of saving.
973 wfDebug( __METHOD__ .
": Failed token check; forcing preview\n" );
974 $this->preview =
true;
978 if ( !preg_match(
'/^\d{14}$/', $this->edittime ) ) {
979 $this->edittime =
null;
982 if ( !preg_match(
'/^\d{14}$/', $this->starttime ) ) {
983 $this->starttime =
null;
986 $this->recreate =
$request->getCheck(
'wpRecreate' );
988 $this->minoredit =
$request->getCheck(
'wpMinoredit' );
989 $this->watchthis =
$request->getCheck(
'wpWatchthis' );
991 $user = $this->context->getUser();
992 # Don't force edit summaries when a user is editing their own user or talk page
994 && $this->mTitle->getText() ==
$user->getName()
996 $this->allowBlankSummary =
true;
998 $this->allowBlankSummary =
$request->getBool(
'wpIgnoreBlankSummary' )
999 || !
$user->getOption(
'forceeditsummary' );
1002 $this->autoSumm =
$request->getText(
'wpAutoSummary' );
1004 $this->allowBlankArticle =
$request->getBool(
'wpIgnoreBlankArticle' );
1005 $this->allowSelfRedirect =
$request->getBool(
'wpIgnoreSelfRedirect' );
1009 $this->changeTags = [];
1011 $this->changeTags = array_filter( array_map(
'trim', explode(
',',
1015 # Not a posted form? Start with nothing.
1016 wfDebug( __METHOD__ .
": Not a posted form.\n" );
1017 $this->textbox1 =
'';
1018 $this->summary =
'';
1019 $this->sectiontitle =
'';
1020 $this->edittime =
'';
1021 $this->editRevId =
null;
1023 $this->
edit =
false;
1024 $this->preview =
false;
1025 $this->
save =
false;
1026 $this->
diff =
false;
1027 $this->minoredit =
false;
1029 $this->watchthis =
$request->getBool(
'watchthis',
false );
1030 $this->recreate =
false;
1034 if ( $this->section ==
'new' &&
$request->getVal(
'preloadtitle' ) ) {
1035 $this->sectiontitle =
$request->getVal(
'preloadtitle' );
1037 $this->summary =
$request->getVal(
'preloadtitle' );
1038 } elseif ( $this->section !=
'new' &&
$request->getVal(
'summary' ) ) {
1039 $this->summary =
$request->getText(
'summary' );
1040 if ( $this->summary !==
'' ) {
1041 $this->hasPresetSummary =
true;
1045 if (
$request->getVal(
'minor' ) ) {
1046 $this->minoredit =
true;
1050 $this->oldid =
$request->getInt(
'oldid' );
1051 $this->parentRevId =
$request->getInt(
'parentRevId' );
1053 $this->bot =
$request->getBool(
'bot',
true );
1054 $this->nosummary =
$request->getBool(
'nosummary' );
1057 $this->contentModel =
$request->getText(
'model', $this->contentModel );
1059 $this->contentFormat =
$request->getText(
'format', $this->contentFormat );
1065 'editpage-invalidcontentmodel-title',
1066 'editpage-invalidcontentmodel-text',
1071 if ( !
$handler->isSupportedFormat( $this->contentFormat ) ) {
1073 'editpage-notsupportedcontentformat-title',
1074 'editpage-notsupportedcontentformat-text',
1088 $this->editintro =
$request->getText(
'editintro',
1090 $this->section ===
'new' ?
'MediaWiki:addsection-editintro' :
'' );
1115 $this->edittime = $this->
page->getTimestamp();
1116 $this->editRevId = $this->
page->getLatest();
1119 if ( $content ===
false ) {
1122 $this->textbox1 = $this->
toEditText( $content );
1124 $user = $this->context->getUser();
1126 # Sort out the "watch" checkbox
1127 if (
$user->getOption(
'watchdefault' ) ) {
1129 $this->watchthis =
true;
1130 } elseif (
$user->getOption(
'watchcreations' ) && !$this->mTitle->exists() ) {
1132 $this->watchthis =
true;
1133 } elseif (
$user->isWatched( $this->mTitle ) ) {
1135 $this->watchthis =
true;
1138 $this->minoredit =
true;
1140 if ( $this->textbox1 ===
false ) {
1158 $user = $this->context->getUser();
1159 $request = $this->context->getRequest();
1162 if ( !$this->mTitle->exists() || $this->section ==
'new' ) {
1163 if ( $this->mTitle->getNamespace() ==
NS_MEDIAWIKI && $this->section !=
'new' ) {
1164 # If this is a system message, get the default text.
1165 $msg = $this->mTitle->getDefaultMessageText();
1169 if ( $content ===
false ) {
1170 # If requested, preload some text.
1171 $preload =
$request->getVal(
'preload',
1173 $this->section ===
'new' ?
'MediaWiki:addsection-preload' :
'' );
1180 if ( $this->section !=
'' ) {
1183 $content = $orig ? $orig->getSection( $this->section ) :
null;
1186 $content = $def_content;
1189 $undoafter =
$request->getInt(
'undoafter' );
1190 $undo =
$request->getInt(
'undo' );
1192 if ( $undo > 0 && $undoafter > 0 ) {
1196 # Sanity check, make sure it's the right page,
1197 # the revisions exist and they were not deleted.
1198 # Otherwise, $content will be left as-is.
1199 if ( !is_null( $undorev ) && !is_null( $oldrev ) &&
1203 $content = $this->
page->getUndoContent( $undorev, $oldrev );
1205 if ( $content ===
false ) {
1206 # Warn the user that something went wrong
1207 $undoMsg =
'failure';
1211 $newContent = $content->preSaveTransform( $this->mTitle,
$user, $popts );
1212 if ( $newContent->getModel() !== $oldContent->getModel() ) {
1217 $this->contentModel = $newContent->getModel();
1218 $this->contentFormat = $oldrev->getContentFormat();
1221 if ( $newContent->equals( $oldContent ) ) {
1222 # Tell the user that the undo results in no change,
1223 # i.e. the revisions were already undone.
1224 $undoMsg =
'nochange';
1227 # Inform the user of our success and set an automatic edit summary
1228 $undoMsg =
'success';
1230 # If we just undid one rev, use an autosummary
1231 $firstrev = $oldrev->getNext();
1232 if ( $firstrev && $firstrev->getId() == $undo ) {
1233 $userText = $undorev->getUserText();
1234 if ( $userText ===
'' ) {
1235 $undoSummary = $this->context->msg(
1236 'undo-summary-username-hidden',
1238 )->inContentLanguage()->text();
1240 $undoSummary = $this->context->msg(
1244 )->inContentLanguage()->text();
1246 if ( $this->summary ===
'' ) {
1247 $this->summary = $undoSummary;
1249 $this->summary = $undoSummary . $this->context->msg(
'colon-separator' )
1252 $this->undidRev = $undo;
1254 $this->formtype =
'diff';
1264 $out = $this->context->getOutput();
1266 $class = ( $undoMsg ==
'success' ?
'' :
'error ' ) .
"mw-undo-{$undoMsg}";
1267 $this->editFormPageTop .=
$out->parse(
"<div class=\"{$class}\">" .
1268 $this->context->msg(
'undo-' . $undoMsg )->plain() .
'</div>',
true,
true );
1271 if ( $content ===
false ) {
1296 if ( $this->section ==
'new' ) {
1299 $revision = $this->mArticle->getRevisionFetched();
1300 if ( $revision ===
null ) {
1302 return $handler->makeEmptyContent();
1321 if ( $this->parentRevId ) {
1324 return $this->mArticle->getRevIdFetched();
1340 if ( $content ===
false || $content ===
null ) {
1342 return $handler->makeEmptyContent();
1343 } elseif ( !$this->undidRev ) {
1348 $logger = LoggerFactory::getInstance(
'editpage' );
1349 if ( $this->contentModel !==
$rev->getContentModel() ) {
1350 $logger->warning(
"Overriding content model from current edit {prev} to {new}", [
1351 'prev' => $this->contentModel,
1352 'new' =>
$rev->getContentModel(),
1353 'title' => $this->
getTitle()->getPrefixedDBkey(),
1354 'method' => __METHOD__
1356 $this->contentModel =
$rev->getContentModel();
1361 if ( !$content->isSupportedFormat( $this->contentFormat ) ) {
1362 $logger->warning(
"Current revision content format unsupported. Overriding {prev} to {new}", [
1364 'prev' => $this->contentFormat,
1365 'new' =>
$rev->getContentFormat(),
1366 'title' => $this->
getTitle()->getPrefixedDBkey(),
1367 'method' => __METHOD__
1369 $this->contentFormat =
$rev->getContentFormat();
1383 $this->mPreloadContent = $content;
1398 if ( !empty( $this->mPreloadContent ) ) {
1404 if ( $preload ===
'' ) {
1405 return $handler->makeEmptyContent();
1408 $user = $this->context->getUser();
1410 # Check for existence to avoid getting MediaWiki:Noarticletext
1413 return $handler->makeEmptyContent();
1422 return $handler->makeEmptyContent();
1432 return $handler->makeEmptyContent();
1435 if ( $content->getModel() !==
$handler->getModelID() ) {
1436 $converted = $content->convert(
$handler->getModelID() );
1438 if ( !$converted ) {
1440 wfDebug(
"Attempt to preload incompatible content: " .
1441 "can't convert " . $content->getModel() .
1444 return $handler->makeEmptyContent();
1447 $content = $converted;
1450 return $content->preloadTransform(
$title, $parserOptions,
$params );
1461 $token =
$request->getVal(
'wpEditToken' );
1462 $user = $this->context->getUser();
1463 $this->mTokenOk =
$user->matchEditToken( $token );
1464 $this->mTokenOkExceptSuffix =
$user->matchEditTokenNoSuffix( $token );
1483 $revisionId = $this->
page->getLatest();
1484 $postEditKey = self::POST_EDIT_COOKIE_KEY_PREFIX . $revisionId;
1487 if ( $statusValue == self::AS_SUCCESS_NEW_ARTICLE ) {
1489 } elseif ( $this->oldid ) {
1493 $response = $this->context->getRequest()->response();
1494 $response->setCookie( $postEditKey, $val, time() + self::POST_EDIT_COOKIE_DURATION );
1504 # Allow bots to exempt some edits from bot flagging
1505 $bot = $this->context->getUser()->isAllowed(
'bot' ) &&
$this->bot;
1508 Hooks::run(
'EditPage::attemptSave:after', [ $this,
$status, $resultDetails ] );
1517 if ( $this->context->getRequest()->getText(
'mode' ) !==
'conflict' ) {
1538 if (
$status->value == self::AS_SUCCESS_UPDATE
1539 ||
$status->value == self::AS_SUCCESS_NEW_ARTICLE
1543 $this->didSave =
true;
1544 if ( !$resultDetails[
'nullEdit'] ) {
1549 $out = $this->context->getOutput();
1553 $request = $this->context->getRequest();
1554 $extraQueryRedirect =
$request->getVal(
'wpExtraQueryRedirect' );
1575 $out->addWikiText(
'<div class="error">' .
"\n" .
$status->getWikiText() .
'</div>' );
1579 $query = $resultDetails[
'redirect'] ?
'redirect=no' :
'';
1580 if ( $extraQueryRedirect ) {
1582 $query = $extraQueryRedirect;
1587 $anchor = isset( $resultDetails[
'sectionanchor'] ) ? $resultDetails[
'sectionanchor'] :
'';
1588 $out->redirect( $this->mTitle->getFullURL(
$query ) . $anchor );
1593 $sectionanchor = $resultDetails[
'sectionanchor'];
1597 'ArticleUpdateBeforeRedirect',
1598 [ $this->mArticle, &$sectionanchor, &$extraQuery ]
1601 if ( $resultDetails[
'redirect'] ) {
1602 if ( $extraQuery ==
'' ) {
1603 $extraQuery =
'redirect=no';
1605 $extraQuery =
'redirect=no&' . $extraQuery;
1608 if ( $extraQueryRedirect ) {
1609 if ( $extraQuery ===
'' ) {
1610 $extraQuery = $extraQueryRedirect;
1612 $extraQuery = $extraQuery .
'&' . $extraQueryRedirect;
1616 $out->redirect( $this->mTitle->getFullURL( $extraQuery ) . $sectionanchor );
1641 $permission = $this->mTitle->isTalkPage() ?
'createtalk' :
'createpage';
1652 $this->hookError =
'<div class="error">' .
"\n" .
$status->getWikiText() .
1669 if ( $this->hookError !=
'' ) {
1670 # ...or the hook could be expecting us to produce an error
1671 $status->fatal(
'hookaborted' );
1678 [ $this->context, $content,
$status, $this->summary,
1679 $user, $this->minoredit ] )
1681 # Error messages etc. could be handled within the hook...
1683 $status->fatal(
'hookaborted' );
1696 } elseif ( !
$status->isOK() ) {
1697 # ...or the hook could be expecting us to produce an error
1700 $status->fatal(
'hookaborted' );
1715 $errmsg =
$status->getWikiText(
1718 $this->context->getLanguage()
1721 <
div class=
"errorbox">
1737 if ( $this->sectiontitle !==
'' ) {
1742 if ( $this->summary ===
'' ) {
1743 $cleanSectionTitle =
$wgParser->stripSectionName( $this->sectiontitle );
1744 return $this->context->msg(
'newsectionsummary' )
1745 ->rawParams( $cleanSectionTitle )->inContentLanguage()->text();
1747 } elseif ( $this->summary !==
'' ) {
1749 # This is a new section, so create a link to the new section
1750 # in the revision summary.
1751 $cleanSummary =
$wgParser->stripSectionName( $this->summary );
1752 return $this->context->msg(
'newsectionsummary' )
1753 ->rawParams( $cleanSummary )->inContentLanguage()->text();
1784 $user = $this->context->getUser();
1786 if ( !
Hooks::run(
'EditPage::attemptSave', [ $this ] ) ) {
1787 wfDebug(
"Hook 'EditPage::attemptSave' aborted article saving\n" );
1788 $status->fatal(
'hookaborted' );
1793 if ( $this->unicodeCheck !== self::UNICODE_CHECK ) {
1794 $status->fatal(
'unicode-support-fail' );
1799 $request = $this->context->getRequest();
1800 $spam =
$request->getText(
'wpAntispam' );
1801 if ( $spam !==
'' ) {
1806 $this->mTitle->getPrefixedText() .
1807 '" submitted bogus field "' .
1811 $status->fatal(
'spamprotectionmatch',
false );
1817 # Construct Content object
1821 'content-failed-to-parse',
1822 $this->contentModel,
1823 $this->contentFormat,
1830 # Check image redirect
1831 if ( $this->mTitle->getNamespace() ==
NS_FILE &&
1832 $textbox_content->isRedirect() &&
1833 !
$user->isAllowed(
'upload' )
1843 if ( $match ===
false && $this->section ==
'new' ) {
1844 # $wgSpamRegex is enforced on this new heading/summary because, unlike
1845 # regular summaries, it is added to the actual wikitext.
1846 if ( $this->sectiontitle !==
'' ) {
1847 # This branch is taken when the API is used with the 'sectiontitle' parameter.
1850 # This branch is taken when the "Add Topic" user interface is used, or the API
1851 # is used with the 'summary' parameter.
1855 if ( $match ===
false ) {
1858 if ( $match !==
false ) {
1861 $pdbk = $this->mTitle->getPrefixedDBkey();
1862 $match = str_replace(
"\n",
'', $match );
1863 wfDebugLog(
'SpamRegex',
"$ip spam regex hit [[$pdbk]]: \"$match\"" );
1864 $status->fatal(
'spamprotectionmatch', $match );
1870 [ $this, $this->textbox1, $this->section, &$this->hookError, $this->summary ] )
1872 # Error messages etc. could be handled within the hook...
1873 $status->fatal(
'hookaborted' );
1876 } elseif ( $this->hookError !=
'' ) {
1877 # ...or the hook could be expecting us to produce an error
1878 $status->fatal(
'hookaborted' );
1883 if (
$user->isBlockedFrom( $this->mTitle,
false ) ) {
1886 $user->spreadAnyEditBlock();
1888 # Check block state against master, thus 'false'.
1889 $status->setResult(
false, self::AS_BLOCKED_PAGE_FOR_USER );
1893 $this->contentLength = strlen( $this->textbox1 );
1894 $config = $this->context->getConfig();
1895 $maxArticleSize = $config->get(
'MaxArticleSize' );
1896 if ( $this->contentLength > $maxArticleSize * 1024 ) {
1898 $this->tooBig =
true;
1899 $status->setResult(
false, self::AS_CONTENT_TOO_BIG );
1903 if ( !
$user->isAllowed(
'edit' ) ) {
1904 if (
$user->isAnon() ) {
1905 $status->setResult(
false, self::AS_READ_ONLY_PAGE_ANON );
1908 $status->fatal(
'readonlytext' );
1914 $changingContentModel =
false;
1915 if ( $this->contentModel !== $this->mTitle->getContentModel() ) {
1916 if ( !$config->get(
'ContentHandlerUseDB' ) ) {
1917 $status->fatal(
'editpage-cannot-use-custom-model' );
1920 } elseif ( !
$user->isAllowed(
'editcontentmodel' ) ) {
1921 $status->setResult(
false, self::AS_NO_CHANGE_CONTENT_MODEL );
1927 if ( !$titleWithNewContentModel->userCan(
'editcontentmodel',
$user )
1928 || !$titleWithNewContentModel->userCan(
'edit',
$user )
1930 $status->setResult(
false, self::AS_NO_CHANGE_CONTENT_MODEL );
1934 $changingContentModel =
true;
1935 $oldContentModel = $this->mTitle->getContentModel();
1938 if ( $this->changeTags ) {
1940 $this->changeTags,
$user );
1941 if ( !$changeTagsStatus->isOK() ) {
1943 return $changeTagsStatus;
1948 $status->fatal(
'readonlytext' );
1952 if (
$user->pingLimiter() ||
$user->pingLimiter(
'linkpurge', 0 )
1953 || ( $changingContentModel &&
$user->pingLimiter(
'editcontentmodel' ) )
1955 $status->fatal(
'actionthrottledtext' );
1960 # If the article has been deleted while editing, don't save it without
1963 $status->setResult(
false, self::AS_ARTICLE_WAS_DELETED );
1967 # Load the page data from the master. If anything changes in the meantime,
1968 # we detect it by using page_latest like a token in a 1 try compare-and-swap.
1969 $this->
page->loadPageData(
'fromdbmaster' );
1970 $new = !$this->
page->exists();
1974 if ( !$this->mTitle->userCan(
'create',
$user ) ) {
1975 $status->fatal(
'nocreatetext' );
1977 wfDebug( __METHOD__ .
": no create permission\n" );
1984 $defaultMessageText = $this->mTitle->getDefaultMessageText();
1985 if ( $this->mTitle->getNamespace() ===
NS_MEDIAWIKI && $defaultMessageText !==
false ) {
1986 $defaultText = $defaultMessageText;
1991 if ( !$this->allowBlankArticle && $this->textbox1 === $defaultText ) {
1992 $this->blankArticle =
true;
1993 $status->fatal(
'blankarticle' );
1994 $status->setResult(
false, self::AS_BLANK_ARTICLE );
2002 $content = $textbox_content;
2004 $result[
'sectionanchor'] =
'';
2005 if ( $this->section ==
'new' ) {
2006 if ( $this->sectiontitle !==
'' ) {
2008 $content = $content->addSectionHeader( $this->sectiontitle );
2009 } elseif ( $this->summary !==
'' ) {
2011 $content = $content->addSectionHeader( $this->summary );
2020 # Article exists. Check for edit conflict.
2022 $this->
page->clear(); # Force reload
of dates,
etc.
2023 $timestamp = $this->
page->getTimestamp();
2024 $latest = $this->
page->getLatest();
2026 wfDebug(
"timestamp: {$timestamp}, edittime: {$this->edittime}\n" );
2029 if ( $timestamp != $this->edittime
2030 || ( $this->editRevId !==
null && $this->editRevId != $latest )
2032 $this->isConflict =
true;
2033 if ( $this->section ==
'new' ) {
2034 if ( $this->
page->getUserText() ==
$user->getName() &&
2041 .
": duplicate new section submission; trigger edit conflict!\n" );
2044 $this->isConflict =
false;
2045 wfDebug( __METHOD__ .
": conflict suppressed; new section\n" );
2047 } elseif ( $this->section ==
''
2049 DB_MASTER, $this->mTitle->getArticleID(),
2053 # Suppress edit conflict with self, except for section edits where merging is required.
2054 wfDebug( __METHOD__ .
": Suppressing edit conflict, same user.\n" );
2055 $this->isConflict =
false;
2060 if ( $this->sectiontitle !==
'' ) {
2068 if ( $this->isConflict ) {
2070 .
": conflict! getting section '{$this->section}' for time '{$this->edittime}'"
2071 .
" (id '{$this->editRevId}') (article time '{$timestamp}')\n" );
2074 if ( $this->editRevId !==
null ) {
2075 $content = $this->
page->replaceSectionAtRev(
2082 $content = $this->
page->replaceSectionContent(
2090 wfDebug( __METHOD__ .
": getting section '{$this->section}'\n" );
2091 $content = $this->
page->replaceSectionContent(
2098 if ( is_null( $content ) ) {
2099 wfDebug( __METHOD__ .
": activating conflict; section replace failed.\n" );
2100 $this->isConflict =
true;
2101 $content = $textbox_content;
2102 } elseif ( $this->isConflict ) {
2106 $this->isConflict =
false;
2107 wfDebug( __METHOD__ .
": Suppressing edit conflict, successful merge.\n" );
2109 $this->section =
'';
2111 wfDebug( __METHOD__ .
": Keeping edit conflict, failed merge.\n" );
2115 if ( $this->isConflict ) {
2116 $status->setResult(
false, self::AS_CONFLICT_DETECTED );
2124 if ( $this->section ==
'new' ) {
2126 if ( !$this->allowBlankSummary && trim( $this->summary ) ==
'' ) {
2127 $this->missingSummary =
true;
2128 $status->fatal(
'missingsummary' );
2134 if ( $this->textbox1 ==
'' ) {
2135 $this->missingComment =
true;
2136 $status->fatal(
'missingcommenttext' );
2140 } elseif ( !$this->allowBlankSummary
2141 && !$content->equals( $this->getOriginalContent(
$user ) )
2142 && !$content->isRedirect()
2145 $this->missingSummary =
true;
2146 $status->fatal(
'missingsummary' );
2152 $sectionanchor =
'';
2153 if ( $this->section ==
'new' ) {
2155 } elseif ( $this->section !=
'' ) {
2156 # Try to get a section anchor from the section source, redirect
2157 # to edited section if header found.
2158 # XXX: Might be better to integrate this into Article::replaceSectionAtRev
2159 # for duplicate heading checking and maybe parsing.
2160 $hasmatch = preg_match(
"/^ *([=]{1,6})(.*?)(\\1) *\\n/i", $this->textbox1,
$matches );
2161 # We can't deal with anchors, includes, html etc in the header for now,
2162 # headline would need to be parsed to improve this.
2163 if ( $hasmatch && strlen(
$matches[2] ) > 0 ) {
2167 $result[
'sectionanchor'] = $sectionanchor;
2173 $this->textbox1 = $this->
toEditText( $content );
2174 $this->section =
'';
2179 if ( !$this->allowSelfRedirect
2180 && $content->isRedirect()
2181 && $content->getRedirectTarget()->equals( $this->
getTitle() )
2185 if ( !$currentTarget || !$currentTarget->equals( $this->getTitle() ) ) {
2186 $this->selfRedirect =
true;
2187 $status->fatal(
'selfredirect' );
2194 $this->contentLength = strlen( $this->
toEditText( $content ) );
2195 if ( $this->contentLength > $maxArticleSize * 1024 ) {
2196 $this->tooBig =
true;
2197 $status->setResult(
false, self::AS_MAX_ARTICLE_SIZE_EXCEEDED );
2203 ( ( $this->minoredit && !$this->isNew ) ?
EDIT_MINOR : 0 ) |
2206 $doEditStatus = $this->
page->doEditContent(
2212 $content->getDefaultFormat(),
2217 if ( !$doEditStatus->isOK() ) {
2221 $errors = $doEditStatus->getErrorsArray();
2222 if ( in_array( $errors[0][0],
2223 [
'edit-gone-missing',
'edit-conflict',
'edit-already-exists' ] )
2225 $this->isConflict =
true;
2229 return $doEditStatus;
2232 $result[
'nullEdit'] = $doEditStatus->hasMessage(
'edit-no-change' );
2235 $user->pingLimiter(
'linkpurge' );
2237 $result[
'redirect'] = $content->isRedirect();
2242 if ( $changingContentModel ) {
2245 $new ?
false : $oldContentModel,
2246 $this->contentModel,
2261 $new = $oldModel ===
false;
2262 $log =
new ManualLogEntry(
'contentmodel', $new ?
'new' :
'change' );
2263 $log->setPerformer(
$user );
2264 $log->setTarget( $this->mTitle );
2265 $log->setComment( $reason );
2266 $log->setParameters( [
2267 '4::oldmodel' => $oldModel,
2268 '5::newmodel' => $newModel
2270 $logid = $log->insert();
2271 $log->publish( $logid );
2278 $user = $this->context->getUser();
2279 if ( !
$user->isLoggedIn() ) {
2310 $baseContent = $baseRevision ? $baseRevision->getContent() :
null;
2312 if ( is_null( $baseContent ) ) {
2318 $currentContent = $currentRevision ? $currentRevision->getContent() :
null;
2320 if ( is_null( $currentContent ) ) {
2326 $result =
$handler->merge3( $baseContent, $editContent, $currentContent );
2331 $this->parentRevId = $currentRevision->getId();
2344 if ( !$this->mBaseRevision ) {
2346 $this->mBaseRevision = $this->editRevId
2386 foreach ( $regexes
as $regex ) {
2388 if ( preg_match( $regex, $text,
$matches ) ) {
2396 $out = $this->context->getOutput();
2398 $out->addModules(
'mediawiki.action.edit' );
2399 $out->addModuleStyles(
'mediawiki.action.edit.styles' );
2400 $out->addModuleStyles(
'mediawiki.editfont.styles' );
2402 $user = $this->context->getUser();
2403 if (
$user->getOption(
'showtoolbar' ) ) {
2408 $out->addModules(
'mediawiki.toolbar' );
2411 if (
$user->getOption(
'uselivepreview' ) ) {
2412 $out->addModules(
'mediawiki.action.edit.preview' );
2415 if (
$user->getOption(
'useeditwarning' ) ) {
2416 $out->addModules(
'mediawiki.action.edit.editWarning' );
2419 # Enabled article-related sidebar, toplinks, etc.
2420 $out->setArticleRelated(
true );
2423 if ( $this->isConflict ) {
2424 $msg =
'editconflict';
2425 } elseif ( $contextTitle->exists() && $this->section !=
'' ) {
2426 $msg = $this->section ==
'new' ?
'editingcomment' :
'editingsection';
2428 $msg = $contextTitle->exists()
2430 && $contextTitle->getDefaultMessageText() !==
false
2436 # Use the title defined by DISPLAYTITLE magic word when present
2437 # NOTE: getDisplayTitle() returns HTML while getPrefixedText() returns plain text.
2438 # setPageTitle() treats the input as wikitext, which should be safe in either case.
2439 $displayTitle = isset( $this->mParserOutput ) ? $this->mParserOutput->getDisplayTitle() :
false;
2440 if ( $displayTitle ===
false ) {
2441 $displayTitle = $contextTitle->getPrefixedText();
2443 $out->setPageTitle( $this->context->msg( $msg, $displayTitle ) );
2445 $config = $this->context->getConfig();
2447 # Transmit the name of the message to JavaScript for live preview
2448 # Keep Resources.php/mediawiki.action.edit.preview in sync with the possible keys
2449 $out->addJsConfigVars( [
2450 'wgEditMessage' => $msg,
2451 'wgAjaxEditStash' => $config->get(
'AjaxEditStash' ),
2456 $out->addJsConfigVars(
2457 'wgEditSubmitButtonLabelPublish',
2458 $config->get(
'EditSubmitButtonLabelPublish' )
2466 if ( $this->suppressIntro ) {
2470 $out = $this->context->getOutput();
2471 $namespace = $this->mTitle->getNamespace();
2474 # Show a warning if editing an interface message
2475 $out->wrapWikiMsg(
"<div class='mw-editinginterface'>\n$1\n</div>",
'editinginterface' );
2476 # If this is a default message (but not css, json, or js),
2477 # show a hint that it is translatable on translatewiki.net
2483 $defaultMessageText = $this->mTitle->getDefaultMessageText();
2484 if ( $defaultMessageText !==
false ) {
2485 $out->wrapWikiMsg(
"<div class='mw-translateinterface'>\n$1\n</div>",
2486 'translateinterface' );
2489 } elseif ( $namespace ==
NS_FILE ) {
2490 # Show a hint to shared repo
2492 if ( $file && !$file->isLocal() ) {
2493 $descUrl = $file->getDescriptionUrl();
2494 # there must be a description url to show a hint to shared repo
2496 if ( !$this->mTitle->exists() ) {
2497 $out->wrapWikiMsg(
"<div class=\"mw-sharedupload-desc-create\">\n$1\n</div>", [
2498 'sharedupload-desc-create', $file->getRepo()->getDisplayName(), $descUrl
2501 $out->wrapWikiMsg(
"<div class=\"mw-sharedupload-desc-edit\">\n$1\n</div>", [
2502 'sharedupload-desc-edit', $file->getRepo()->getDisplayName(), $descUrl
2509 # Show a warning message when someone creates/edits a user (talk) page but the user does not exist
2510 # Show log extract when the user is currently blocked
2512 $username = explode(
'/', $this->mTitle->getText(), 2 )[0];
2517 $out->wrapWikiMsg(
"<div class=\"mw-userpage-userdoesnotexist error\">\n$1\n</div>",
2520 # Show log extract if the user is currently blocked
2528 'showIfEmpty' =>
false,
2530 'blocked-notice-logextract',
2531 $user->getName() # Support GENDER
in notice
2537 # Try to add a custom edit intro, or use the standard one if this is not possible.
2540 $this->context->msg(
'helppage' )->inContentLanguage()->text()
2542 if ( $this->context->getUser()->isLoggedIn() ) {
2545 "<div class=\"mw-newarticletext plainlinks\">\n$1\n</div>",
2554 "<div class=\"mw-newarticletextanon plainlinks\">\n$1\n</div>",
2556 'newarticletextanon',
2562 # Give a notice if the user is editing a deleted/moved page...
2563 if ( !$this->mTitle->exists() ) {
2570 'conds' => [
'log_action != ' .
$dbr->addQuotes(
'revision' ) ],
2571 'showIfEmpty' =>
false,
2572 'msgKey' => [
'recreate-moveddeleted-warn' ]
2584 if ( $this->editintro ) {
2588 $this->context->getOutput()->addWikiTextTitleTidy(
2589 '<div class="mw-editintro">{{:' .
$title->getFullText() .
'}}</div>',
2617 if ( $content ===
null || $content ===
false || is_string( $content ) ) {
2622 throw new MWException(
'This content model is not supported: ' . $content->getModel() );
2625 return $content->serialize( $this->contentFormat );
2645 if ( $text ===
false || $text ===
null ) {
2650 $this->contentModel, $this->contentFormat );
2653 throw new MWException(
'This content model is not supported: ' . $content->getModel() );
2668 # need to parse the preview early so that we know which templates are used,
2669 # otherwise users with "show preview after edit box" will get a blank list
2670 # we parse this near the beginning so that setHeaders can do the title
2671 # setting work instead of leaving it in getPreviewText
2672 $previewOutput =
'';
2673 if ( $this->formtype ==
'preview' ) {
2677 $out = $this->context->getOutput();
2681 Hooks::run(
'EditPage::showEditForm:initial', [ &$editPage, &
$out ] );
2688 if ( !$this->isConflict &&
2689 $this->section !=
'' &&
2694 $out->showErrorPage(
'sectioneditnotsupported-title',
'sectioneditnotsupported-text' );
2700 $out->addHTML( $this->editFormPageTop );
2702 $user = $this->context->getUser();
2703 if (
$user->getOption(
'previewontop' ) ) {
2707 $out->addHTML( $this->editFormTextTop );
2709 $showToolbar =
true;
2711 if ( $this->formtype ==
'save' ) {
2714 $showToolbar =
false;
2716 $out->wrapWikiMsg(
"<div class='error mw-deleted-while-editing'>\n$1\n</div>",
2717 'deletedwhileediting' );
2726 'class' =>
'mw-editform',
2727 'id' => self::EDITFORM_ID,
2728 'name' => self::EDITFORM_ID,
2731 'enctype' =>
'multipart/form-data'
2735 if ( is_callable( $formCallback ) ) {
2736 wfWarn(
'The $formCallback parameter to ' . __METHOD__ .
'is deprecated' );
2737 call_user_func_array( $formCallback, [ &
$out ] );
2745 Xml::openElement(
'div', [
'id' =>
'antispam-container',
'style' =>
'display: none;' ] )
2748 [
'for' =>
'wpAntispam' ],
2749 $this->context->msg(
'simpleantispam-label' )->parse()
2755 'name' =>
'wpAntispam',
2756 'id' =>
'wpAntispam',
2765 Hooks::run(
'EditPage::showEditForm:fields', [ &$editPage, &
$out ] );
2771 $username = $this->lastDelete->user_name;
2773 ->getComment(
'log_comment', $this->lastDelete )->text;
2777 $key = $comment ===
''
2778 ?
'confirmrecreate-noreason'
2779 :
'confirmrecreate';
2781 '<div class="mw-confirm-recreate">' .
2782 $this->context->msg( $key,
$username,
"<nowiki>$comment</nowiki>" )->parse() .
2783 Xml::checkLabel( $this->context->msg(
'recreate' )->text(),
'wpRecreate',
'wpRecreate',
false,
2790 # When the summary is hidden, also hide them on preview/show changes
2791 if ( $this->nosummary ) {
2795 # If a blank edit summary was previously provided, and the appropriate
2796 # user preference is active, pass a hidden tag as wpIgnoreBlankSummary. This will stop the
2797 # user being bounced back more than once in the event that a summary
2800 # For a bit more sophisticated detection of blank summaries, hash the
2801 # automatic one and pass that in the hidden field wpAutoSummary.
2802 if ( $this->missingSummary || ( $this->section ==
'new' && $this->nosummary ) ) {
2806 if ( $this->undidRev ) {
2810 if ( $this->selfRedirect ) {
2814 if ( $this->hasPresetSummary ) {
2818 $this->autoSumm = md5(
'' );
2821 $autosumm = $this->autoSumm ? $this->autoSumm : md5( $this->summary );
2832 if ( $this->section ==
'new' ) {
2837 $out->addHTML( $this->editFormTextBeforeContent );
2838 if ( $this->isConflict ) {
2844 $this->textbox1 = $this->
toEditText( $content );
2853 if ( !$this->mTitle->isUserConfigPage() && $showToolbar &&
$user->getOption(
'showtoolbar' ) ) {
2854 $out->addHTML( self::getEditToolbar( $this->mTitle ) );
2857 if ( $this->blankArticle ) {
2861 if ( $this->isConflict ) {
2866 $conflictTextBoxAttribs = [];
2868 $conflictTextBoxAttribs[
'style'] =
'display:none;';
2869 } elseif ( $this->isOldRev ) {
2870 $conflictTextBoxAttribs[
'class'] =
'mw-textarea-oldrev';
2879 $out->addHTML( $this->editFormTextAfterContent );
2889 $out->addHTML( $this->editFormTextAfterTools .
"\n" );
2897 self::getPreviewLimitReport( $this->mParserOutput ) ) );
2899 $out->addModules(
'mediawiki.action.edit.collapsibleFooter' );
2901 if ( $this->isConflict ) {
2906 $msg = $this->context->msg(
2907 'content-failed-to-parse',
2908 $this->contentModel,
2909 $this->contentFormat,
2912 $out->addWikiText(
'<div class="error">' . $msg->text() .
'</div>' );
2917 if ( $this->isConflict ) {
2919 } elseif ( $this->preview ) {
2921 } elseif ( $this->
diff ) {
2926 $out->addHTML(
Html::hidden(
'mode', $mode, [
'id' =>
'mw-edit-mode' ] ) );
2931 $out->addHTML( $this->editFormTextBottom .
"\n</form>\n" );
2933 if ( !
$user->getOption(
'previewontop' ) ) {
2947 $this->context, MediaWikiServices::getInstance()->getLinkRenderer()
2952 if ( $this->preview ) {
2954 } elseif ( $this->section !=
'' ) {
2959 $templateListFormatter->format( $templates,
$type )
2970 preg_match(
"/^(=+)(.+)\\1\\s*(\n|$)/i", $text,
$matches );
2980 $out = $this->context->getOutput();
2981 $user = $this->context->getUser();
2982 if ( $this->isConflict ) {
2984 $this->editRevId = $this->
page->getLatest();
2986 if ( $this->section !=
'' && $this->section !=
'new' ) {
2987 if ( !$this->summary && !$this->preview && !$this->
diff ) {
2989 if ( $sectionTitle !==
false ) {
2990 $this->summary =
"/* $sectionTitle */ ";
2997 if ( $this->missingComment ) {
2998 $out->wrapWikiMsg(
"<div id='mw-missingcommenttext'>\n$1\n</div>",
'missingcommenttext' );
3001 if ( $this->missingSummary && $this->section !=
'new' ) {
3003 "<div id='mw-missingsummary'>\n$1\n</div>",
3004 [
'missingsummary', $buttonLabel ]
3008 if ( $this->missingSummary && $this->section ==
'new' ) {
3010 "<div id='mw-missingcommentheader'>\n$1\n</div>",
3011 [
'missingcommentheader', $buttonLabel ]
3015 if ( $this->blankArticle ) {
3017 "<div id='mw-blankarticle'>\n$1\n</div>",
3018 [
'blankarticle', $buttonLabel ]
3022 if ( $this->selfRedirect ) {
3024 "<div id='mw-selfredirect'>\n$1\n</div>",
3025 [
'selfredirect', $buttonLabel ]
3029 if ( $this->hookError !==
'' ) {
3030 $out->addWikiText( $this->hookError );
3033 if ( $this->section !=
'new' ) {
3034 $revision = $this->mArticle->getRevisionFetched();
3040 "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
3041 'rev-deleted-text-permission'
3045 "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
3046 'rev-deleted-text-view'
3050 if ( !$revision->isCurrent() ) {
3051 $this->mArticle->setOldSubtitle( $revision->getId() );
3052 $out->addWikiMsg(
'editingold' );
3053 $this->isOldRev =
true;
3055 } elseif ( $this->mTitle->exists() ) {
3058 $out->wrapWikiMsg(
"<div class='errorbox'>\n$1\n</div>\n",
3059 [
'missing-revision', $this->oldid ] );
3066 "<div id=\"mw-read-only-warning\">\n$1\n</div>",
3069 } elseif (
$user->isAnon() ) {
3070 if ( $this->formtype !=
'preview' ) {
3072 "<div id='mw-anon-edit-warning' class='warningbox'>\n$1\n</div>",
3073 [
'anoneditwarning',
3076 'returnto' => $this->
getTitle()->getPrefixedDBkey()
3080 'returnto' => $this->
getTitle()->getPrefixedDBkey()
3085 $out->wrapWikiMsg(
"<div id=\"mw-anon-preview-warning\" class=\"warningbox\">\n$1</div>",
3086 'anonpreviewwarning'
3090 if ( $this->mTitle->isUserConfigPage() ) {
3091 # Check the skin exists
3094 "<div class='error' id='mw-userinvalidconfigtitle'>\n$1\n</div>",
3095 [
'userinvalidconfigtitle', $this->mTitle->getSkinFromConfigSubpage() ]
3098 if ( $this->
getTitle()->isSubpageOf(
$user->getUserPage() ) ) {
3099 $isUserCssConfig = $this->mTitle->isUserCssConfigPage();
3100 $isUserJsonConfig = $this->mTitle->isUserJsonConfigPage();
3101 $isUserJsConfig = $this->mTitle->isUserJsConfigPage();
3103 $warning = $isUserCssConfig
3105 : ( $isUserJsonConfig ?
'userjsonispublic' :
'userjsispublic' );
3107 $out->wrapWikiMsg(
'<div class="mw-userconfigpublic">$1</div>', $warning );
3109 if ( $this->formtype !==
'preview' ) {
3110 $config = $this->context->getConfig();
3111 if ( $isUserCssConfig && $config->get(
'AllowUserCss' ) ) {
3113 "<div id='mw-usercssyoucanpreview'>\n$1\n</div>",
3114 [
'usercssyoucanpreview' ]
3116 } elseif ( $isUserJsonConfig ) {
3118 "<div id='mw-userjsonyoucanpreview'>\n$1\n</div>",
3119 [
'userjsonyoucanpreview' ]
3121 } elseif ( $isUserJsConfig && $config->get(
'AllowUserJs' ) ) {
3123 "<div id='mw-userjsyoucanpreview'>\n$1\n</div>",
3124 [
'userjsyoucanpreview' ]
3136 # Add header copyright warning
3148 $conf = $this->context->getConfig();
3149 $oldCommentSchema = $conf->get(
'CommentTableSchemaMigrationStage' ) ===
MIGRATION_OLD;
3153 return ( is_array( $inputAttrs ) ? $inputAttrs : [] ) + [
3154 'id' =>
'wpSummary',
3155 'name' =>
'wpSummary',
3159 'spellcheck' =>
'true',
3173 $inputAttrs = OOUI\Element::configFromHtmlAttributes(
3182 $inputAttrs[
'inputId'] = $inputAttrs[
'id'];
3183 $inputAttrs[
'id'] =
'wpSummaryWidget';
3185 return new OOUI\FieldLayout(
3186 new OOUI\TextInputWidget( [
3188 'infusable' =>
true,
3191 'label' =>
new OOUI\HtmlSnippet( $labelText ),
3193 'id' =>
'wpSummaryLabel',
3194 'classes' => [ $this->missingSummary ?
'mw-summarymissed' :
'mw-summary' ],
3206 # Add a class if 'missingsummary' is triggered to allow styling of the summary line
3207 $summaryClass = $this->missingSummary ?
'mw-summarymissed' :
'mw-summary';
3208 if ( $isSubjectPreview ) {
3209 if ( $this->nosummary ) {
3213 if ( !$this->mShowSummaryField ) {
3218 $labelText = $this->context->msg( $isSubjectPreview ?
'subject' :
'summary' )->parse();
3222 [
'class' => $summaryClass ]
3236 if ( !
$summary || ( !$this->preview && !$this->
diff ) ) {
3242 if ( $isSubjectPreview ) {
3243 $summary = $this->context->msg(
'newsectionsummary' )
3245 ->inContentLanguage()->text();
3248 $message = $isSubjectPreview ?
'subject-preview' :
'summary-preview';
3250 $summary = $this->context->msg( $message )->parse()
3256 $out = $this->context->getOutput();
3261 $out->addHTML(
Html::hidden(
'wpScrolltop', $this->scrolltop, [
'id' =>
'wpScrolltop' ] ) );
3277 $this->context->getOutput()->addHTML(
3279 Html::hidden(
"wpEditToken", $this->context->getUser()->getEditToken() ) .
3306 $attribs = [
'style' =>
'display:none;' ];
3309 $classes = $builder->getTextboxProtectionCSSClasses( $this->
getTitle() );
3311 # Is an old revision being edited?
3312 if ( $this->isOldRev ) {
3313 $classes[] =
'mw-textarea-oldrev';
3326 $textoverride !==
null ? $textoverride : $this->textbox1,
3333 $this->
showTextbox( $this->textbox2,
'wpTextbox2', [
'tabindex' => 6,
'readonly' ] );
3338 $attribs = $builder->buildTextboxAttribs(
3341 $this->context->getUser(),
3345 $this->context->getOutput()->addHTML(
3353 $classes[] =
'ontop';
3356 $attribs = [
'id' =>
'wikiPreview',
'class' => implode(
' ', $classes ) ];
3358 if ( $this->formtype !=
'preview' ) {
3359 $attribs[
'style'] =
'display: none;';
3362 $out = $this->context->getOutput();
3365 if ( $this->formtype ==
'preview' ) {
3369 $pageViewLang = $this->mTitle->getPageViewLanguage();
3370 $attribs = [
'lang' => $pageViewLang->getHtmlCode(),
'dir' => $pageViewLang->getDir(),
3371 'class' =>
'mw-content-' . $pageViewLang->getDir() ];
3375 $out->addHTML(
'</div>' );
3377 if ( $this->formtype ==
'diff' ) {
3381 $msg = $this->context->msg(
3382 'content-failed-to-parse',
3383 $this->contentModel,
3384 $this->contentFormat,
3387 $out->addWikiText(
'<div class="error">' . $msg->text() .
'</div>' );
3400 $this->mArticle->openShowCategory();
3402 # This hook seems slightly odd here, but makes things more
3403 # consistent for extensions.
3404 $out = $this->context->getOutput();
3406 $out->addHTML( $text );
3408 $this->mArticle->closeShowCategory();
3422 $oldtitlemsg =
'currentrev';
3423 # if message does not exist, show diff against the preloaded default
3424 if ( $this->mTitle->getNamespace() ==
NS_MEDIAWIKI && !$this->mTitle->exists() ) {
3425 $oldtext = $this->mTitle->getDefaultMessageText();
3426 if ( $oldtext !==
false ) {
3427 $oldtitlemsg =
'defaultmessagetext';
3437 if ( $this->editRevId !==
null ) {
3438 $newContent = $this->
page->replaceSectionAtRev(
3439 $this->section, $textboxContent, $this->summary, $this->editRevId
3442 $newContent = $this->
page->replaceSectionContent(
3443 $this->section, $textboxContent, $this->summary, $this->edittime
3447 if ( $newContent ) {
3448 Hooks::run(
'EditPageGetDiffContent', [ $this, &$newContent ] );
3450 $user = $this->context->getUser();
3452 $newContent = $newContent->preSaveTransform( $this->mTitle,
$user, $popts );
3455 if ( ( $oldContent && !$oldContent->isEmpty() ) || ( $newContent && !$newContent->isEmpty() ) ) {
3456 $oldtitle = $this->context->msg( $oldtitlemsg )->parse();
3457 $newtitle = $this->context->msg(
'yourtext' )->parse();
3459 if ( !$oldContent ) {
3460 $oldContent = $newContent->getContentHandler()->makeEmptyContent();
3463 if ( !$newContent ) {
3464 $newContent = $oldContent->getContentHandler()->makeEmptyContent();
3467 $de = $oldContent->getContentHandler()->createDifferenceEngine( $this->context );
3468 $de->setContent( $oldContent, $newContent );
3470 $difftext = $de->getDiff( $oldtitle, $newtitle );
3471 $de->showDiffStyle();
3476 $this->context->getOutput()->addHTML(
'<div id="wikiDiff">' . $difftext .
'</div>' );
3483 $msg =
'editpage-head-copy-warn';
3484 if ( !$this->context->msg( $msg )->isDisabled() ) {
3485 $this->context->getOutput()->wrapWikiMsg(
"<div class='editpage-head-copywarn'>\n$1\n</div>",
3486 'editpage-head-copy-warn' );
3499 $msg =
'editpage-tos-summary';
3500 Hooks::run(
'EditPageTosSummary', [ $this->mTitle, &$msg ] );
3501 if ( !$this->context->msg( $msg )->isDisabled() ) {
3502 $out = $this->context->getOutput();
3503 $out->addHTML(
'<div class="mw-tos-summary">' );
3504 $out->addWikiMsg( $msg );
3505 $out->addHTML(
'</div>' );
3514 $this->context->getOutput()->addHTML(
'<div class="mw-editTools">' .
3515 $this->context->msg(
'edittools' )->inContentLanguage()->parse() .
3540 $copywarnMsg = [
'copyrightwarning',
3541 '[[' .
wfMessage(
'copyrightpage' )->inContentLanguage()->text() .
']]',
3544 $copywarnMsg = [
'copyrightwarning2',
3545 '[[' .
wfMessage(
'copyrightpage' )->inContentLanguage()->text() .
']]' ];
3550 $msg = call_user_func_array(
'wfMessage', $copywarnMsg )->title(
$title );
3552 $msg->inLanguage( $langcode );
3554 return "<div id=\"editpage-copywarn\">\n" .
3555 $msg->$format() .
"\n</div>";
3572 $limitReport =
Html::rawElement(
'div', [
'class' =>
'mw-limitReportExplanation' ],
3573 wfMessage(
'limitreport-title' )->parseAsBlock()
3577 $limitReport .=
Html::openElement(
'div', [
'class' =>
'preview-limit-report-wrapper' ] );
3580 'class' =>
'preview-limit-report wikitable'
3586 [ $key, &
$value, &$limitReport,
true,
true ]
3589 $valueMsg =
wfMessage( [
"$key-value-html",
"$key-value" ] );
3590 if ( !$valueMsg->exists() ) {
3593 if ( !$keyMsg->isDisabled() && !$valueMsg->isDisabled() ) {
3608 return $limitReport;
3612 $out = $this->context->getOutput();
3613 $out->addHTML(
"<div class='editOptions'>\n" );
3615 if ( $this->section !=
'new' ) {
3622 [
'minor' => $this->minoredit,
'watch' => $this->watchthis ]
3624 $checkboxesHTML =
new OOUI\HorizontalLayout( [
'items' => $checkboxes ] );
3626 $out->addHTML(
"<div class='editCheckboxes'>" . $checkboxesHTML .
"</div>\n" );
3630 $out->addHTML( $this->editFormTextAfterWarn );
3632 $out->addHTML(
"<div class='editButtons'>\n" );
3637 $message = $this->context->msg(
'edithelppage' )->inContentLanguage()->text();
3641 $this->context->msg(
'edithelp' )->text(),
3642 [
'target' =>
'helpwindow',
'href' => $edithelpurl ],
3645 $this->context->msg(
'word-separator' )->escaped() .
3646 $this->context->msg(
'newwindow' )->parse();
3648 $out->addHTML(
" <span class='cancelLink'>{$cancel}</span>\n" );
3649 $out->addHTML(
" <span class='editHelp'>{$edithelp}</span>\n" );
3650 $out->addHTML(
"</div><!-- editButtons -->\n" );
3654 $out->addHTML(
"</div><!-- editOptions -->\n" );
3662 $out = $this->context->getOutput();
3665 if (
Hooks::run(
'EditPageBeforeConflictDiff', [ &$editPage, &
$out ] ) ) {
3681 if ( !$this->isConflict && $this->oldid > 0 ) {
3684 $cancelParams[
'redirect'] =
'no';
3687 return new OOUI\ButtonWidget( [
3688 'id' =>
'mw-editform-cancel',
3690 'label' =>
new OOUI\HtmlSnippet( $this->context->msg(
'cancel' )->parse() ),
3692 'infusable' =>
true,
3693 'flags' =>
'destructive',
3707 return $title->getLocalURL( [
'action' => $this->
action ] );
3718 if ( $this->deletedSinceEdit !==
null ) {
3722 $this->deletedSinceEdit =
false;
3724 if ( !$this->mTitle->exists() && $this->mTitle->isDeletedQuick() ) {
3726 if ( $this->lastDelete ) {
3727 $deleteTime =
wfTimestamp( TS_MW, $this->lastDelete->log_timestamp );
3728 if ( $deleteTime > $this->starttime ) {
3729 $this->deletedSinceEdit =
true;
3744 $data =
$dbr->selectRow(
3745 array_merge( [
'logging' ], $commentQuery[
'tables'], $actorQuery[
'tables'], [
'user' ] ),
3755 ] + $commentQuery[
'fields'] + $actorQuery[
'fields'],
3757 'log_namespace' => $this->mTitle->getNamespace(),
3758 'log_title' => $this->mTitle->getDBkey(),
3759 'log_type' =>
'delete',
3760 'log_action' =>
'delete',
3763 [
'LIMIT' => 1,
'ORDER BY' =>
'log_timestamp DESC' ],
3765 'user' => [
'JOIN',
'user_id=' . $actorQuery[
'fields'][
'log_user'] ],
3766 ] + $commentQuery[
'joins'] + $actorQuery[
'joins']
3769 if ( is_object( $data ) ) {
3771 $data->user_name = $this->context->msg(
'rev-deleted-user' )->escaped();
3775 $data->log_comment_text = $this->context->msg(
'rev-deleted-comment' )->escaped();
3776 $data->log_comment_data =
null;
3789 $out = $this->context->getOutput();
3790 $config = $this->context->getConfig();
3796 if ( $this->textbox1 !==
'' ) {
3800 $parsedNote =
$out->parse(
"<div class='previewnote'>" .
3801 $this->context->msg(
'session_fail_preview_html' )->text() .
"</div>",
3815 'AlternateEditPreview',
3816 [ $this, &$content, &$previewHTML, &$this->mParserOutput ] )
3818 return $previewHTML;
3821 # provide a anchor link to the editform
3822 $continueEditing =
'<span class="mw-continue-editing">' .
3823 '[[#' . self::EDITFORM_ID .
'|' .
3824 $this->context->getLanguage()->getArrow() .
' ' .
3825 $this->context->msg(
'continue-editing' )->text() .
']]</span>';
3826 if ( $this->mTriedSave && !$this->mTokenOk ) {
3827 if ( $this->mTokenOkExceptSuffix ) {
3828 $note = $this->context->msg(
'token_suffix_mismatch' )->plain();
3831 $note = $this->context->msg(
'session_fail_preview' )->plain();
3834 } elseif ( $this->incompleteForm ) {
3835 $note = $this->context->msg(
'edit_form_incomplete' )->plain();
3836 if ( $this->mTriedSave ) {
3840 $note = $this->context->msg(
'previewnote' )->plain() .
' ' . $continueEditing;
3843 # don't parse non-wikitext pages, show message about preview
3844 if ( $this->mTitle->isUserConfigPage() || $this->mTitle->isSiteConfigPage() ) {
3845 if ( $this->mTitle->isUserConfigPage() ) {
3847 } elseif ( $this->mTitle->isSiteConfigPage() ) {
3855 if ( $level ===
'user' && !$config->get(
'AllowUserCss' ) ) {
3860 if ( $level ===
'user' ) {
3865 if ( $level ===
'user' && !$config->get(
'AllowUserJs' ) ) {
3872 # Used messages to make sure grep find them:
3873 # Messages: usercsspreview, userjsonpreview, userjspreview,
3874 # sitecsspreview, sitejsonpreview, sitejspreview
3875 if ( $level && $format ) {
3876 $note =
"<div id='mw-{$level}{$format}preview'>" .
3877 $this->context->msg(
"{$level}{$format}preview" )->text() .
3878 ' ' . $continueEditing .
"</div>";
3882 # If we're adding a comment, we need to show the
3883 # summary as the headline
3884 if ( $this->section ===
"new" && $this->summary !==
"" ) {
3885 $content = $content->addSectionHeader( $this->summary );
3888 $hook_args = [ $this, &$content ];
3889 Hooks::run(
'EditPageGetPreviewContent', $hook_args );
3892 $parserOutput = $parserResult[
'parserOutput'];
3893 $previewHTML = $parserResult[
'html'];
3894 $this->mParserOutput = $parserOutput;
3895 $out->addParserOutputMetadata( $parserOutput );
3897 if (
count( $parserOutput->getWarnings() ) ) {
3898 $note .=
"\n\n" . implode(
"\n\n", $parserOutput->getWarnings() );
3902 $m = $this->context->msg(
3903 'content-failed-to-parse',
3904 $this->contentModel,
3905 $this->contentFormat,
3908 $note .=
"\n\n" . $m->parse();
3912 if ( $this->isConflict ) {
3913 $conflict =
'<h2 id="mw-previewconflict">'
3914 . $this->context->msg(
'previewconflict' )->escaped() .
"</h2>\n";
3916 $conflict =
'<hr />';
3919 $previewhead =
"<div class='previewnote'>\n" .
3920 '<h2 id="mw-previewheader">' . $this->context->msg(
'preview' )->escaped() .
"</h2>" .
3921 $out->parse( $note,
true,
true ) . $conflict .
"</div>\n";
3923 $pageViewLang = $this->mTitle->getPageViewLanguage();
3924 $attribs = [
'lang' => $pageViewLang->getHtmlCode(),
'dir' => $pageViewLang->getDir(),
3925 'class' =>
'mw-content-' . $pageViewLang->getDir() ];
3932 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
3933 $stats->increment(
'edit.failures.' . $failureType );
3941 $parserOptions = $this->
page->makeParserOptions( $this->context );
3942 $parserOptions->setIsPreview(
true );
3943 $parserOptions->setIsSectionPreview( !is_null( $this->section ) && $this->section !==
'' );
3944 $parserOptions->enableLimitReport();
3945 return $parserOptions;
3958 $user = $this->context->getUser();
3961 $scopedCallback = $parserOptions->setupFakeRevision(
3962 $this->mTitle, $pstContent,
$user );
3963 $parserOutput = $pstContent->getParserOutput( $this->mTitle,
null, $parserOptions );
3964 ScopedCallback::consume( $scopedCallback );
3966 'parserOutput' => $parserOutput,
3967 'html' => $parserOutput->getText( [
3968 'enableSectionEditLinks' =>
false
3977 if ( $this->preview || $this->section !=
'' ) {
3979 if ( !isset( $this->mParserOutput ) ) {
3982 foreach ( $this->mParserOutput->getTemplates()
as $ns =>
$template ) {
3989 return $this->mTitle->getTemplateLinksFrom();
4005 $showSignature =
true;
4021 'id' =>
'mw-editbutton-bold',
4023 'close' =>
'\'\
'\'',
4024 'sample' =>
wfMessage(
'bold_sample' )->text(),
4025 'tip' =>
wfMessage(
'bold_tip' )->text(),
4028 'id' =>
'mw-editbutton-italic',
4031 'sample' =>
wfMessage(
'italic_sample' )->text(),
4032 'tip' =>
wfMessage(
'italic_tip' )->text(),
4035 'id' =>
'mw-editbutton-link',
4038 'sample' =>
wfMessage(
'link_sample' )->text(),
4039 'tip' =>
wfMessage(
'link_tip' )->text(),
4042 'id' =>
'mw-editbutton-extlink',
4045 'sample' =>
wfMessage(
'extlink_sample' )->text(),
4046 'tip' =>
wfMessage(
'extlink_tip' )->text(),
4049 'id' =>
'mw-editbutton-headline',
4052 'sample' =>
wfMessage(
'headline_sample' )->text(),
4053 'tip' =>
wfMessage(
'headline_tip' )->text(),
4055 $imagesAvailable ? [
4056 'id' =>
'mw-editbutton-image',
4059 'sample' =>
wfMessage(
'image_sample' )->text(),
4060 'tip' =>
wfMessage(
'image_tip' )->text(),
4062 $imagesAvailable ? [
4063 'id' =>
'mw-editbutton-media',
4066 'sample' =>
wfMessage(
'media_sample' )->text(),
4067 'tip' =>
wfMessage(
'media_tip' )->text(),
4070 'id' =>
'mw-editbutton-nowiki',
4071 'open' =>
"<nowiki>",
4072 'close' =>
"</nowiki>",
4073 'sample' =>
wfMessage(
'nowiki_sample' )->text(),
4074 'tip' =>
wfMessage(
'nowiki_tip' )->text(),
4077 'id' =>
'mw-editbutton-signature',
4078 'open' =>
wfMessage(
'sig-text',
'~~~~' )->inContentLanguage()->text(),
4081 'tip' =>
wfMessage(
'sig_tip' )->text(),
4084 'id' =>
'mw-editbutton-hr',
4085 'open' =>
"\n----\n",
4092 $script =
'mw.loader.using("mediawiki.toolbar", function () {';
4093 foreach ( $toolarray
as $tool ) {
4113 'mw.toolbar.addButton',
4115 ResourceLoader::inDebugMode()
4121 $toolbar =
'<div id="toolbar"></div>';
4123 if (
Hooks::run(
'EditPageBeforeEditToolbar', [ &$toolbar ] ) ) {
4126 $wgOut->addScript( ResourceLoader::makeInlineScript( $script ) );
4153 $user = $this->context->getUser();
4155 if ( !$this->isNew &&
$user->isAllowed(
'minoredit' ) ) {
4156 $checkboxes[
'wpMinoredit'] = [
4157 'id' =>
'wpMinoredit',
4158 'label-message' =>
'minoredit',
4160 'tooltip' =>
'minoredit',
4161 'label-id' =>
'mw-editpage-minoredit',
4162 'legacy-name' =>
'minor',
4163 'default' => $checked[
'minor'],
4167 if (
$user->isLoggedIn() ) {
4168 $checkboxes[
'wpWatchthis'] = [
4169 'id' =>
'wpWatchthis',
4170 'label-message' =>
'watchthis',
4172 'tooltip' =>
'watch',
4173 'label-id' =>
'mw-editpage-watch',
4174 'legacy-name' =>
'watch',
4175 'default' => $checked[
'watch'],
4180 Hooks::run(
'EditPageGetCheckboxesDefinition', [ $editPage, &$checkboxes ] );
4204 if ( isset(
$options[
'tooltip'] ) ) {
4205 $accesskey = $this->context->msg(
"accesskey-{$options['tooltip']}" )->text();
4208 if ( isset(
$options[
'title-message'] ) ) {
4209 $title = $this->context->msg(
$options[
'title-message'] )->text();
4212 $checkboxes[ $legacyName ] =
new OOUI\FieldLayout(
4213 new OOUI\CheckboxInputWidget( [
4215 'accessKey' => $accesskey,
4220 'infusable' =>
true,
4223 'align' =>
'inline',
4224 'label' =>
new OOUI\HtmlSnippet( $this->context->msg(
$options[
'label-message'] )->parse() ),
4234 $legacyCheckboxes = [];
4235 if ( !$this->isNew ) {
4236 $legacyCheckboxes[
'minor'] =
'';
4238 $legacyCheckboxes[
'watch'] =
'';
4240 foreach ( $checkboxes
as $name => $oouiLayout ) {
4245 Hooks::run(
'EditPageBeforeEditChecks', [ &$ep, &$legacyCheckboxes, &
$tabindex ],
'1.29' );
4248 if (
$html && !isset( $checkboxes[
$name] ) ) {
4249 $checkboxes[
$name] =
new OOUI\Widget( [
'content' =>
new OOUI\HtmlSnippet(
$html ) ] );
4264 $this->context->getConfig()->get(
'EditSubmitButtonLabelPublish' );
4267 $newPage = !$this->mTitle->exists();
4269 if ( $labelAsPublish ) {
4270 $buttonLabelKey = $newPage ?
'publishpage' :
'publishchanges';
4272 $buttonLabelKey = $newPage ?
'savearticle' :
'savechanges';
4275 return $buttonLabelKey;
4290 $this->context->getConfig()->get(
'EditSubmitButtonLabelPublish' );
4293 $buttonTooltip = $labelAsPublish ?
'publish' :
'save';
4295 $buttons[
'save'] =
new OOUI\ButtonInputWidget( [
4298 'id' =>
'wpSaveWidget',
4299 'inputId' =>
'wpSave',
4301 'useInputTag' =>
true,
4302 'flags' => [
'progressive',
'primary' ],
4303 'label' => $buttonLabel,
4304 'infusable' =>
true,
4312 $buttons[
'preview'] =
new OOUI\ButtonInputWidget( [
4313 'name' =>
'wpPreview',
4315 'id' =>
'wpPreviewWidget',
4316 'inputId' =>
'wpPreview',
4318 'useInputTag' =>
true,
4319 'label' => $this->context->msg(
'showpreview' )->text(),
4320 'infusable' =>
true,
4328 $buttons[
'diff'] =
new OOUI\ButtonInputWidget( [
4331 'id' =>
'wpDiffWidget',
4332 'inputId' =>
'wpDiff',
4334 'useInputTag' =>
true,
4335 'label' => $this->context->msg(
'showdiff' )->text(),
4336 'infusable' =>
true,
4356 $out = $this->context->getOutput();
4357 $out->prepareErrorPage( $this->context->msg(
'nosuchsectiontitle' ) );
4359 $res = $this->context->msg(
'nosuchsectiontext', $this->section )->parseAsBlock();
4366 $out->returnToMain(
false, $this->mTitle );
4377 if ( is_array( $match ) ) {
4378 $match = $this->context->getLanguage()->listToText( $match );
4380 $out = $this->context->getOutput();
4381 $out->prepareErrorPage( $this->context->msg(
'spamprotectiontitle' ) );
4383 $out->addHTML(
'<div id="spamprotected">' );
4384 $out->addWikiMsg(
'spamprotectiontext' );
4388 $out->addHTML(
'</div>' );
4390 $out->wrapWikiMsg(
'<h2>$1</h2>',
"yourdiff" );
4393 $out->wrapWikiMsg(
'<h2>$1</h2>',
"yourtext" );
4410 return rtrim(
$request->getText( $field ) );
4430 $out = $this->context->getOutput();
4431 $editNotices = $this->mTitle->getEditNotices( $this->oldid );
4432 if (
count( $editNotices ) ) {
4433 $out->addHTML( implode(
"\n", $editNotices ) );
4435 $msg = $this->context->msg(
'editnotice-notext' );
4436 if ( !$msg->isDisabled() ) {
4438 '<div class="mw-editnotice-notext">'
4439 . $msg->parseAsBlock()
4450 if ( $this->mTitle->isTalkPage() ) {
4451 $this->context->getOutput()->addWikiMsg(
'talkpagetext' );
4459 if ( $this->contentLength ===
false ) {
4460 $this->contentLength = strlen( $this->textbox1 );
4463 $out = $this->context->getOutput();
4464 $lang = $this->context->getLanguage();
4465 $maxArticleSize = $this->context->getConfig()->get(
'MaxArticleSize' );
4466 if ( $this->tooBig || $this->contentLength > $maxArticleSize * 1024 ) {
4467 $out->wrapWikiMsg(
"<div class='error' id='mw-edit-longpageerror'>\n$1\n</div>",
4470 $lang->formatNum( round( $this->contentLength / 1024, 3 ) ),
4471 $lang->formatNum( $maxArticleSize )
4475 if ( !$this->context->msg(
'longpage-hint' )->isDisabled() ) {
4476 $out->wrapWikiMsg(
"<div id='mw-edit-longpage-hint'>\n$1\n</div>",
4479 $lang->formatSize( strlen( $this->textbox1 ) ),
4480 strlen( $this->textbox1 )
4491 $out = $this->context->getOutput();
4492 if ( $this->mTitle->isProtected(
'edit' ) &&
4495 # Is the title semi-protected?
4496 if ( $this->mTitle->isSemiProtected() ) {
4497 $noticeMsg =
'semiprotectedpagewarning';
4499 # Then it must be protected based on static groups (regular)
4500 $noticeMsg =
'protectedpagewarning';
4503 [
'lim' => 1,
'msgKey' => [ $noticeMsg ] ] );
4505 if ( $this->mTitle->isCascadeProtected() ) {
4506 # Is this page under cascading protection from some source pages?
4508 list( $cascadeSources, ) = $this->mTitle->getCascadeProtectionSources();
4509 $notice =
"<div class='mw-cascadeprotectedwarning'>\n$1\n";
4510 $cascadeSourcesCount =
count( $cascadeSources );
4511 if ( $cascadeSourcesCount > 0 ) {
4512 # Explain, and list the titles responsible
4513 foreach ( $cascadeSources
as $page ) {
4514 $notice .=
'* [[:' .
$page->getPrefixedText() .
"]]\n";
4517 $notice .=
'</div>';
4518 $out->wrapWikiMsg( $notice, [
'cascadeprotectedwarning', $cascadeSourcesCount ] );
4520 if ( !$this->mTitle->exists() && $this->mTitle->getRestrictions(
'create' ) ) {
4523 'showIfEmpty' =>
false,
4524 'msgKey' => [
'titleprotectedwarning' ],
4525 'wrap' =>
"<div class=\"mw-titleprotectedwarning\">\n$1</div>" ] );
4575 $userAgent = $this->context->getRequest()->getHeader(
'User-Agent' );
4576 if ( $userAgent && preg_match(
'/MSIE|Edge/', $userAgent ) ) {
4578 return $wgParser->guessLegacySectionNameFromWikiText( $text );
4581 return $wgParser->guessSectionNameFromWikiText( $text );
4591 $this->editConflictHelperFactory = $factory;
4592 $this->editConflictHelper =
null;
4599 if ( !$this->editConflictHelper ) {
4600 $this->editConflictHelper = call_user_func(
4601 $this->editConflictHelperFactory,
4617 MediaWikiServices::getInstance()->getStatsdDataFactory(),