27 use Wikimedia\ScopedCallback;
414 # Placeholders for text injection by hooks (must be HTML)
415 # extensions should take care to _append_ to the present value
487 $this->context->setTitle( $this->mTitle );
489 $this->contentModel = $this->mTitle->getContentModel();
492 $this->contentFormat =
$handler->getDefaultFormat();
493 $this->editConflictHelperFactory = [ $this,
'newTextConflictHelper' ];
525 $this->mContextTitle =
$title;
537 if ( is_null( $this->mContextTitle ) ) {
538 wfDeprecated( __METHOD__ .
' called with no title set',
'1.32' );
554 return $this->enableApiEditOverride ===
true ||
565 $this->enableApiEditOverride = $enableOverride;
589 if ( !
Hooks::run(
'AlternateEdit', [ $this ] ) ) {
593 wfDebug( __METHOD__ .
": enter\n" );
595 $request = $this->context->getRequest();
597 if (
$request->getBool(
'redlink' ) && $this->mTitle->exists() ) {
598 $this->context->getOutput()->redirect( $this->mTitle->getFullURL() );
603 $this->firsttime =
false;
608 $this->preview =
true;
612 $this->formtype =
'save';
613 } elseif ( $this->preview ) {
614 $this->formtype =
'preview';
615 } elseif ( $this->
diff ) {
616 $this->formtype =
'diff';
617 }
else { #
First time through
618 $this->firsttime =
true;
620 $this->formtype =
'preview';
622 $this->formtype =
'initial';
628 wfDebug( __METHOD__ .
": User can't edit\n" );
630 if ( $this->context->getUser()->getBlock() ) {
632 $this->context->getUser()->trackBlockWithCookie();
637 $this->context->getUser()->spreadAnyEditBlock();
646 $revision = $this->mArticle->getRevisionFetched();
653 if ( $this->undidRev ) {
655 $prevRev = $undidRevObj ? $undidRevObj->getPrevious() :
null;
657 if ( !$this->undidRev
664 'contentmodelediterror',
665 $revision->getContentModel(),
673 $this->isConflict =
false;
675 # Show applicable editing introductions
676 if ( $this->formtype ==
'initial' || $this->firsttime ) {
680 # Attempt submission here. This will check for edit conflicts,
681 # and redundantly check for locked database, blocked IPs, etc.
682 # that edit() already checked just in case someone tries to sneak
683 # in the back door with a hand-edited submission URL.
685 if ( $this->formtype ==
'save' ) {
686 $resultDetails =
null;
693 # First time through: get contents, set time for conflict
695 if ( $this->formtype ==
'initial' || $this->firsttime ) {
697 $out = $this->context->getOutput();
698 if (
$out->getRedirect() ===
'' ) {
704 if ( !$this->mTitle->getArticleID() ) {
705 Hooks::run(
'EditFormPreloadText', [ &$this->textbox1, &$this->mTitle ] );
707 Hooks::run(
'EditFormInitialText', [ $this ] );
720 $user = $this->context->getUser();
721 $permErrors = $this->mTitle->getUserPermissionsErrors(
'edit',
$user, $rigor );
722 # Can this title be created?
723 if ( !$this->mTitle->exists() ) {
724 $permErrors = array_merge(
727 $this->mTitle->getUserPermissionsErrors(
'create',
$user, $rigor ),
732 # Ignore some permissions errors when a user is just previewing/viewing diffs
734 foreach ( $permErrors
as $error ) {
735 if ( ( $this->preview || $this->
diff )
737 $error[0] ==
'blockedtext' ||
738 $error[0] ==
'autoblockedtext' ||
739 $error[0] ==
'systemblockedtext'
764 $out = $this->context->getOutput();
765 if ( $this->context->getRequest()->getBool(
'redlink' ) ) {
769 $out->redirect( $this->mTitle->getFullURL() );
775 # Use the normal message if there's nothing to display
777 $action = $this->mTitle->exists() ?
'edit' :
778 ( $this->mTitle->isTalkPage() ?
'createtalk' :
'createpage' );
784 $out->formatPermissionsErrorMessage( $permErrors,
'edit' )
794 $out = $this->context->getOutput();
795 Hooks::run(
'EditPage::showReadOnlyForm:initial', [ $this, &
$out ] );
797 $out->setRobotPolicy(
'noindex,nofollow' );
798 $out->setPageTitle( $this->context->msg(
800 $this->getContextTitle()->getPrefixedText()
803 $out->addHTML( $this->editFormPageTop );
804 $out->addHTML( $this->editFormTextTop );
806 if ( $errorMessage !==
'' ) {
807 $out->addWikiTextAsInterface( $errorMessage );
808 $out->addHTML(
"<hr />\n" );
811 # If the user made changes, preserve them when showing the markup
812 # (This happens when a user is blocked during edit, for instance)
813 if ( !$this->firsttime ) {
815 $out->addWikiMsg(
'viewyourtext' );
820 # Serialize using the default format if the content model is not supported
821 # (e.g. for an old revision with a different model)
824 $out->addWikiMsg(
'viewsourcetext' );
827 $out->addHTML( $this->editFormTextBeforeContent );
828 $this->
showTextbox( $text,
'wpTextbox1', [
'readonly' ] );
829 $out->addHTML( $this->editFormTextAfterContent );
833 $out->addModules(
'mediawiki.action.edit.collapsibleFooter' );
835 $out->addHTML( $this->editFormTextBottom );
836 if ( $this->mTitle->exists() ) {
837 $out->returnToMain(
null, $this->mTitle );
847 $config = $this->context->getConfig();
848 $previewOnOpenNamespaces = $config->get(
'PreviewOnOpenNamespaces' );
849 $request = $this->context->getRequest();
850 if ( $config->get(
'RawHtml' ) ) {
856 if (
$request->getVal(
'preview' ) ==
'yes' ) {
859 } elseif (
$request->getVal(
'preview' ) ==
'no' ) {
862 } elseif ( $this->section ==
'new' ) {
865 } elseif ( (
$request->getCheck(
'preload' ) || $this->mTitle->exists() )
866 && $this->context->getUser()->getOption(
'previewonfirst' )
870 } elseif ( !$this->mTitle->exists()
871 && isset( $previewOnOpenNamespaces[$this->mTitle->getNamespace()] )
872 && $previewOnOpenNamespaces[$this->mTitle->getNamespace()]
888 if ( $this->mTitle->isUserConfigPage() ) {
889 $name = $this->mTitle->getSkinFromConfigSubpage();
890 $skins = array_merge(
894 return !in_array(
$name, $skins )
895 && in_array( strtolower(
$name ), $skins );
910 return $contentHandler->supportsSections();
919 # Section edit can come from either the form or a link
920 $this->section =
$request->getVal(
'wpSection',
$request->getVal(
'section' ) );
923 throw new ErrorPageError(
'sectioneditnotsupported-title',
'sectioneditnotsupported-text' );
926 $this->isNew = !$this->mTitle->exists() || $this->section ==
'new';
929 # These fields need to be checked for encoding.
930 # Also remove trailing whitespace, but don't remove _initial_
931 # whitespace from the text boxes. This may be significant formatting.
932 $this->textbox1 = rtrim(
$request->getText(
'wpTextbox1' ) );
933 if ( !
$request->getCheck(
'wpTextbox2' ) ) {
943 $this->unicodeCheck =
$request->getText(
'wpUnicodeCheck' );
945 $this->summary =
$request->getText(
'wpSummary' );
947 # If the summary consists of a heading, e.g. '==Foobar==', extract the title from the
948 # header syntax, e.g. 'Foobar'. This is mainly an issue when we are using wpSummary for
950 $this->summary = preg_replace(
'/^\s*=+\s*(.*?)\s*=+\s*$/',
'$1', $this->summary );
952 # Treat sectiontitle the same way as summary.
953 # Note that wpSectionTitle is not yet a part of the actual edit form, as wpSummary is
954 # currently doing double duty as both edit summary and section title. Right now this
955 # is just to allow API edits to work around this limitation, but this should be
956 # incorporated into the actual edit form when EditPage is rewritten (T20654, T28312).
957 $this->sectiontitle =
$request->getText(
'wpSectionTitle' );
958 $this->sectiontitle = preg_replace(
'/^\s*=+\s*(.*?)\s*=+\s*$/',
'$1', $this->sectiontitle );
960 $this->edittime =
$request->getVal(
'wpEdittime' );
961 $this->editRevId =
$request->getIntOrNull(
'editRevId' );
962 $this->starttime =
$request->getVal(
'wpStarttime' );
969 $this->scrolltop =
$request->getIntOrNull(
'wpScrolltop' );
971 if ( $this->textbox1 ===
'' && !
$request->getCheck(
'wpTextbox1' ) ) {
975 $this->incompleteForm =
true;
979 $this->incompleteForm = !
$request->getVal(
'wpUltimateParam' );
981 if ( $this->incompleteForm ) {
982 # If the form is incomplete, force to preview.
983 wfDebug( __METHOD__ .
": Form data appears to be incomplete\n" );
985 $this->preview =
true;
987 $this->preview =
$request->getCheck(
'wpPreview' );
995 # Some browsers will not report any submit button
996 # if the user hits enter in the comment box.
997 # The unmarked state will be assumed to be a save,
998 # if the form seems otherwise complete.
999 wfDebug( __METHOD__ .
": Passed token check.\n" );
1000 } elseif ( $this->
diff ) {
1001 # Failed token check, but only requested "Show Changes".
1002 wfDebug( __METHOD__ .
": Failed token check; Show Changes requested.\n" );
1004 # Page might be a hack attempt posted from
1005 # an external site. Preview instead of saving.
1006 wfDebug( __METHOD__ .
": Failed token check; forcing preview\n" );
1007 $this->preview =
true;
1011 if ( !preg_match(
'/^\d{14}$/', $this->edittime ) ) {
1012 $this->edittime =
null;
1015 if ( !preg_match(
'/^\d{14}$/', $this->starttime ) ) {
1016 $this->starttime =
null;
1019 $this->recreate =
$request->getCheck(
'wpRecreate' );
1021 $this->minoredit =
$request->getCheck(
'wpMinoredit' );
1022 $this->watchthis =
$request->getCheck(
'wpWatchthis' );
1024 $user = $this->context->getUser();
1025 # Don't force edit summaries when a user is editing their own user or talk page
1026 if ( ( $this->mTitle->mNamespace ==
NS_USER || $this->mTitle->mNamespace ==
NS_USER_TALK )
1027 && $this->mTitle->getText() ==
$user->getName()
1029 $this->allowBlankSummary =
true;
1031 $this->allowBlankSummary =
$request->getBool(
'wpIgnoreBlankSummary' )
1032 || !
$user->getOption(
'forceeditsummary' );
1035 $this->autoSumm =
$request->getText(
'wpAutoSummary' );
1037 $this->allowBlankArticle =
$request->getBool(
'wpIgnoreBlankArticle' );
1038 $this->allowSelfRedirect =
$request->getBool(
'wpIgnoreSelfRedirect' );
1042 $this->changeTags = [];
1044 $this->changeTags = array_filter( array_map(
'trim', explode(
',',
1048 # Not a posted form? Start with nothing.
1049 wfDebug( __METHOD__ .
": Not a posted form.\n" );
1050 $this->textbox1 =
'';
1051 $this->summary =
'';
1052 $this->sectiontitle =
'';
1053 $this->edittime =
'';
1054 $this->editRevId =
null;
1056 $this->
edit =
false;
1057 $this->preview =
false;
1058 $this->
save =
false;
1059 $this->
diff =
false;
1060 $this->minoredit =
false;
1062 $this->watchthis =
$request->getBool(
'watchthis',
false );
1063 $this->recreate =
false;
1067 if ( $this->section ==
'new' &&
$request->getVal(
'preloadtitle' ) ) {
1068 $this->sectiontitle =
$request->getVal(
'preloadtitle' );
1070 $this->summary =
$request->getVal(
'preloadtitle' );
1071 } elseif ( $this->section !=
'new' &&
$request->getVal(
'summary' ) !==
'' ) {
1072 $this->summary =
$request->getText(
'summary' );
1073 if ( $this->summary !==
'' ) {
1074 $this->hasPresetSummary =
true;
1078 if (
$request->getVal(
'minor' ) ) {
1079 $this->minoredit =
true;
1083 $this->oldid =
$request->getInt(
'oldid' );
1084 $this->parentRevId =
$request->getInt(
'parentRevId' );
1086 $this->bot =
$request->getBool(
'bot',
true );
1087 $this->nosummary =
$request->getBool(
'nosummary' );
1090 $this->contentModel =
$request->getText(
'model', $this->contentModel );
1092 $this->contentFormat =
$request->getText(
'format', $this->contentFormat );
1098 'editpage-invalidcontentmodel-title',
1099 'editpage-invalidcontentmodel-text',
1104 if ( !
$handler->isSupportedFormat( $this->contentFormat ) ) {
1106 'editpage-notsupportedcontentformat-title',
1107 'editpage-notsupportedcontentformat-text',
1121 $this->editintro =
$request->getText(
'editintro',
1123 $this->section ===
'new' ?
'MediaWiki:addsection-editintro' :
'' );
1148 $this->edittime = $this->
page->getTimestamp();
1149 $this->editRevId = $this->
page->getLatest();
1157 $user = $this->context->getUser();
1159 # Sort out the "watch" checkbox
1160 if (
$user->getOption(
'watchdefault' ) ) {
1162 $this->watchthis =
true;
1163 } elseif (
$user->getOption(
'watchcreations' ) && !$this->mTitle->exists() ) {
1165 $this->watchthis =
true;
1166 } elseif (
$user->isWatched( $this->mTitle ) ) {
1168 $this->watchthis =
true;
1171 $this->minoredit =
true;
1173 if ( $this->textbox1 ===
false ) {
1189 $user = $this->context->getUser();
1190 $request = $this->context->getRequest();
1193 if ( !$this->mTitle->exists() || $this->section ==
'new' ) {
1194 if ( $this->mTitle->getNamespace() ==
NS_MEDIAWIKI && $this->section !=
'new' ) {
1195 # If this is a system message, get the default text.
1196 $msg = $this->mTitle->getDefaultMessageText();
1201 # If requested, preload some text.
1202 $preload =
$request->getVal(
'preload',
1204 $this->section ===
'new' ?
'MediaWiki:addsection-preload' :
'' );
1210 } elseif ( $this->section !=
'' ) {
1213 $content = $orig ? $orig->getSection( $this->section ) :
null;
1219 $undoafter =
$request->getInt(
'undoafter' );
1220 $undo =
$request->getInt(
'undo' );
1222 if ( $undo > 0 && $undoafter > 0 ) {
1227 # Sanity check, make sure it's the right page,
1228 # the revisions exist and they were not deleted.
1229 # Otherwise, $content will be left as-is.
1230 if ( !is_null( $undorev ) && !is_null( $oldrev ) &&
1238 $this->context->getOutput()->redirect( $this->mTitle->getFullURL( [
1239 'action' =>
'mcrundo',
1241 'undoafter' => $undoafter,
1245 $content = $this->
page->getUndoContent( $undorev, $oldrev );
1248 # Warn the user that something went wrong
1249 $undoMsg =
'failure';
1253 if ( $undoMsg ===
null ) {
1256 $user, MediaWikiServices::getInstance()->getContentLanguage() );
1257 $newContent =
$content->preSaveTransform( $this->mTitle,
$user, $popts );
1258 if ( $newContent->getModel() !== $oldContent->getModel() ) {
1263 $this->contentModel = $newContent->getModel();
1264 $this->contentFormat = $oldrev->getContentFormat();
1267 if ( $newContent->equals( $oldContent ) ) {
1268 # Tell the user that the undo results in no change,
1269 # i.e. the revisions were already undone.
1270 $undoMsg =
'nochange';
1273 # Inform the user of our success and set an automatic edit summary
1274 $undoMsg =
'success';
1276 # If we just undid one rev, use an autosummary
1277 $firstrev = $oldrev->getNext();
1278 if ( $firstrev && $firstrev->getId() == $undo ) {
1279 $userText = $undorev->getUserText();
1280 if ( $userText ===
'' ) {
1281 $undoSummary = $this->context->msg(
1282 'undo-summary-username-hidden',
1284 )->inContentLanguage()->text();
1286 $undoSummary = $this->context->msg(
1290 )->inContentLanguage()->text();
1292 if ( $this->summary ===
'' ) {
1293 $this->summary = $undoSummary;
1295 $this->summary = $undoSummary . $this->context->msg(
'colon-separator' )
1298 $this->undidRev = $undo;
1300 $this->formtype =
'diff';
1310 $out = $this->context->getOutput();
1313 $class = ( $undoMsg ==
'success' ?
'' :
'error ' ) .
"mw-undo-{$undoMsg}";
1314 $this->editFormPageTop .= Html::rawElement(
1315 'div', [
'class' => $class ],
1316 $out->parseAsInterface(
1317 $this->context->msg(
'undo-' . $undoMsg )->plain()
1326 $curRevision = $this->
page->getRevision();
1327 $oldRevision = $this->mArticle->getRevisionFetched();
1331 && $curRevision->getId() !== $oldRevision->getId()
1335 $this->context->getOutput()->redirect(
1336 $this->mTitle->getFullURL(
1338 'action' =>
'mcrrestore',
1339 'restore' => $oldRevision->getId(),
1372 if ( $this->section ==
'new' ) {
1375 $revision = $this->mArticle->getRevisionFetched();
1376 if ( $revision ===
null ) {
1378 return $handler->makeEmptyContent();
1397 if ( $this->parentRevId ) {
1400 return $this->mArticle->getRevIdFetched();
1418 return $handler->makeEmptyContent();
1419 } elseif ( !$this->undidRev ) {
1424 $logger = LoggerFactory::getInstance(
'editpage' );
1425 if ( $this->contentModel !==
$rev->getContentModel() ) {
1426 $logger->warning(
"Overriding content model from current edit {prev} to {new}", [
1427 'prev' => $this->contentModel,
1428 'new' =>
$rev->getContentModel(),
1429 'title' => $this->
getTitle()->getPrefixedDBkey(),
1430 'method' => __METHOD__
1432 $this->contentModel =
$rev->getContentModel();
1437 if ( !
$content->isSupportedFormat( $this->contentFormat ) ) {
1438 $logger->warning(
"Current revision content format unsupported. Overriding {prev} to {new}", [
1440 'prev' => $this->contentFormat,
1441 'new' =>
$rev->getContentFormat(),
1442 'title' => $this->
getTitle()->getPrefixedDBkey(),
1443 'method' => __METHOD__
1445 $this->contentFormat =
$rev->getContentFormat();
1474 if ( !empty( $this->mPreloadContent ) ) {
1480 if ( $preload ===
'' ) {
1481 return $handler->makeEmptyContent();
1484 $user = $this->context->getUser();
1486 # Check for existence to avoid getting MediaWiki:Noarticletext
1489 return $handler->makeEmptyContent();
1498 return $handler->makeEmptyContent();
1508 return $handler->makeEmptyContent();
1514 if ( !$converted ) {
1516 wfDebug(
"Attempt to preload incompatible content: " .
1517 "can't convert " .
$content->getModel() .
1520 return $handler->makeEmptyContent();
1537 $token =
$request->getVal(
'wpEditToken' );
1538 $user = $this->context->getUser();
1539 $this->mTokenOk =
$user->matchEditToken( $token );
1540 $this->mTokenOkExceptSuffix =
$user->matchEditTokenNoSuffix( $token );
1559 $revisionId = $this->
page->getLatest();
1560 $postEditKey = self::POST_EDIT_COOKIE_KEY_PREFIX . $revisionId;
1563 if ( $statusValue == self::AS_SUCCESS_NEW_ARTICLE ) {
1565 } elseif ( $this->oldid ) {
1569 $response = $this->context->getRequest()->response();
1570 $response->setCookie( $postEditKey, $val, time() + self::POST_EDIT_COOKIE_DURATION );
1585 $bot = $this->context->getUser()->isAllowed(
'bot' ) &&
$this->bot;
1588 Hooks::run(
'EditPage::attemptSave:after', [ $this,
$status, $resultDetails ] );
1597 if ( $this->context->getRequest()->getText(
'mode' ) !==
'conflict' ) {
1618 if (
$status->value == self::AS_SUCCESS_UPDATE
1619 ||
$status->value == self::AS_SUCCESS_NEW_ARTICLE
1623 $this->didSave =
true;
1624 if ( !$resultDetails[
'nullEdit'] ) {
1629 $out = $this->context->getOutput();
1633 $request = $this->context->getRequest();
1634 $extraQueryRedirect =
$request->getVal(
'wpExtraQueryRedirect' );
1655 $out->wrapWikiTextAsInterface(
'error',
$status->getWikiText() );
1659 $query = $resultDetails[
'redirect'] ?
'redirect=no' :
'';
1660 if ( $extraQueryRedirect ) {
1664 $query .= $extraQueryRedirect;
1666 $anchor = $resultDetails[
'sectionanchor'] ??
'';
1667 $out->redirect( $this->mTitle->getFullURL(
$query ) . $anchor );
1672 $sectionanchor = $resultDetails[
'sectionanchor'];
1676 'ArticleUpdateBeforeRedirect',
1677 [ $this->mArticle, &$sectionanchor, &$extraQuery ]
1680 if ( $resultDetails[
'redirect'] ) {
1681 if ( $extraQuery !==
'' ) {
1682 $extraQuery =
'&' . $extraQuery;
1684 $extraQuery =
'redirect=no' . $extraQuery;
1686 if ( $extraQueryRedirect ) {
1687 if ( $extraQuery !==
'' ) {
1690 $extraQuery .= $extraQueryRedirect;
1693 $out->redirect( $this->mTitle->getFullURL( $extraQuery ) . $sectionanchor );
1718 $permission = $this->mTitle->isTalkPage() ?
'createtalk' :
'createpage';
1729 $this->hookError =
'<div class="error">' .
"\n" .
$status->getWikiText() .
1746 if ( $this->hookError !=
'' ) {
1747 # ...or the hook could be expecting us to produce an error
1748 $status->fatal(
'hookaborted' );
1756 $user, $this->minoredit ] )
1758 # Error messages etc. could be handled within the hook...
1760 $status->fatal(
'hookaborted' );
1773 } elseif ( !
$status->isOK() ) {
1774 # ...or the hook could be expecting us to produce an error
1777 $status->fatal(
'hookaborted' );
1792 $errmsg =
$status->getWikiText(
1795 $this->context->getLanguage()
1798 <
div class=
"errorbox">
1814 if ( $this->sectiontitle !==
'' ) {
1819 if ( $this->summary ===
'' ) {
1820 $cleanSectionTitle =
$wgParser->stripSectionName( $this->sectiontitle );
1821 return $this->context->msg(
'newsectionsummary' )
1822 ->plaintextParams( $cleanSectionTitle )->inContentLanguage()->text();
1824 } elseif ( $this->summary !==
'' ) {
1826 # This is a new section, so create a link to the new section
1827 # in the revision summary.
1828 $cleanSummary =
$wgParser->stripSectionName( $this->summary );
1829 return $this->context->msg(
'newsectionsummary' )
1830 ->plaintextParams( $cleanSummary )->inContentLanguage()->text();
1861 $user = $this->context->getUser();
1863 if ( !
Hooks::run(
'EditPage::attemptSave', [ $this ] ) ) {
1864 wfDebug(
"Hook 'EditPage::attemptSave' aborted article saving\n" );
1865 $status->fatal(
'hookaborted' );
1870 if ( $this->unicodeCheck !== self::UNICODE_CHECK ) {
1871 $status->fatal(
'unicode-support-fail' );
1876 $request = $this->context->getRequest();
1877 $spam =
$request->getText(
'wpAntispam' );
1878 if ( $spam !==
'' ) {
1883 $this->mTitle->getPrefixedText() .
1884 '" submitted bogus field "' .
1888 $status->fatal(
'spamprotectionmatch',
false );
1894 # Construct Content object
1898 'content-failed-to-parse',
1899 $this->contentModel,
1900 $this->contentFormat,
1907 # Check image redirect
1908 if ( $this->mTitle->getNamespace() ==
NS_FILE &&
1909 $textbox_content->isRedirect() &&
1910 !
$user->isAllowed(
'upload' )
1920 if ( $match ===
false && $this->section ==
'new' ) {
1921 # $wgSpamRegex is enforced on this new heading/summary because, unlike
1922 # regular summaries, it is added to the actual wikitext.
1923 if ( $this->sectiontitle !==
'' ) {
1924 # This branch is taken when the API is used with the 'sectiontitle' parameter.
1927 # This branch is taken when the "Add Topic" user interface is used, or the API
1928 # is used with the 'summary' parameter.
1932 if ( $match ===
false ) {
1935 if ( $match !==
false ) {
1938 $pdbk = $this->mTitle->getPrefixedDBkey();
1939 $match = str_replace(
"\n",
'', $match );
1940 wfDebugLog(
'SpamRegex',
"$ip spam regex hit [[$pdbk]]: \"$match\"" );
1941 $status->fatal(
'spamprotectionmatch', $match );
1947 [ $this, $this->textbox1, $this->section, &$this->hookError, $this->summary ] )
1949 # Error messages etc. could be handled within the hook...
1950 $status->fatal(
'hookaborted' );
1953 } elseif ( $this->hookError !=
'' ) {
1954 # ...or the hook could be expecting us to produce an error
1955 $status->fatal(
'hookaborted' );
1960 if (
$user->isBlockedFrom( $this->mTitle ) ) {
1963 $user->spreadAnyEditBlock();
1965 # Check block state against master, thus 'false'.
1966 $status->setResult(
false, self::AS_BLOCKED_PAGE_FOR_USER );
1970 $this->contentLength = strlen( $this->textbox1 );
1971 $config = $this->context->getConfig();
1972 $maxArticleSize = $config->get(
'MaxArticleSize' );
1973 if ( $this->contentLength > $maxArticleSize * 1024 ) {
1975 $this->tooBig =
true;
1976 $status->setResult(
false, self::AS_CONTENT_TOO_BIG );
1980 if ( !
$user->isAllowed(
'edit' ) ) {
1981 if (
$user->isAnon() ) {
1982 $status->setResult(
false, self::AS_READ_ONLY_PAGE_ANON );
1985 $status->fatal(
'readonlytext' );
1991 $changingContentModel =
false;
1992 if ( $this->contentModel !== $this->mTitle->getContentModel() ) {
1993 if ( !$config->get(
'ContentHandlerUseDB' ) ) {
1994 $status->fatal(
'editpage-cannot-use-custom-model' );
1997 } elseif ( !
$user->isAllowed(
'editcontentmodel' ) ) {
1998 $status->setResult(
false, self::AS_NO_CHANGE_CONTENT_MODEL );
2004 if ( !$titleWithNewContentModel->userCan(
'editcontentmodel',
$user )
2005 || !$titleWithNewContentModel->userCan(
'edit',
$user )
2007 $status->setResult(
false, self::AS_NO_CHANGE_CONTENT_MODEL );
2011 $changingContentModel =
true;
2012 $oldContentModel = $this->mTitle->getContentModel();
2015 if ( $this->changeTags ) {
2017 $this->changeTags,
$user );
2018 if ( !$changeTagsStatus->isOK() ) {
2020 return $changeTagsStatus;
2025 $status->fatal(
'readonlytext' );
2029 if (
$user->pingLimiter() ||
$user->pingLimiter(
'linkpurge', 0 )
2030 || ( $changingContentModel &&
$user->pingLimiter(
'editcontentmodel' ) )
2032 $status->fatal(
'actionthrottledtext' );
2037 # If the article has been deleted while editing, don't save it without
2040 $status->setResult(
false, self::AS_ARTICLE_WAS_DELETED );
2044 # Load the page data from the master. If anything changes in the meantime,
2045 # we detect it by using page_latest like a token in a 1 try compare-and-swap.
2046 $this->
page->loadPageData(
'fromdbmaster' );
2047 $new = !$this->
page->exists();
2051 if ( !$this->mTitle->userCan(
'create',
$user ) ) {
2052 $status->fatal(
'nocreatetext' );
2054 wfDebug( __METHOD__ .
": no create permission\n" );
2061 $defaultMessageText = $this->mTitle->getDefaultMessageText();
2062 if ( $this->mTitle->getNamespace() ===
NS_MEDIAWIKI && $defaultMessageText !==
false ) {
2063 $defaultText = $defaultMessageText;
2068 if ( !$this->allowBlankArticle && $this->textbox1 === $defaultText ) {
2069 $this->blankArticle =
true;
2070 $status->fatal(
'blankarticle' );
2071 $status->setResult(
false, self::AS_BLANK_ARTICLE );
2081 $result[
'sectionanchor'] =
'';
2082 if ( $this->section ==
'new' ) {
2083 if ( $this->sectiontitle !==
'' ) {
2086 } elseif ( $this->summary !==
'' ) {
2097 # Article exists. Check for edit conflict.
2099 $this->
page->clear(); # Force reload
of dates,
etc.
2100 $timestamp = $this->
page->getTimestamp();
2101 $latest = $this->
page->getLatest();
2103 wfDebug(
"timestamp: {$timestamp}, edittime: {$this->edittime}\n" );
2109 if ( $timestamp != $this->edittime
2110 || ( $this->editRevId !==
null && $this->editRevId != $latest )
2112 $this->isConflict =
true;
2113 if ( $this->section ==
'new' ) {
2114 if ( $this->
page->getUserText() ==
$user->getName() &&
2121 .
": duplicate new section submission; trigger edit conflict!\n" );
2124 $this->isConflict =
false;
2125 wfDebug( __METHOD__ .
": conflict suppressed; new section\n" );
2127 } elseif ( $this->section ==
''
2129 DB_MASTER, $this->mTitle->getArticleID(),
2133 # Suppress edit conflict with self, except for section edits where merging is required.
2134 wfDebug( __METHOD__ .
": Suppressing edit conflict, same user.\n" );
2135 $this->isConflict =
false;
2140 if ( $this->sectiontitle !==
'' ) {
2148 if ( $this->isConflict ) {
2150 .
": conflict! getting section '{$this->section}' for time '{$this->edittime}'"
2151 .
" (id '{$this->editRevId}') (article time '{$timestamp}')\n" );
2154 if ( $this->editRevId !==
null ) {
2170 wfDebug( __METHOD__ .
": getting section '{$this->section}'\n" );
2179 wfDebug( __METHOD__ .
": activating conflict; section replace failed.\n" );
2180 $this->isConflict =
true;
2182 } elseif ( $this->isConflict ) {
2186 $this->isConflict =
false;
2187 wfDebug( __METHOD__ .
": Suppressing edit conflict, successful merge.\n" );
2189 $this->section =
'';
2191 wfDebug( __METHOD__ .
": Keeping edit conflict, failed merge.\n" );
2195 if ( $this->isConflict ) {
2196 $status->setResult(
false, self::AS_CONFLICT_DETECTED );
2204 if ( $this->section ==
'new' ) {
2206 if ( !$this->allowBlankSummary && trim( $this->summary ) ==
'' ) {
2207 $this->missingSummary =
true;
2208 $status->fatal(
'missingsummary' );
2214 if ( $this->textbox1 ==
'' ) {
2215 $this->missingComment =
true;
2216 $status->fatal(
'missingcommenttext' );
2220 } elseif ( !$this->allowBlankSummary
2225 $this->missingSummary =
true;
2226 $status->fatal(
'missingsummary' );
2232 $sectionanchor =
'';
2233 if ( $this->section ==
'new' ) {
2235 } elseif ( $this->section !=
'' ) {
2236 # Try to get a section anchor from the section source, redirect
2237 # to edited section if header found.
2238 # XXX: Might be better to integrate this into Article::replaceSectionAtRev
2239 # for duplicate heading checking and maybe parsing.
2240 $hasmatch = preg_match(
"/^ *([=]{1,6})(.*?)(\\1) *\\n/i", $this->textbox1,
$matches );
2241 # We can't deal with anchors, includes, html etc in the header for now,
2242 # headline would need to be parsed to improve this.
2243 if ( $hasmatch && strlen(
$matches[2] ) > 0 ) {
2247 $result[
'sectionanchor'] = $sectionanchor;
2254 $this->section =
'';
2259 if ( !$this->allowSelfRedirect
2265 if ( !$currentTarget || !$currentTarget->equals( $this->getTitle() ) ) {
2266 $this->selfRedirect =
true;
2267 $status->fatal(
'selfredirect' );
2275 if ( $this->contentLength > $maxArticleSize * 1024 ) {
2276 $this->tooBig =
true;
2277 $status->setResult(
false, self::AS_MAX_ARTICLE_SIZE_EXCEEDED );
2283 ( ( $this->minoredit && !$this->isNew ) ?
EDIT_MINOR : 0 ) |
2286 $doEditStatus = $this->
page->doEditContent(
2297 if ( !$doEditStatus->isOK() ) {
2301 $errors = $doEditStatus->getErrorsArray();
2302 if ( in_array( $errors[0][0],
2303 [
'edit-gone-missing',
'edit-conflict',
'edit-already-exists' ] )
2305 $this->isConflict =
true;
2309 return $doEditStatus;
2312 $result[
'nullEdit'] = $doEditStatus->hasMessage(
'edit-no-change' );
2315 $user->pingLimiter(
'linkpurge' );
2322 if ( $changingContentModel ) {
2325 $new ?
false : $oldContentModel,
2326 $this->contentModel,
2341 $new = $oldModel ===
false;
2342 $log =
new ManualLogEntry(
'contentmodel', $new ?
'new' :
'change' );
2343 $log->setPerformer(
$user );
2344 $log->setTarget( $this->mTitle );
2345 $log->setComment( $reason );
2346 $log->setParameters( [
2347 '4::oldmodel' => $oldModel,
2348 '5::newmodel' => $newModel
2350 $logid = $log->insert();
2351 $log->publish( $logid );
2358 $user = $this->context->getUser();
2359 if ( !
$user->isLoggedIn() ) {
2391 $baseContent = $baseRevision ? $baseRevision->getContent() :
null;
2393 if ( is_null( $baseContent ) ) {
2399 $currentContent = $currentRevision ? $currentRevision->getContent() :
null;
2401 if ( is_null( $currentContent ) ) {
2407 $result =
$handler->merge3( $baseContent, $editContent, $currentContent );
2412 $this->parentRevId = $currentRevision->getId();
2432 if ( !$this->mBaseRevision ) {
2434 $this->mBaseRevision = $this->editRevId
2474 foreach ( $regexes
as $regex ) {
2476 if ( preg_match( $regex, $text,
$matches ) ) {
2484 $out = $this->context->getOutput();
2486 $out->addModules(
'mediawiki.action.edit' );
2487 $out->addModuleStyles(
'mediawiki.action.edit.styles' );
2488 $out->addModuleStyles(
'mediawiki.editfont.styles' );
2490 $user = $this->context->getUser();
2492 if (
$user->getOption(
'uselivepreview' ) ) {
2493 $out->addModules(
'mediawiki.action.edit.preview' );
2496 if (
$user->getOption(
'useeditwarning' ) ) {
2497 $out->addModules(
'mediawiki.action.edit.editWarning' );
2500 # Enabled article-related sidebar, toplinks, etc.
2501 $out->setArticleRelated(
true );
2504 if ( $this->isConflict ) {
2505 $msg =
'editconflict';
2506 } elseif ( $contextTitle->exists() && $this->section !=
'' ) {
2507 $msg = $this->section ==
'new' ?
'editingcomment' :
'editingsection';
2509 $msg = $contextTitle->exists()
2511 && $contextTitle->getDefaultMessageText() !==
false
2517 # Use the title defined by DISPLAYTITLE magic word when present
2518 # NOTE: getDisplayTitle() returns HTML while getPrefixedText() returns plain text.
2519 # setPageTitle() treats the input as wikitext, which should be safe in either case.
2520 $displayTitle = isset( $this->mParserOutput ) ? $this->mParserOutput->getDisplayTitle() :
false;
2521 if ( $displayTitle ===
false ) {
2522 $displayTitle = $contextTitle->getPrefixedText();
2524 $out->setDisplayTitle( $displayTitle );
2526 $out->setPageTitle( $this->context->msg( $msg, $displayTitle ) );
2528 $config = $this->context->getConfig();
2530 # Transmit the name of the message to JavaScript for live preview
2531 # Keep Resources.php/mediawiki.action.edit.preview in sync with the possible keys
2532 $out->addJsConfigVars( [
2533 'wgEditMessage' => $msg,
2534 'wgAjaxEditStash' => $config->get(
'AjaxEditStash' ),
2539 $out->addJsConfigVars(
2540 'wgEditSubmitButtonLabelPublish',
2541 $config->get(
'EditSubmitButtonLabelPublish' )
2549 if ( $this->suppressIntro ) {
2553 $out = $this->context->getOutput();
2554 $namespace = $this->mTitle->getNamespace();
2557 # Show a warning if editing an interface message
2558 $out->wrapWikiMsg(
"<div class='mw-editinginterface'>\n$1\n</div>",
'editinginterface' );
2559 # If this is a default message (but not css, json, or js),
2560 # show a hint that it is translatable on translatewiki.net
2566 $defaultMessageText = $this->mTitle->getDefaultMessageText();
2567 if ( $defaultMessageText !==
false ) {
2568 $out->wrapWikiMsg(
"<div class='mw-translateinterface'>\n$1\n</div>",
2569 'translateinterface' );
2572 } elseif ( $namespace ==
NS_FILE ) {
2573 # Show a hint to shared repo
2576 $descUrl =
$file->getDescriptionUrl();
2577 # there must be a description url to show a hint to shared repo
2579 if ( !$this->mTitle->exists() ) {
2580 $out->wrapWikiMsg(
"<div class=\"mw-sharedupload-desc-create\">\n$1\n</div>", [
2581 'sharedupload-desc-create',
$file->getRepo()->getDisplayName(), $descUrl
2584 $out->wrapWikiMsg(
"<div class=\"mw-sharedupload-desc-edit\">\n$1\n</div>", [
2585 'sharedupload-desc-edit',
$file->getRepo()->getDisplayName(), $descUrl
2592 # Show a warning message when someone creates/edits a user (talk) page but the user does not exist
2593 # Show log extract when the user is currently blocked
2595 $username = explode(
'/', $this->mTitle->getText(), 2 )[0];
2600 $out->wrapWikiMsg(
"<div class=\"mw-userpage-userdoesnotexist error\">\n$1\n</div>",
2603 !is_null( $block ) &&
2605 ( $block->isSitewide() ||
$user->isBlockedFrom( $this->mTitle ) )
2616 'showIfEmpty' =>
false,
2618 'blocked-notice-logextract',
2619 $user->getName() # Support GENDER
in notice
2625 # Try to add a custom edit intro, or use the standard one if this is not possible.
2628 $this->context->msg(
'helppage' )->inContentLanguage()->text()
2630 if ( $this->context->getUser()->isLoggedIn() ) {
2633 "<div class=\"mw-newarticletext plainlinks\">\n$1\n</div>",
2642 "<div class=\"mw-newarticletextanon plainlinks\">\n$1\n</div>",
2644 'newarticletextanon',
2650 # Give a notice if the user is editing a deleted/moved page...
2651 if ( !$this->mTitle->exists() ) {
2658 'conds' => [
'log_action != ' .
$dbr->addQuotes(
'revision' ) ],
2659 'showIfEmpty' =>
false,
2660 'msgKey' => [
'recreate-moveddeleted-warn' ]
2672 if ( $this->editintro ) {
2676 $this->context->getOutput()->addWikiTextAsContent(
2677 '<div class="mw-editintro">{{:' .
$title->getFullText() .
'}}</div>',
2714 return $content->serialize( $this->contentFormat );
2734 if ( $text ===
false || $text ===
null ) {
2739 $this->contentModel, $this->contentFormat );
2757 # need to parse the preview early so that we know which templates are used,
2758 # otherwise users with "show preview after edit box" will get a blank list
2759 # we parse this near the beginning so that setHeaders can do the title
2760 # setting work instead of leaving it in getPreviewText
2761 $previewOutput =
'';
2762 if ( $this->formtype ==
'preview' ) {
2766 $out = $this->context->getOutput();
2770 Hooks::run(
'EditPage::showEditForm:initial', [ &$editPage, &
$out ] );
2777 if ( !$this->isConflict &&
2778 $this->section !=
'' &&
2783 $out->showErrorPage(
'sectioneditnotsupported-title',
'sectioneditnotsupported-text' );
2789 $out->addHTML( $this->editFormPageTop );
2791 $user = $this->context->getUser();
2792 if (
$user->getOption(
'previewontop' ) ) {
2796 $out->addHTML( $this->editFormTextTop );
2799 $out->wrapWikiMsg(
"<div class='error mw-deleted-while-editing'>\n$1\n</div>",
2800 'deletedwhileediting' );
2805 $out->addHTML( Html::openElement(
2808 'class' =>
'mw-editform',
2809 'id' => self::EDITFORM_ID,
2810 'name' => self::EDITFORM_ID,
2813 'enctype' =>
'multipart/form-data'
2817 if ( is_callable( $formCallback ) ) {
2818 wfWarn(
'The $formCallback parameter to ' . __METHOD__ .
'is deprecated' );
2819 call_user_func_array( $formCallback, [ &
$out ] );
2823 $out->addHTML( Html::hidden(
'wpUnicodeCheck', self::UNICODE_CHECK ) );
2827 Xml::openElement(
'div', [
'id' =>
'antispam-container',
'style' =>
'display: none;' ] )
2830 [
'for' =>
'wpAntispam' ],
2831 $this->context->msg(
'simpleantispam-label' )->parse()
2837 'name' =>
'wpAntispam',
2838 'id' =>
'wpAntispam',
2847 Hooks::run(
'EditPage::showEditForm:fields', [ &$editPage, &
$out ] );
2853 $username = $this->lastDelete->user_name;
2855 ->getComment(
'log_comment', $this->lastDelete )->text;
2859 $key = $comment ===
''
2860 ?
'confirmrecreate-noreason'
2861 :
'confirmrecreate';
2863 '<div class="mw-confirm-recreate">' .
2864 $this->context->msg( $key,
$username,
"<nowiki>$comment</nowiki>" )->parse() .
2865 Xml::checkLabel( $this->context->msg(
'recreate' )->text(),
'wpRecreate',
'wpRecreate',
false,
2872 # When the summary is hidden, also hide them on preview/show changes
2873 if ( $this->nosummary ) {
2874 $out->addHTML( Html::hidden(
'nosummary',
true ) );
2877 # If a blank edit summary was previously provided, and the appropriate
2878 # user preference is active, pass a hidden tag as wpIgnoreBlankSummary. This will stop the
2879 # user being bounced back more than once in the event that a summary
2882 # For a bit more sophisticated detection of blank summaries, hash the
2883 # automatic one and pass that in the hidden field wpAutoSummary.
2884 if ( $this->missingSummary || ( $this->section ==
'new' && $this->nosummary ) ) {
2885 $out->addHTML( Html::hidden(
'wpIgnoreBlankSummary',
true ) );
2888 if ( $this->undidRev ) {
2889 $out->addHTML( Html::hidden(
'wpUndidRevision', $this->undidRev ) );
2892 if ( $this->selfRedirect ) {
2893 $out->addHTML( Html::hidden(
'wpIgnoreSelfRedirect',
true ) );
2896 if ( $this->hasPresetSummary ) {
2900 $this->autoSumm = md5(
'' );
2903 $autosumm = $this->autoSumm !==
'' ? $this->autoSumm : md5( $this->summary );
2904 $out->addHTML( Html::hidden(
'wpAutoSummary', $autosumm ) );
2906 $out->addHTML( Html::hidden(
'oldid', $this->oldid ) );
2909 $out->addHTML( Html::hidden(
'format', $this->contentFormat ) );
2910 $out->addHTML( Html::hidden(
'model', $this->contentModel ) );
2914 if ( $this->section ==
'new' ) {
2919 $out->addHTML( $this->editFormTextBeforeContent );
2920 if ( $this->isConflict ) {
2935 if ( !$this->mTitle->isUserConfigPage() ) {
2936 $out->addHTML( self::getEditToolbar( $this->mTitle ) );
2939 if ( $this->blankArticle ) {
2940 $out->addHTML( Html::hidden(
'wpIgnoreBlankArticle',
true ) );
2943 if ( $this->isConflict ) {
2948 $conflictTextBoxAttribs = [];
2950 $conflictTextBoxAttribs[
'style'] =
'display:none;';
2951 } elseif ( $this->isOldRev ) {
2952 $conflictTextBoxAttribs[
'class'] =
'mw-textarea-oldrev';
2961 $out->addHTML( $this->editFormTextAfterContent );
2971 $out->addHTML( $this->editFormTextAfterTools .
"\n" );
2975 $out->addHTML( Html::rawElement(
'div', [
'class' =>
'hiddencats' ],
2978 $out->addHTML( Html::rawElement(
'div', [
'class' =>
'limitreport' ],
2979 self::getPreviewLimitReport( $this->mParserOutput ) ) );
2981 $out->addModules(
'mediawiki.action.edit.collapsibleFooter' );
2983 if ( $this->isConflict ) {
2988 $msg = $this->context->msg(
2989 'content-failed-to-parse',
2990 $this->contentModel,
2991 $this->contentFormat,
2994 $out->wrapWikiTextAsInterface(
'error', $msg->plain() );
2999 if ( $this->isConflict ) {
3001 } elseif ( $this->preview ) {
3003 } elseif ( $this->
diff ) {
3008 $out->addHTML( Html::hidden(
'mode', $mode, [
'id' =>
'mw-edit-mode' ] ) );
3012 $out->addHTML( Html::hidden(
'wpUltimateParam',
true ) );
3013 $out->addHTML( $this->editFormTextBottom .
"\n</form>\n" );
3015 if ( !
$user->getOption(
'previewontop' ) ) {
3029 $this->context, MediaWikiServices::getInstance()->getLinkRenderer()
3034 if ( $this->preview ) {
3036 } elseif ( $this->section !=
'' ) {
3040 return Html::rawElement(
'div', [
'class' =>
'templatesUsed' ],
3041 $templateListFormatter->format( $templates,
$type )
3052 preg_match(
"/^(=+)(.+)\\1\\s*(\n|$)/i", $text,
$matches );
3062 $out = $this->context->getOutput();
3063 $user = $this->context->getUser();
3064 if ( $this->isConflict ) {
3066 $this->editRevId = $this->
page->getLatest();
3068 if ( $this->section !=
'' && $this->section !=
'new' && !$this->summary &&
3069 !$this->preview && !$this->
diff
3072 if ( $sectionTitle !==
false ) {
3073 $this->summary =
"/* $sectionTitle */ ";
3079 if ( $this->missingComment ) {
3080 $out->wrapWikiMsg(
"<div id='mw-missingcommenttext'>\n$1\n</div>",
'missingcommenttext' );
3083 if ( $this->missingSummary && $this->section !=
'new' ) {
3085 "<div id='mw-missingsummary'>\n$1\n</div>",
3086 [
'missingsummary', $buttonLabel ]
3090 if ( $this->missingSummary && $this->section ==
'new' ) {
3092 "<div id='mw-missingcommentheader'>\n$1\n</div>",
3093 [
'missingcommentheader', $buttonLabel ]
3097 if ( $this->blankArticle ) {
3099 "<div id='mw-blankarticle'>\n$1\n</div>",
3100 [
'blankarticle', $buttonLabel ]
3104 if ( $this->selfRedirect ) {
3106 "<div id='mw-selfredirect'>\n$1\n</div>",
3107 [
'selfredirect', $buttonLabel ]
3111 if ( $this->hookError !==
'' ) {
3112 $out->addWikiTextAsInterface( $this->hookError );
3115 if ( $this->section !=
'new' ) {
3116 $revision = $this->mArticle->getRevisionFetched();
3122 "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
3123 'rev-deleted-text-permission'
3127 "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
3128 'rev-deleted-text-view'
3132 if ( !$revision->isCurrent() ) {
3133 $this->mArticle->setOldSubtitle( $revision->getId() );
3135 Html::warningBox(
"\n$1\n" ),
3138 $this->isOldRev =
true;
3140 } elseif ( $this->mTitle->exists() ) {
3143 $out->wrapWikiMsg(
"<div class='errorbox'>\n$1\n</div>\n",
3144 [
'missing-revision', $this->oldid ] );
3151 "<div id=\"mw-read-only-warning\">\n$1\n</div>",
3154 } elseif (
$user->isAnon() ) {
3155 if ( $this->formtype !=
'preview' ) {
3156 $returntoquery = array_diff_key(
3157 $this->context->getRequest()->getValues(),
3158 [
'title' =>
true,
'returnto' =>
true,
'returntoquery' =>
true ]
3161 "<div id='mw-anon-edit-warning' class='warningbox'>\n$1\n</div>",
3162 [
'anoneditwarning',
3165 'returnto' => $this->
getTitle()->getPrefixedDBkey(),
3170 'returnto' => $this->
getTitle()->getPrefixedDBkey(),
3176 $out->wrapWikiMsg(
"<div id=\"mw-anon-preview-warning\" class=\"warningbox\">\n$1</div>",
3177 'anonpreviewwarning'
3180 } elseif ( $this->mTitle->isUserConfigPage() ) {
3181 # Check the skin exists
3184 "<div class='error' id='mw-userinvalidconfigtitle'>\n$1\n</div>",
3185 [
'userinvalidconfigtitle', $this->mTitle->getSkinFromConfigSubpage() ]
3188 if ( $this->
getTitle()->isSubpageOf(
$user->getUserPage() ) ) {
3189 $isUserCssConfig = $this->mTitle->isUserCssConfigPage();
3190 $isUserJsonConfig = $this->mTitle->isUserJsonConfigPage();
3191 $isUserJsConfig = $this->mTitle->isUserJsConfigPage();
3193 $warning = $isUserCssConfig
3195 : ( $isUserJsonConfig ?
'userjsonispublic' :
'userjsispublic' );
3197 $out->wrapWikiMsg(
'<div class="mw-userconfigpublic">$1</div>', $warning );
3199 if ( $this->formtype !==
'preview' ) {
3200 $config = $this->context->getConfig();
3201 if ( $isUserCssConfig && $config->get(
'AllowUserCss' ) ) {
3203 "<div id='mw-usercssyoucanpreview'>\n$1\n</div>",
3204 [
'usercssyoucanpreview' ]
3206 } elseif ( $isUserJsonConfig ) {
3208 "<div id='mw-userjsonyoucanpreview'>\n$1\n</div>",
3209 [
'userjsonyoucanpreview' ]
3211 } elseif ( $isUserJsConfig && $config->get(
'AllowUserJs' ) ) {
3213 "<div id='mw-userjsyoucanpreview'>\n$1\n</div>",
3214 [
'userjsyoucanpreview' ]
3225 # Add header copyright warning
3240 return ( is_array( $inputAttrs ) ? $inputAttrs : [] ) + [
3241 'id' =>
'wpSummary',
3242 'name' =>
'wpSummary',
3246 'spellcheck' =>
'true',
3260 $inputAttrs = OOUI\Element::configFromHtmlAttributes(
3269 $inputAttrs[
'inputId'] = $inputAttrs[
'id'];
3270 $inputAttrs[
'id'] =
'wpSummaryWidget';
3272 return new OOUI\FieldLayout(
3273 new OOUI\TextInputWidget( [
3275 'infusable' =>
true,
3278 'label' =>
new OOUI\HtmlSnippet( $labelText ),
3280 'id' =>
'wpSummaryLabel',
3281 'classes' => [ $this->missingSummary ?
'mw-summarymissed' :
'mw-summary' ],
3293 # Add a class if 'missingsummary' is triggered to allow styling of the summary line
3294 $summaryClass = $this->missingSummary ?
'mw-summarymissed' :
'mw-summary';
3295 if ( $isSubjectPreview ) {
3296 if ( $this->nosummary ) {
3299 } elseif ( !$this->mShowSummaryField ) {
3303 $labelText = $this->context->msg( $isSubjectPreview ?
'subject' :
'summary' )->parse();
3307 [
'class' => $summaryClass ]
3321 if ( !
$summary || ( !$this->preview && !$this->
diff ) ) {
3327 if ( $isSubjectPreview ) {
3328 $summary = $this->context->msg(
'newsectionsummary' )
3330 ->inContentLanguage()->text();
3333 $message = $isSubjectPreview ?
'subject-preview' :
'summary-preview';
3335 $summary = $this->context->msg( $message )->parse()
3341 $out = $this->context->getOutput();
3342 $out->addHTML( Html::hidden(
'wpSection', $this->section ) );
3343 $out->addHTML( Html::hidden(
'wpStarttime', $this->starttime ) );
3344 $out->addHTML( Html::hidden(
'wpEdittime', $this->edittime ) );
3345 $out->addHTML( Html::hidden(
'editRevId', $this->editRevId ) );
3346 $out->addHTML( Html::hidden(
'wpScrolltop', $this->scrolltop, [
'id' =>
'wpScrolltop' ] ) );
3362 $this->context->getOutput()->addHTML(
3364 Html::hidden(
"wpEditToken", $this->context->getUser()->getEditToken() ) .
3391 $attribs = [
'style' =>
'display:none;' ];
3394 $classes = $builder->getTextboxProtectionCSSClasses( $this->
getTitle() );
3396 # Is an old revision being edited?
3397 if ( $this->isOldRev ) {
3398 $classes[] =
'mw-textarea-oldrev';
3411 $textoverride ?? $this->textbox1,
3418 $this->
showTextbox( $this->textbox2,
'wpTextbox2', [
'tabindex' => 6,
'readonly' ] );
3423 $attribs = $builder->buildTextboxAttribs(
3426 $this->context->getUser(),
3430 $this->context->getOutput()->addHTML(
3431 Html::textarea(
$name, $builder->addNewLineAtEnd( $text ),
$attribs )
3438 $classes[] =
'ontop';
3441 $attribs = [
'id' =>
'wikiPreview',
'class' => implode(
' ', $classes ) ];
3443 if ( $this->formtype !=
'preview' ) {
3444 $attribs[
'style'] =
'display: none;';
3447 $out = $this->context->getOutput();
3450 if ( $this->formtype ==
'preview' ) {
3454 $pageViewLang = $this->mTitle->getPageViewLanguage();
3455 $attribs = [
'lang' => $pageViewLang->getHtmlCode(),
'dir' => $pageViewLang->getDir(),
3456 'class' =>
'mw-content-' . $pageViewLang->getDir() ];
3460 $out->addHTML(
'</div>' );
3462 if ( $this->formtype ==
'diff' ) {
3466 $msg = $this->context->msg(
3467 'content-failed-to-parse',
3468 $this->contentModel,
3469 $this->contentFormat,
3472 $out->wrapWikiTextAsInterface(
'error', $msg->plain() );
3485 $this->mArticle->openShowCategory();
3487 # This hook seems slightly odd here, but makes things more
3488 # consistent for extensions.
3489 $out = $this->context->getOutput();
3491 $out->addHTML( $text );
3493 $this->mArticle->closeShowCategory();
3505 $oldtitlemsg =
'currentrev';
3506 # if message does not exist, show diff against the preloaded default
3507 if ( $this->mTitle->getNamespace() ==
NS_MEDIAWIKI && !$this->mTitle->exists() ) {
3508 $oldtext = $this->mTitle->getDefaultMessageText();
3509 if ( $oldtext !==
false ) {
3510 $oldtitlemsg =
'defaultmessagetext';
3520 if ( $this->editRevId !==
null ) {
3521 $newContent = $this->
page->replaceSectionAtRev(
3522 $this->section, $textboxContent, $this->summary, $this->editRevId
3525 $newContent = $this->
page->replaceSectionContent(
3526 $this->section, $textboxContent, $this->summary, $this->edittime
3530 if ( $newContent ) {
3531 Hooks::run(
'EditPageGetDiffContent', [ $this, &$newContent ] );
3533 $user = $this->context->getUser();
3535 MediaWikiServices::getInstance()->getContentLanguage() );
3536 $newContent = $newContent->preSaveTransform( $this->mTitle,
$user, $popts );
3539 if ( ( $oldContent && !$oldContent->isEmpty() ) || ( $newContent && !$newContent->isEmpty() ) ) {
3540 $oldtitle = $this->context->msg( $oldtitlemsg )->parse();
3541 $newtitle = $this->context->msg(
'yourtext' )->parse();
3543 if ( !$oldContent ) {
3544 $oldContent = $newContent->getContentHandler()->makeEmptyContent();
3547 if ( !$newContent ) {
3548 $newContent = $oldContent->getContentHandler()->makeEmptyContent();
3551 $de = $oldContent->getContentHandler()->createDifferenceEngine( $this->context );
3552 $de->setContent( $oldContent, $newContent );
3554 $difftext = $de->getDiff( $oldtitle, $newtitle );
3555 $de->showDiffStyle();
3560 $this->context->getOutput()->addHTML(
'<div id="wikiDiff">' . $difftext .
'</div>' );
3567 $msg =
'editpage-head-copy-warn';
3568 if ( !$this->context->msg( $msg )->isDisabled() ) {
3569 $this->context->getOutput()->wrapWikiMsg(
"<div class='editpage-head-copywarn'>\n$1\n</div>",
3570 'editpage-head-copy-warn' );
3583 $msg =
'editpage-tos-summary';
3584 Hooks::run(
'EditPageTosSummary', [ $this->mTitle, &$msg ] );
3585 if ( !$this->context->msg( $msg )->isDisabled() ) {
3586 $out = $this->context->getOutput();
3587 $out->addHTML(
'<div class="mw-tos-summary">' );
3588 $out->addWikiMsg( $msg );
3589 $out->addHTML(
'</div>' );
3598 $this->context->getOutput()->addHTML(
'<div class="mw-editTools">' .
3599 $this->context->msg(
'edittools' )->inContentLanguage()->parse() .
3624 $copywarnMsg = [
'copyrightwarning',
3625 '[[' .
wfMessage(
'copyrightpage' )->inContentLanguage()->text() .
']]',
3628 $copywarnMsg = [
'copyrightwarning2',
3629 '[[' .
wfMessage(
'copyrightpage' )->inContentLanguage()->text() .
']]' ];
3636 $msg->inLanguage( $langcode );
3638 return "<div id=\"editpage-copywarn\">\n" .
3639 $msg->$format() .
"\n</div>";
3656 $limitReport = Html::rawElement(
'div', [
'class' =>
'mw-limitReportExplanation' ],
3657 wfMessage(
'limitreport-title' )->parseAsBlock()
3661 $limitReport .= Html::openElement(
'div', [
'class' =>
'preview-limit-report-wrapper' ] );
3663 $limitReport .= Html::openElement(
'table', [
3664 'class' =>
'preview-limit-report wikitable'
3666 Html::openElement(
'tbody' );
3670 [ $key, &
$value, &$limitReport,
true,
true ]
3673 $valueMsg =
wfMessage( [
"$key-value-html",
"$key-value" ] );
3674 if ( !$valueMsg->exists() ) {
3677 if ( !$keyMsg->isDisabled() && !$valueMsg->isDisabled() ) {
3678 $limitReport .= Html::openElement(
'tr' ) .
3679 Html::rawElement(
'th',
null, $keyMsg->parse() ) .
3680 Html::rawElement(
'td',
null,
3683 Html::closeElement(
'tr' );
3688 $limitReport .= Html::closeElement(
'tbody' ) .
3689 Html::closeElement(
'table' ) .
3690 Html::closeElement(
'div' );
3692 return $limitReport;
3696 $out = $this->context->getOutput();
3697 $out->addHTML(
"<div class='editOptions'>\n" );
3699 if ( $this->section !=
'new' ) {
3706 [
'minor' => $this->minoredit,
'watch' => $this->watchthis ]
3708 $checkboxesHTML =
new OOUI\HorizontalLayout( [
'items' => $checkboxes ] );
3710 $out->addHTML(
"<div class='editCheckboxes'>" . $checkboxesHTML .
"</div>\n" );
3714 $out->addHTML( $this->editFormTextAfterWarn );
3716 $out->addHTML(
"<div class='editButtons'>\n" );
3721 $message = $this->context->msg(
'edithelppage' )->inContentLanguage()->text();
3725 $this->context->msg(
'edithelp' )->text(),
3726 [
'target' =>
'helpwindow',
'href' => $edithelpurl ],
3729 $this->context->msg(
'word-separator' )->escaped() .
3730 $this->context->msg(
'newwindow' )->parse();
3732 $out->addHTML(
" <span class='cancelLink'>{$cancel}</span>\n" );
3733 $out->addHTML(
" <span class='editHelp'>{$edithelp}</span>\n" );
3734 $out->addHTML(
"</div><!-- editButtons -->\n" );
3738 $out->addHTML(
"</div><!-- editOptions -->\n" );
3746 $out = $this->context->getOutput();
3749 if (
Hooks::run(
'EditPageBeforeConflictDiff', [ &$editPage, &
$out ] ) ) {
3765 if ( !$this->isConflict && $this->oldid > 0 ) {
3768 $cancelParams[
'redirect'] =
'no';
3771 return new OOUI\ButtonWidget( [
3772 'id' =>
'mw-editform-cancel',
3774 'label' =>
new OOUI\HtmlSnippet( $this->context->msg(
'cancel' )->parse() ),
3776 'infusable' =>
true,
3777 'flags' =>
'destructive',
3791 return $title->getLocalURL( [
'action' => $this->
action ] );
3802 if ( $this->deletedSinceEdit !==
null ) {
3806 $this->deletedSinceEdit =
false;
3808 if ( !$this->mTitle->exists() && $this->mTitle->isDeletedQuick() ) {
3810 if ( $this->lastDelete ) {
3811 $deleteTime =
wfTimestamp( TS_MW, $this->lastDelete->log_timestamp );
3812 if ( $deleteTime > $this->starttime ) {
3813 $this->deletedSinceEdit =
true;
3831 array_merge( [
'logging' ], $commentQuery[
'tables'], $actorQuery[
'tables'], [
'user' ] ),
3841 ] + $commentQuery[
'fields'] + $actorQuery[
'fields'],
3843 'log_namespace' => $this->mTitle->getNamespace(),
3844 'log_title' => $this->mTitle->getDBkey(),
3845 'log_type' =>
'delete',
3846 'log_action' =>
'delete',
3849 [
'LIMIT' => 1,
'ORDER BY' =>
'log_timestamp DESC' ],
3851 'user' => [
'JOIN',
'user_id=' . $actorQuery[
'fields'][
'log_user'] ],
3852 ] + $commentQuery[
'joins'] + $actorQuery[
'joins']
3855 if ( is_object(
$data ) ) {
3857 $data->user_name = $this->context->msg(
'rev-deleted-user' )->escaped();
3861 $data->log_comment_text = $this->context->msg(
'rev-deleted-comment' )->escaped();
3862 $data->log_comment_data =
null;
3875 $out = $this->context->getOutput();
3876 $config = $this->context->getConfig();
3882 if ( $this->textbox1 !==
'' ) {
3886 $parsedNote = Html::rawElement(
'div', [
'class' =>
'previewnote' ],
3887 $out->parseAsInterface(
3888 $this->context->msg(
'session_fail_preview_html' )->plain()
3902 'AlternateEditPreview',
3903 [ $this, &
$content, &$previewHTML, &$this->mParserOutput ] )
3905 return $previewHTML;
3908 # provide a anchor link to the editform
3909 $continueEditing =
'<span class="mw-continue-editing">' .
3910 '[[#' . self::EDITFORM_ID .
'|' .
3911 $this->context->getLanguage()->getArrow() .
' ' .
3912 $this->context->msg(
'continue-editing' )->text() .
']]</span>';
3913 if ( $this->mTriedSave && !$this->mTokenOk ) {
3914 if ( $this->mTokenOkExceptSuffix ) {
3915 $note = $this->context->msg(
'token_suffix_mismatch' )->plain();
3918 $note = $this->context->msg(
'session_fail_preview' )->plain();
3921 } elseif ( $this->incompleteForm ) {
3922 $note = $this->context->msg(
'edit_form_incomplete' )->plain();
3923 if ( $this->mTriedSave ) {
3927 $note = $this->context->msg(
'previewnote' )->plain() .
' ' . $continueEditing;
3930 # don't parse non-wikitext pages, show message about preview
3931 if ( $this->mTitle->isUserConfigPage() || $this->mTitle->isSiteConfigPage() ) {
3932 if ( $this->mTitle->isUserConfigPage() ) {
3934 } elseif ( $this->mTitle->isSiteConfigPage() ) {
3942 if ( $level ===
'user' && !$config->get(
'AllowUserCss' ) ) {
3947 if ( $level ===
'user' ) {
3952 if ( $level ===
'user' && !$config->get(
'AllowUserJs' ) ) {
3959 # Used messages to make sure grep find them:
3960 # Messages: usercsspreview, userjsonpreview, userjspreview,
3961 # sitecsspreview, sitejsonpreview, sitejspreview
3962 if ( $level && $format ) {
3963 $note =
"<div id='mw-{$level}{$format}preview'>" .
3964 $this->context->msg(
"{$level}{$format}preview" )->plain() .
3965 ' ' . $continueEditing .
"</div>";
3969 # If we're adding a comment, we need to show the
3970 # summary as the headline
3971 if ( $this->section ===
"new" && $this->summary !==
"" ) {
3976 Hooks::run(
'EditPageGetPreviewContent', $hook_args );
3979 $parserOutput = $parserResult[
'parserOutput'];
3980 $previewHTML = $parserResult[
'html'];
3981 $this->mParserOutput = $parserOutput;
3982 $out->addParserOutputMetadata( $parserOutput );
3983 if (
$out->userCanPreview() ) {
3987 if (
count( $parserOutput->getWarnings() ) ) {
3988 $note .=
"\n\n" . implode(
"\n\n", $parserOutput->getWarnings() );
3992 $m = $this->context->msg(
3993 'content-failed-to-parse',
3994 $this->contentModel,
3995 $this->contentFormat,
3998 $note .=
"\n\n" . $m->plain(); # gets parsed down
below
4002 if ( $this->isConflict ) {
4003 $conflict = Html::rawElement(
4004 'h2', [
'id' =>
'mw-previewconflict' ],
4005 $this->context->msg(
'previewconflict' )->escaped()
4008 $conflict =
'<hr />';
4011 $previewhead = Html::rawElement(
4012 'div', [
'class' =>
'previewnote' ],
4014 'h2', [
'id' =>
'mw-previewheader' ],
4015 $this->context->msg(
'preview' )->escaped()
4017 $out->parseAsInterface( $note ) . $conflict
4020 $pageViewLang = $this->mTitle->getPageViewLanguage();
4021 $attribs = [
'lang' => $pageViewLang->getHtmlCode(),
'dir' => $pageViewLang->getDir(),
4022 'class' =>
'mw-content-' . $pageViewLang->getDir() ];
4023 $previewHTML = Html::rawElement(
'div',
$attribs, $previewHTML );
4029 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
4030 $stats->increment(
'edit.failures.' . $failureType );
4038 $parserOptions = $this->
page->makeParserOptions( $this->context );
4039 $parserOptions->setIsPreview(
true );
4040 $parserOptions->setIsSectionPreview( !is_null( $this->section ) && $this->section !==
'' );
4041 $parserOptions->enableLimitReport();
4048 return $parserOptions;
4061 $user = $this->context->getUser();
4069 $pstContent =
$content->preSaveTransform( $this->mTitle,
$user, $parserOptions );
4070 $scopedCallback = $parserOptions->setupFakeRevision( $this->mTitle, $pstContent,
$user );
4071 $parserOutput = $pstContent->getParserOutput( $this->mTitle,
null, $parserOptions );
4072 ScopedCallback::consume( $scopedCallback );
4074 'parserOutput' => $parserOutput,
4075 'html' => $parserOutput->getText( [
4076 'enableSectionEditLinks' =>
false
4085 if ( $this->preview || $this->section !=
'' ) {
4087 if ( !isset( $this->mParserOutput ) ) {
4090 foreach ( $this->mParserOutput->getTemplates()
as $ns =>
$template ) {
4097 return $this->mTitle->getTemplateLinksFrom();
4108 $startingToolbar =
'<div id="toolbar"></div>';
4109 $toolbar = $startingToolbar;
4111 if ( !
Hooks::run(
'EditPageBeforeEditToolbar', [ &$toolbar ] ) ) {
4115 return ( $toolbar === $startingToolbar ) ?
null : $toolbar;
4139 $user = $this->context->getUser();
4141 if ( !$this->isNew &&
$user->isAllowed(
'minoredit' ) ) {
4142 $checkboxes[
'wpMinoredit'] = [
4143 'id' =>
'wpMinoredit',
4144 'label-message' =>
'minoredit',
4146 'tooltip' =>
'minoredit',
4147 'label-id' =>
'mw-editpage-minoredit',
4148 'legacy-name' =>
'minor',
4149 'default' => $checked[
'minor'],
4153 if (
$user->isLoggedIn() ) {
4154 $checkboxes[
'wpWatchthis'] = [
4155 'id' =>
'wpWatchthis',
4156 'label-message' =>
'watchthis',
4158 'tooltip' =>
'watch',
4159 'label-id' =>
'mw-editpage-watch',
4160 'legacy-name' =>
'watch',
4161 'default' => $checked[
'watch'],
4166 Hooks::run(
'EditPageGetCheckboxesDefinition', [ $editPage, &$checkboxes ] );
4190 if ( isset(
$options[
'tooltip'] ) ) {
4191 $accesskey = $this->context->msg(
"accesskey-{$options['tooltip']}" )->text();
4194 if ( isset(
$options[
'title-message'] ) ) {
4195 $title = $this->context->msg(
$options[
'title-message'] )->text();
4198 $checkboxes[ $legacyName ] =
new OOUI\FieldLayout(
4199 new OOUI\CheckboxInputWidget( [
4201 'accessKey' => $accesskey,
4206 'infusable' =>
true,
4209 'align' =>
'inline',
4210 'label' =>
new OOUI\HtmlSnippet( $this->context->msg(
$options[
'label-message'] )->parse() ),
4212 'id' =>
$options[
'label-id'] ??
null,
4228 $this->context->getConfig()->get(
'EditSubmitButtonLabelPublish' );
4231 $newPage = !$this->mTitle->exists();
4233 if ( $labelAsPublish ) {
4234 $buttonLabelKey = $newPage ?
'publishpage' :
'publishchanges';
4236 $buttonLabelKey = $newPage ?
'savearticle' :
'savechanges';
4239 return $buttonLabelKey;
4254 $this->context->getConfig()->get(
'EditSubmitButtonLabelPublish' );
4257 $buttonTooltip = $labelAsPublish ?
'publish' :
'save';
4259 $buttons[
'save'] =
new OOUI\ButtonInputWidget( [
4262 'id' =>
'wpSaveWidget',
4263 'inputId' =>
'wpSave',
4265 'useInputTag' =>
true,
4266 'flags' => [
'progressive',
'primary' ],
4267 'label' => $buttonLabel,
4268 'infusable' =>
true,
4276 $buttons[
'preview'] =
new OOUI\ButtonInputWidget( [
4277 'name' =>
'wpPreview',
4279 'id' =>
'wpPreviewWidget',
4280 'inputId' =>
'wpPreview',
4282 'useInputTag' =>
true,
4283 'label' => $this->context->msg(
'showpreview' )->text(),
4284 'infusable' =>
true,
4292 $buttons[
'diff'] =
new OOUI\ButtonInputWidget( [
4295 'id' =>
'wpDiffWidget',
4296 'inputId' =>
'wpDiff',
4298 'useInputTag' =>
true,
4299 'label' => $this->context->msg(
'showdiff' )->text(),
4300 'infusable' =>
true,
4320 $out = $this->context->getOutput();
4321 $out->prepareErrorPage( $this->context->msg(
'nosuchsectiontitle' ) );
4323 $res = $this->context->msg(
'nosuchsectiontext', $this->section )->parseAsBlock();
4330 $out->returnToMain(
false, $this->mTitle );
4341 if ( is_array( $match ) ) {
4342 $match = $this->context->getLanguage()->listToText( $match );
4344 $out = $this->context->getOutput();
4345 $out->prepareErrorPage( $this->context->msg(
'spamprotectiontitle' ) );
4347 $out->addHTML(
'<div id="spamprotected">' );
4348 $out->addWikiMsg(
'spamprotectiontext' );
4352 $out->addHTML(
'</div>' );
4354 $out->wrapWikiMsg(
'<h2>$1</h2>',
"yourdiff" );
4357 $out->wrapWikiMsg(
'<h2>$1</h2>',
"yourtext" );
4374 return rtrim(
$request->getText( $field ) );
4394 $out = $this->context->getOutput();
4395 $editNotices = $this->mTitle->getEditNotices( $this->oldid );
4396 if (
count( $editNotices ) ) {
4397 $out->addHTML( implode(
"\n", $editNotices ) );
4399 $msg = $this->context->msg(
'editnotice-notext' );
4400 if ( !$msg->isDisabled() ) {
4402 '<div class="mw-editnotice-notext">'
4403 . $msg->parseAsBlock()
4414 if ( $this->mTitle->isTalkPage() ) {
4415 $this->context->getOutput()->addWikiMsg(
'talkpagetext' );
4423 if ( $this->contentLength ===
false ) {
4424 $this->contentLength = strlen( $this->textbox1 );
4427 $out = $this->context->getOutput();
4428 $lang = $this->context->getLanguage();
4429 $maxArticleSize = $this->context->getConfig()->get(
'MaxArticleSize' );
4430 if ( $this->tooBig || $this->contentLength > $maxArticleSize * 1024 ) {
4431 $out->wrapWikiMsg(
"<div class='error' id='mw-edit-longpageerror'>\n$1\n</div>",
4434 $lang->formatNum( round( $this->contentLength / 1024, 3 ) ),
4435 $lang->formatNum( $maxArticleSize )
4438 } elseif ( !$this->context->msg(
'longpage-hint' )->isDisabled() ) {
4439 $out->wrapWikiMsg(
"<div id='mw-edit-longpage-hint'>\n$1\n</div>",
4442 $lang->formatSize( strlen( $this->textbox1 ) ),
4443 strlen( $this->textbox1 )
4453 $out = $this->context->getOutput();
4454 if ( $this->mTitle->isProtected(
'edit' ) &&
4457 # Is the title semi-protected?
4458 if ( $this->mTitle->isSemiProtected() ) {
4459 $noticeMsg =
'semiprotectedpagewarning';
4461 # Then it must be protected based on static groups (regular)
4462 $noticeMsg =
'protectedpagewarning';
4465 [
'lim' => 1,
'msgKey' => [ $noticeMsg ] ] );
4467 if ( $this->mTitle->isCascadeProtected() ) {
4468 # Is this page under cascading protection from some source pages?
4470 list( $cascadeSources, ) = $this->mTitle->getCascadeProtectionSources();
4471 $notice =
"<div class='mw-cascadeprotectedwarning'>\n$1\n";
4472 $cascadeSourcesCount =
count( $cascadeSources );
4473 if ( $cascadeSourcesCount > 0 ) {
4474 # Explain, and list the titles responsible
4475 foreach ( $cascadeSources
as $page ) {
4476 $notice .=
'* [[:' .
$page->getPrefixedText() .
"]]\n";
4479 $notice .=
'</div>';
4480 $out->wrapWikiMsg( $notice, [
'cascadeprotectedwarning', $cascadeSourcesCount ] );
4482 if ( !$this->mTitle->exists() && $this->mTitle->getRestrictions(
'create' ) ) {
4485 'showIfEmpty' =>
false,
4486 'msgKey' => [
'titleprotectedwarning' ],
4487 'wrap' =>
"<div class=\"mw-titleprotectedwarning\">\n$1</div>" ] );
4537 $userAgent = $this->context->getRequest()->getHeader(
'User-Agent' );
4538 if ( $userAgent && preg_match(
'/MSIE|Edge/', $userAgent ) ) {
4540 return $wgParser->guessLegacySectionNameFromWikiText( $text );
4546 return '#' . urlencode( mb_substr(
$name, 1 ) );
4556 $this->editConflictHelperFactory = $factory;
4557 $this->editConflictHelper =
null;
4564 if ( !$this->editConflictHelper ) {
4565 $this->editConflictHelper = call_user_func(
4566 $this->editConflictHelperFactory,
4582 MediaWikiServices::getInstance()->getStatsdDataFactory(),