25 use Wikimedia\ScopedCallback;
375 # Placeholders for text injection by hooks (must be HTML)
376 # extensions should take care to _append_ to the present value
429 $this->mTitle =
$article->getTitle();
430 $this->context =
$article->getContext();
432 $this->contentModel = $this->mTitle->getContentModel();
435 $this->contentFormat =
$handler->getDefaultFormat();
437 $this->oouiEnabled = $wgOOUIEditPage;
469 $this->mContextTitle =
$title;
480 if ( is_null( $this->mContextTitle ) ) {
504 return $this->enableApiEditOverride ===
true ||
515 $this->enableApiEditOverride = $enableOverride;
539 if ( !
Hooks::run(
'AlternateEdit', [ $this ] ) ) {
543 wfDebug( __METHOD__ .
": enter\n" );
546 if (
$wgRequest->getBool(
'redlink' ) && $this->mTitle->exists() ) {
547 $wgOut->redirect( $this->mTitle->getFullURL() );
552 $this->firsttime =
false;
557 $this->preview =
true;
561 $this->formtype =
'save';
562 } elseif ( $this->preview ) {
563 $this->formtype =
'preview';
564 } elseif ( $this->
diff ) {
565 $this->formtype =
'diff';
566 }
else { #
First time through
567 $this->firsttime =
true;
569 $this->formtype =
'preview';
571 $this->formtype =
'initial';
577 wfDebug( __METHOD__ .
": User can't edit\n" );
582 $user->spreadAnyEditBlock();
590 $revision = $this->mArticle->getRevisionFetched();
597 if ( $this->undidRev ) {
599 $prevRev = $undidRevObj ? $undidRevObj->getPrevious() :
null;
601 if ( !$this->undidRev
608 'contentmodelediterror',
609 $revision->getContentModel(),
617 $this->isConflict =
false;
619 $this->isCssJsSubpage = $this->mTitle->isCssJsSubpage();
620 $this->isCssSubpage = $this->mTitle->isCssSubpage();
621 $this->isJsSubpage = $this->mTitle->isJsSubpage();
625 # Show applicable editing introductions
626 if ( $this->formtype ==
'initial' || $this->firsttime ) {
630 # Attempt submission here. This will check for edit conflicts,
631 # and redundantly check for locked database, blocked IPs, etc.
632 # that edit() already checked just in case someone tries to sneak
633 # in the back door with a hand-edited submission URL.
635 if (
'save' == $this->formtype ) {
636 $resultDetails =
null;
643 # First time through: get contents, set time for conflict
645 if (
'initial' == $this->formtype || $this->firsttime ) {
651 if ( !$this->mTitle->getArticleID() ) {
652 Hooks::run(
'EditFormPreloadText', [ &$this->textbox1, &$this->mTitle ] );
654 Hooks::run(
'EditFormInitialText', [ $this ] );
669 $permErrors = $this->mTitle->getUserPermissionsErrors(
'edit',
$wgUser, $rigor );
670 # Can this title be created?
671 if ( !$this->mTitle->exists() ) {
672 $permErrors = array_merge(
675 $this->mTitle->getUserPermissionsErrors(
'create',
$wgUser, $rigor ),
680 # Ignore some permissions errors when a user is just previewing/viewing diffs
682 foreach ( $permErrors
as $error ) {
683 if ( ( $this->preview || $this->
diff )
685 $error[0] ==
'blockedtext' ||
686 $error[0] ==
'autoblockedtext' ||
687 $error[0] ==
'systemblockedtext'
718 $wgOut->redirect( $this->mTitle->getFullURL() );
724 # Use the normal message if there's nothing to display
726 $action = $this->mTitle->exists() ?
'edit' :
727 ( $this->mTitle->isTalkPage() ?
'createtalk' :
'createpage' );
733 $wgOut->formatPermissionsErrorMessage( $permErrors,
'edit' )
747 $wgOut->setRobotPolicy(
'noindex,nofollow' );
748 $wgOut->setPageTitle( $this->context->msg(
750 $this->getContextTitle()->getPrefixedText()
753 $wgOut->addHTML( $this->editFormPageTop );
754 $wgOut->addHTML( $this->editFormTextTop );
756 if ( $errorMessage !==
'' ) {
757 $wgOut->addWikiText( $errorMessage );
758 $wgOut->addHTML(
"<hr />\n" );
761 # If the user made changes, preserve them when showing the markup
762 # (This happens when a user is blocked during edit, for instance)
763 if ( !$this->firsttime ) {
765 $wgOut->addWikiMsg(
'viewyourtext' );
770 # Serialize using the default format if the content model is not supported
771 # (e.g. for an old revision with a different model)
774 $wgOut->addWikiMsg(
'viewsourcetext' );
777 $wgOut->addHTML( $this->editFormTextBeforeContent );
778 $this->
showTextbox( $text,
'wpTextbox1', [
'readonly' ] );
779 $wgOut->addHTML( $this->editFormTextAfterContent );
783 $wgOut->addModules(
'mediawiki.action.edit.collapsibleFooter' );
785 $wgOut->addHTML( $this->editFormTextBottom );
786 if ( $this->mTitle->exists() ) {
787 $wgOut->returnToMain(
null, $this->mTitle );
798 if (
$wgRequest->getVal(
'preview' ) ==
'yes' ) {
801 } elseif (
$wgRequest->getVal(
'preview' ) ==
'no' ) {
804 } elseif ( $this->section ==
'new' ) {
807 } elseif ( (
$wgRequest->getVal(
'preload' ) !==
null || $this->mTitle->exists() )
808 &&
$wgUser->getOption(
'previewonfirst' )
812 } elseif ( !$this->mTitle->exists()
813 && isset( $wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()] )
814 && $wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()]
830 if ( $this->mTitle->isCssJsSubpage() ) {
831 $name = $this->mTitle->getSkinFromCssJsSubpage();
832 $skins = array_merge(
836 return !in_array(
$name, $skins )
837 && in_array( strtolower(
$name ), $skins );
852 return $contentHandler->supportsSections();
863 # Allow users to change the mode for testing
864 $this->oouiEnabled =
$request->getFuzzyBool(
'ooui', $this->oouiEnabled );
866 # Section edit can come from either the form or a link
867 $this->section =
$request->getVal(
'wpSection',
$request->getVal(
'section' ) );
870 throw new ErrorPageError(
'sectioneditnotsupported-title',
'sectioneditnotsupported-text' );
873 $this->isNew = !$this->mTitle->exists() || $this->section ==
'new';
876 # These fields need to be checked for encoding.
877 # Also remove trailing whitespace, but don't remove _initial_
878 # whitespace from the text boxes. This may be significant formatting.
880 if ( !
$request->getCheck(
'wpTextbox2' ) ) {
890 # Truncate for whole multibyte characters
893 # If the summary consists of a heading, e.g. '==Foobar==', extract the title from the
894 # header syntax, e.g. 'Foobar'. This is mainly an issue when we are using wpSummary for
896 $this->summary = preg_replace(
'/^\s*=+\s*(.*?)\s*=+\s*$/',
'$1', $this->summary );
898 # Treat sectiontitle the same way as summary.
899 # Note that wpSectionTitle is not yet a part of the actual edit form, as wpSummary is
900 # currently doing double duty as both edit summary and section title. Right now this
901 # is just to allow API edits to work around this limitation, but this should be
902 # incorporated into the actual edit form when EditPage is rewritten (Bugs 18654, 26312).
904 $this->sectiontitle = preg_replace(
'/^\s*=+\s*(.*?)\s*=+\s*$/',
'$1', $this->sectiontitle );
906 $this->edittime =
$request->getVal(
'wpEdittime' );
907 $this->editRevId =
$request->getIntOrNull(
'editRevId' );
908 $this->starttime =
$request->getVal(
'wpStarttime' );
915 $this->scrolltop =
$request->getIntOrNull(
'wpScrolltop' );
917 if ( $this->textbox1 ===
'' &&
$request->getVal(
'wpTextbox1' ) === null ) {
921 $this->incompleteForm =
true;
929 $this->incompleteForm = ( !
$request->getVal(
'wpUltimateParam' )
930 && is_null( $this->edittime ) );
932 if ( $this->incompleteForm ) {
933 # If the form is incomplete, force to preview.
934 wfDebug( __METHOD__ .
": Form data appears to be incomplete\n" );
935 wfDebug(
"POST DATA: " . var_export( $_POST,
true ) .
"\n" );
936 $this->preview =
true;
938 $this->preview =
$request->getCheck(
'wpPreview' );
946 # Some browsers will not report any submit button
947 # if the user hits enter in the comment box.
948 # The unmarked state will be assumed to be a save,
949 # if the form seems otherwise complete.
950 wfDebug( __METHOD__ .
": Passed token check.\n" );
951 } elseif ( $this->
diff ) {
952 # Failed token check, but only requested "Show Changes".
953 wfDebug( __METHOD__ .
": Failed token check; Show Changes requested.\n" );
955 # Page might be a hack attempt posted from
956 # an external site. Preview instead of saving.
957 wfDebug( __METHOD__ .
": Failed token check; forcing preview\n" );
958 $this->preview =
true;
962 if ( !preg_match(
'/^\d{14}$/', $this->edittime ) ) {
963 $this->edittime =
null;
966 if ( !preg_match(
'/^\d{14}$/', $this->starttime ) ) {
967 $this->starttime =
null;
970 $this->recreate =
$request->getCheck(
'wpRecreate' );
972 $this->minoredit =
$request->getCheck(
'wpMinoredit' );
973 $this->watchthis =
$request->getCheck(
'wpWatchthis' );
975 # Don't force edit summaries when a user is editing their own user or talk page
977 && $this->mTitle->getText() ==
$wgUser->getName()
979 $this->allowBlankSummary =
true;
981 $this->allowBlankSummary =
$request->getBool(
'wpIgnoreBlankSummary' )
982 || !
$wgUser->getOption(
'forceeditsummary' );
985 $this->autoSumm =
$request->getText(
'wpAutoSummary' );
987 $this->allowBlankArticle =
$request->getBool(
'wpIgnoreBlankArticle' );
988 $this->allowSelfRedirect =
$request->getBool(
'wpIgnoreSelfRedirect' );
992 $this->changeTags = [];
994 $this->changeTags = array_filter( array_map(
'trim', explode(
',',
998 # Not a posted form? Start with nothing.
999 wfDebug( __METHOD__ .
": Not a posted form.\n" );
1000 $this->textbox1 =
'';
1001 $this->summary =
'';
1002 $this->sectiontitle =
'';
1003 $this->edittime =
'';
1004 $this->editRevId =
null;
1006 $this->
edit =
false;
1007 $this->preview =
false;
1008 $this->
save =
false;
1009 $this->
diff =
false;
1010 $this->minoredit =
false;
1012 $this->watchthis =
$request->getBool(
'watchthis',
false );
1013 $this->recreate =
false;
1017 if ( $this->section ==
'new' &&
$request->getVal(
'preloadtitle' ) ) {
1018 $this->sectiontitle =
$request->getVal(
'preloadtitle' );
1020 $this->summary =
$request->getVal(
'preloadtitle' );
1021 } elseif ( $this->section !=
'new' &&
$request->getVal(
'summary' ) ) {
1022 $this->summary =
$request->getText(
'summary' );
1023 if ( $this->summary !==
'' ) {
1024 $this->hasPresetSummary =
true;
1028 if (
$request->getVal(
'minor' ) ) {
1029 $this->minoredit =
true;
1033 $this->oldid =
$request->getInt(
'oldid' );
1034 $this->parentRevId =
$request->getInt(
'parentRevId' );
1036 $this->bot =
$request->getBool(
'bot',
true );
1037 $this->nosummary =
$request->getBool(
'nosummary' );
1040 $this->contentModel =
$request->getText(
'model', $this->contentModel );
1042 $this->contentFormat =
$request->getText(
'format', $this->contentFormat );
1048 'editpage-invalidcontentmodel-title',
1049 'editpage-invalidcontentmodel-text',
1054 if ( !
$handler->isSupportedFormat( $this->contentFormat ) ) {
1056 'editpage-notsupportedcontentformat-title',
1057 'editpage-notsupportedcontentformat-text',
1071 $this->editintro =
$request->getText(
'editintro',
1073 $this->section ===
'new' ?
'MediaWiki:addsection-editintro' :
'' );
1099 $this->edittime = $this->
page->getTimestamp();
1100 $this->editRevId = $this->
page->getLatest();
1109 # Sort out the "watch" checkbox
1110 if ( $wgUser->getOption(
'watchdefault' ) ) {
1112 $this->watchthis =
true;
1113 } elseif (
$wgUser->getOption(
'watchcreations' ) && !$this->mTitle->exists() ) {
1115 $this->watchthis =
true;
1116 } elseif (
$wgUser->isWatched( $this->mTitle ) ) {
1118 $this->watchthis =
true;
1121 $this->minoredit =
true;
1123 if ( $this->textbox1 ===
false ) {
1143 if ( !$this->mTitle->exists() || $this->section ==
'new' ) {
1144 if ( $this->mTitle->getNamespace() ==
NS_MEDIAWIKI && $this->section !=
'new' ) {
1145 # If this is a system message, get the default text.
1146 $msg = $this->mTitle->getDefaultMessageText();
1151 # If requested, preload some text.
1154 $this->section ===
'new' ?
'MediaWiki:addsection-preload' :
'' );
1161 if ( $this->section !=
'' ) {
1164 $content = $orig ? $orig->getSection( $this->section ) :
null;
1170 $undoafter =
$wgRequest->getInt(
'undoafter' );
1173 if ( $undo > 0 && $undoafter > 0 ) {
1177 # Sanity check, make sure it's the right page,
1178 # the revisions exist and they were not deleted.
1179 # Otherwise, $content will be left as-is.
1180 if ( !is_null( $undorev ) && !is_null( $oldrev ) &&
1184 $content = $this->
page->getUndoContent( $undorev, $oldrev );
1187 # Warn the user that something went wrong
1188 $undoMsg =
'failure';
1192 $newContent =
$content->preSaveTransform( $this->mTitle,
$wgUser, $popts );
1193 if ( $newContent->getModel() !== $oldContent->getModel() ) {
1198 $this->contentModel = $newContent->getModel();
1199 $this->contentFormat = $oldrev->getContentFormat();
1202 if ( $newContent->equals( $oldContent ) ) {
1203 # Tell the user that the undo results in no change,
1204 # i.e. the revisions were already undone.
1205 $undoMsg =
'nochange';
1208 # Inform the user of our success and set an automatic edit summary
1209 $undoMsg =
'success';
1211 # If we just undid one rev, use an autosummary
1212 $firstrev = $oldrev->getNext();
1213 if ( $firstrev && $firstrev->getId() == $undo ) {
1214 $userText = $undorev->getUserText();
1215 if ( $userText ===
'' ) {
1216 $undoSummary = $this->context->msg(
1217 'undo-summary-username-hidden',
1219 )->inContentLanguage()->text();
1221 $undoSummary = $this->context->msg(
1225 )->inContentLanguage()->text();
1227 if ( $this->summary ===
'' ) {
1228 $this->summary = $undoSummary;
1230 $this->summary = $undoSummary . $this->context->msg(
'colon-separator' )
1233 $this->undidRev = $undo;
1235 $this->formtype =
'diff';
1246 $class = ( $undoMsg ==
'success' ?
'' :
'error ' ) .
"mw-undo-{$undoMsg}";
1247 $this->editFormPageTop .=
$wgOut->parse(
"<div class=\"{$class}\">" .
1248 $this->context->msg(
'undo-' . $undoMsg )->plain() .
'</div>',
true,
true );
1276 if ( $this->section ==
'new' ) {
1279 $revision = $this->mArticle->getRevisionFetched();
1280 if ( $revision ===
null ) {
1282 return $handler->makeEmptyContent();
1301 if ( $this->parentRevId ) {
1304 return $this->mArticle->getRevIdFetched();
1322 return $handler->makeEmptyContent();
1323 } elseif ( !$this->undidRev ) {
1328 $logger = LoggerFactory::getInstance(
'editpage' );
1329 if ( $this->contentModel !==
$rev->getContentModel() ) {
1330 $logger->warning(
"Overriding content model from current edit {prev} to {new}", [
1331 'prev' => $this->contentModel,
1332 'new' =>
$rev->getContentModel(),
1333 'title' => $this->
getTitle()->getPrefixedDBkey(),
1334 'method' => __METHOD__
1336 $this->contentModel =
$rev->getContentModel();
1341 if ( !
$content->isSupportedFormat( $this->contentFormat ) ) {
1342 $logger->warning(
"Current revision content format unsupported. Overriding {prev} to {new}", [
1344 'prev' => $this->contentFormat,
1345 'new' =>
$rev->getContentFormat(),
1346 'title' => $this->
getTitle()->getPrefixedDBkey(),
1347 'method' => __METHOD__
1349 $this->contentFormat =
$rev->getContentFormat();
1380 if ( !empty( $this->mPreloadContent ) ) {
1386 if ( $preload ===
'' ) {
1387 return $handler->makeEmptyContent();
1391 # Check for existence to avoid getting MediaWiki:Noarticletext
1394 return $handler->makeEmptyContent();
1403 return $handler->makeEmptyContent();
1413 return $handler->makeEmptyContent();
1419 if ( !$converted ) {
1421 wfDebug(
"Attempt to preload incompatible content: " .
1422 "can't convert " .
$content->getModel() .
1425 return $handler->makeEmptyContent();
1443 $token =
$request->getVal(
'wpEditToken' );
1444 $this->mTokenOk =
$wgUser->matchEditToken( $token );
1445 $this->mTokenOkExceptSuffix =
$wgUser->matchEditTokenNoSuffix( $token );
1466 $revisionId = $this->
page->getLatest();
1467 $postEditKey = self::POST_EDIT_COOKIE_KEY_PREFIX . $revisionId;
1470 if ( $statusValue == self::AS_SUCCESS_NEW_ARTICLE ) {
1472 } elseif ( $this->oldid ) {
1477 $response->setCookie( $postEditKey, $val, time() + self::POST_EDIT_COOKIE_DURATION, [
1478 'httpOnly' =>
false,
1491 # Allow bots to exempt some edits from bot flagging
1495 Hooks::run(
'EditPage::attemptSave:after', [ $this,
$status, $resultDetails ] );
1516 if (
$status->value == self::AS_SUCCESS_UPDATE
1517 ||
$status->value == self::AS_SUCCESS_NEW_ARTICLE
1519 $this->didSave =
true;
1520 if ( !$resultDetails[
'nullEdit'] ) {
1528 $extraQueryRedirect =
$request->getVal(
'wpExtraQueryRedirect' );
1548 $wgOut->addWikiText(
'<div class="error">' .
"\n" .
$status->getWikiText() .
'</div>' );
1552 $query = $resultDetails[
'redirect'] ?
'redirect=no' :
'';
1553 if ( $extraQueryRedirect ) {
1555 $query = $extraQueryRedirect;
1560 $anchor = isset( $resultDetails[
'sectionanchor'] ) ? $resultDetails[
'sectionanchor'] :
'';
1561 $wgOut->redirect( $this->mTitle->getFullURL(
$query ) . $anchor );
1566 $sectionanchor = $resultDetails[
'sectionanchor'];
1570 'ArticleUpdateBeforeRedirect',
1571 [ $this->mArticle, &$sectionanchor, &$extraQuery ]
1574 if ( $resultDetails[
'redirect'] ) {
1575 if ( $extraQuery ==
'' ) {
1576 $extraQuery =
'redirect=no';
1578 $extraQuery =
'redirect=no&' . $extraQuery;
1581 if ( $extraQueryRedirect ) {
1582 if ( $extraQuery ===
'' ) {
1583 $extraQuery = $extraQueryRedirect;
1585 $extraQuery = $extraQuery .
'&' . $extraQueryRedirect;
1589 $wgOut->redirect( $this->mTitle->getFullURL( $extraQuery ) . $sectionanchor );
1614 $permission = $this->mTitle->isTalkPage() ?
'createtalk' :
'createpage';
1625 $this->hookError =
'<div class="error">' .
"\n" .
$status->getWikiText() .
1642 if ( $this->hookError !=
'' ) {
1643 # ...or the hook could be expecting us to produce an error
1644 $status->fatal(
'hookaborted' );
1654 # Error messages etc. could be handled within the hook...
1656 $status->fatal(
'hookaborted' );
1662 $this->hookError =
$status->getWikiText();
1669 } elseif ( !
$status->isOK() ) {
1670 # ...or the hook could be expecting us to produce an error
1672 $this->hookError =
$status->getWikiText();
1673 $status->fatal(
'hookaborted' );
1690 if ( $this->sectiontitle !==
'' ) {
1691 $sectionanchor =
$wgParser->guessLegacySectionNameFromWikiText( $this->sectiontitle );
1695 if ( $this->summary ===
'' ) {
1696 $cleanSectionTitle =
$wgParser->stripSectionName( $this->sectiontitle );
1697 return $this->context->msg(
'newsectionsummary' )
1698 ->rawParams( $cleanSectionTitle )->inContentLanguage()->text();
1700 } elseif ( $this->summary !==
'' ) {
1701 $sectionanchor =
$wgParser->guessLegacySectionNameFromWikiText( $this->summary );
1702 # This is a new section, so create a link to the new section
1703 # in the revision summary.
1704 $cleanSummary =
$wgParser->stripSectionName( $this->summary );
1705 return $this->context->msg(
'newsectionsummary' )
1706 ->rawParams( $cleanSummary )->inContentLanguage()->text();
1737 global $wgContentHandlerUseDB;
1741 if ( !
Hooks::run(
'EditPage::attemptSave', [ $this ] ) ) {
1742 wfDebug(
"Hook 'EditPage::attemptSave' aborted article saving\n" );
1743 $status->fatal(
'hookaborted' );
1749 if ( $spam !==
'' ) {
1754 $this->mTitle->getPrefixedText() .
1755 '" submitted bogus field "' .
1759 $status->fatal(
'spamprotectionmatch',
false );
1765 # Construct Content object
1769 'content-failed-to-parse',
1770 $this->contentModel,
1771 $this->contentFormat,
1778 # Check image redirect
1779 if ( $this->mTitle->getNamespace() ==
NS_FILE &&
1780 $textbox_content->isRedirect() &&
1781 !
$wgUser->isAllowed(
'upload' )
1791 if ( $match ===
false && $this->section ==
'new' ) {
1792 # $wgSpamRegex is enforced on this new heading/summary because, unlike
1793 # regular summaries, it is added to the actual wikitext.
1794 if ( $this->sectiontitle !==
'' ) {
1795 # This branch is taken when the API is used with the 'sectiontitle' parameter.
1798 # This branch is taken when the "Add Topic" user interface is used, or the API
1799 # is used with the 'summary' parameter.
1803 if ( $match ===
false ) {
1806 if ( $match !==
false ) {
1809 $pdbk = $this->mTitle->getPrefixedDBkey();
1810 $match = str_replace(
"\n",
'', $match );
1811 wfDebugLog(
'SpamRegex',
"$ip spam regex hit [[$pdbk]]: \"$match\"" );
1812 $status->fatal(
'spamprotectionmatch', $match );
1818 [ $this, $this->textbox1, $this->section, &$this->hookError, $this->summary ] )
1820 # Error messages etc. could be handled within the hook...
1821 $status->fatal(
'hookaborted' );
1824 } elseif ( $this->hookError !=
'' ) {
1825 # ...or the hook could be expecting us to produce an error
1826 $status->fatal(
'hookaborted' );
1831 if (
$wgUser->isBlockedFrom( $this->mTitle,
false ) ) {
1834 $wgUser->spreadAnyEditBlock();
1836 # Check block state against master, thus 'false'.
1837 $status->setResult(
false, self::AS_BLOCKED_PAGE_FOR_USER );
1841 $this->contentLength = strlen( $this->textbox1 );
1844 $this->tooBig =
true;
1845 $status->setResult(
false, self::AS_CONTENT_TOO_BIG );
1849 if ( !
$wgUser->isAllowed(
'edit' ) ) {
1851 $status->setResult(
false, self::AS_READ_ONLY_PAGE_ANON );
1854 $status->fatal(
'readonlytext' );
1860 $changingContentModel =
false;
1861 if ( $this->contentModel !== $this->mTitle->getContentModel() ) {
1862 if ( !$wgContentHandlerUseDB ) {
1863 $status->fatal(
'editpage-cannot-use-custom-model' );
1866 } elseif ( !
$wgUser->isAllowed(
'editcontentmodel' ) ) {
1867 $status->setResult(
false, self::AS_NO_CHANGE_CONTENT_MODEL );
1873 if ( !$titleWithNewContentModel->userCan(
'editcontentmodel',
$wgUser )
1874 || !$titleWithNewContentModel->userCan(
'edit',
$wgUser )
1876 $status->setResult(
false, self::AS_NO_CHANGE_CONTENT_MODEL );
1880 $changingContentModel =
true;
1881 $oldContentModel = $this->mTitle->getContentModel();
1884 if ( $this->changeTags ) {
1887 if ( !$changeTagsStatus->isOK() ) {
1889 return $changeTagsStatus;
1894 $status->fatal(
'readonlytext' );
1898 if (
$wgUser->pingLimiter() ||
$wgUser->pingLimiter(
'linkpurge', 0 )
1899 || ( $changingContentModel &&
$wgUser->pingLimiter(
'editcontentmodel' ) )
1901 $status->fatal(
'actionthrottledtext' );
1906 # If the article has been deleted while editing, don't save it without
1909 $status->setResult(
false, self::AS_ARTICLE_WAS_DELETED );
1913 # Load the page data from the master. If anything changes in the meantime,
1914 # we detect it by using page_latest like a token in a 1 try compare-and-swap.
1915 $this->
page->loadPageData(
'fromdbmaster' );
1916 $new = !$this->
page->exists();
1920 if ( !$this->mTitle->userCan(
'create',
$wgUser ) ) {
1921 $status->fatal(
'nocreatetext' );
1923 wfDebug( __METHOD__ .
": no create permission\n" );
1930 $defaultMessageText = $this->mTitle->getDefaultMessageText();
1931 if ( $this->mTitle->getNamespace() ===
NS_MEDIAWIKI && $defaultMessageText !==
false ) {
1932 $defaultText = $defaultMessageText;
1937 if ( !$this->allowBlankArticle && $this->textbox1 === $defaultText ) {
1938 $this->blankArticle =
true;
1939 $status->fatal(
'blankarticle' );
1940 $status->setResult(
false, self::AS_BLANK_ARTICLE );
1950 $result[
'sectionanchor'] =
'';
1951 if ( $this->section ==
'new' ) {
1952 if ( $this->sectiontitle !==
'' ) {
1955 } elseif ( $this->summary !==
'' ) {
1966 # Article exists. Check for edit conflict.
1968 $this->
page->clear(); # Force reload
of dates,
etc.
1969 $timestamp = $this->
page->getTimestamp();
1970 $latest = $this->
page->getLatest();
1972 wfDebug(
"timestamp: {$timestamp}, edittime: {$this->edittime}\n" );
1975 if ( $timestamp != $this->edittime
1976 || ( $this->editRevId !==
null && $this->editRevId != $latest )
1978 $this->isConflict =
true;
1979 if ( $this->section ==
'new' ) {
1980 if ( $this->
page->getUserText() == $wgUser->getName() &&
1987 .
": duplicate new section submission; trigger edit conflict!\n" );
1990 $this->isConflict =
false;
1991 wfDebug( __METHOD__ .
": conflict suppressed; new section\n" );
1993 } elseif ( $this->section ==
''
1995 DB_MASTER, $this->mTitle->getArticleID(),
1999 # Suppress edit conflict with self, except for section edits where merging is required.
2000 wfDebug( __METHOD__ .
": Suppressing edit conflict, same user.\n" );
2001 $this->isConflict =
false;
2006 if ( $this->sectiontitle !==
'' ) {
2014 if ( $this->isConflict ) {
2016 .
": conflict! getting section '{$this->section}' for time '{$this->edittime}'"
2017 .
" (id '{$this->editRevId}') (article time '{$timestamp}')\n" );
2020 if ( $this->editRevId !==
null ) {
2036 wfDebug( __METHOD__ .
": getting section '{$this->section}'\n" );
2045 wfDebug( __METHOD__ .
": activating conflict; section replace failed.\n" );
2046 $this->isConflict =
true;
2048 } elseif ( $this->isConflict ) {
2052 $this->isConflict =
false;
2053 wfDebug( __METHOD__ .
": Suppressing edit conflict, successful merge.\n" );
2055 $this->section =
'';
2057 wfDebug( __METHOD__ .
": Keeping edit conflict, failed merge.\n" );
2061 if ( $this->isConflict ) {
2062 $status->setResult(
false, self::AS_CONFLICT_DETECTED );
2070 if ( $this->section ==
'new' ) {
2072 if ( !$this->allowBlankSummary && trim( $this->summary ) ==
'' ) {
2073 $this->missingSummary =
true;
2074 $status->fatal(
'missingsummary' );
2080 if ( $this->textbox1 ==
'' ) {
2081 $this->missingComment =
true;
2082 $status->fatal(
'missingcommenttext' );
2086 } elseif ( !$this->allowBlankSummary
2091 $this->missingSummary =
true;
2092 $status->fatal(
'missingsummary' );
2098 $sectionanchor =
'';
2099 if ( $this->section ==
'new' ) {
2101 } elseif ( $this->section !=
'' ) {
2102 # Try to get a section anchor from the section source, redirect
2103 # to edited section if header found.
2104 # XXX: Might be better to integrate this into Article::replaceSectionAtRev
2105 # for duplicate heading checking and maybe parsing.
2106 $hasmatch = preg_match(
"/^ *([=]{1,6})(.*?)(\\1) *\\n/i", $this->textbox1,
$matches );
2107 # We can't deal with anchors, includes, html etc in the header for now,
2108 # headline would need to be parsed to improve this.
2109 if ( $hasmatch && strlen(
$matches[2] ) > 0 ) {
2113 $result[
'sectionanchor'] = $sectionanchor;
2120 $this->section =
'';
2125 if ( !$this->allowSelfRedirect
2131 if ( !$currentTarget || !$currentTarget->equals( $this->getTitle() ) ) {
2132 $this->selfRedirect =
true;
2133 $status->fatal(
'selfredirect' );
2142 $this->tooBig =
true;
2143 $status->setResult(
false, self::AS_MAX_ARTICLE_SIZE_EXCEEDED );
2149 ( ( $this->minoredit && !$this->isNew ) ?
EDIT_MINOR : 0 ) |
2152 $doEditStatus = $this->
page->doEditContent(
2163 if ( !$doEditStatus->isOK() ) {
2167 $errors = $doEditStatus->getErrorsArray();
2168 if ( in_array( $errors[0][0],
2169 [
'edit-gone-missing',
'edit-conflict',
'edit-already-exists' ] )
2171 $this->isConflict =
true;
2175 return $doEditStatus;
2178 $result[
'nullEdit'] = $doEditStatus->hasMessage(
'edit-no-change' );
2181 $wgUser->pingLimiter(
'linkpurge' );
2188 if ( $changingContentModel ) {
2191 $new ?
false : $oldContentModel,
2192 $this->contentModel,
2207 $new = $oldModel ===
false;
2208 $log =
new ManualLogEntry(
'contentmodel', $new ?
'new' :
'change' );
2209 $log->setPerformer(
$user );
2210 $log->setTarget( $this->mTitle );
2211 $log->setComment( $reason );
2212 $log->setParameters( [
2213 '4::oldmodel' => $oldModel,
2214 '5::newmodel' => $newModel
2216 $logid = $log->insert();
2217 $log->publish( $logid );
2226 if ( !
$wgUser->isLoggedIn() ) {
2258 $baseContent = $baseRevision ? $baseRevision->getContent() :
null;
2260 if ( is_null( $baseContent ) ) {
2266 $currentContent = $currentRevision ? $currentRevision->getContent() :
null;
2268 if ( is_null( $currentContent ) ) {
2274 $result =
$handler->merge3( $baseContent, $editContent, $currentContent );
2279 $this->parentRevId = $currentRevision->getId();
2292 if ( !$this->mBaseRevision ) {
2294 $this->mBaseRevision = $this->editRevId
2323 global $wgSummarySpamRegex;
2336 if ( preg_match( $regex, $text,
$matches ) ) {
2346 $wgOut->addModules(
'mediawiki.action.edit' );
2347 $wgOut->addModuleStyles(
'mediawiki.action.edit.styles' );
2349 if (
$wgUser->getOption(
'showtoolbar' ) ) {
2354 $wgOut->addModules(
'mediawiki.toolbar' );
2357 if (
$wgUser->getOption(
'uselivepreview' ) ) {
2358 $wgOut->addModules(
'mediawiki.action.edit.preview' );
2361 if (
$wgUser->getOption(
'useeditwarning' ) ) {
2362 $wgOut->addModules(
'mediawiki.action.edit.editWarning' );
2365 # Enabled article-related sidebar, toplinks, etc.
2366 $wgOut->setArticleRelated(
true );
2369 if ( $this->isConflict ) {
2370 $msg =
'editconflict';
2371 } elseif ( $contextTitle->exists() && $this->section !=
'' ) {
2372 $msg = $this->section ==
'new' ?
'editingcomment' :
'editingsection';
2374 $msg = $contextTitle->exists()
2376 && $contextTitle->getDefaultMessageText() !==
false
2382 # Use the title defined by DISPLAYTITLE magic word when present
2383 # NOTE: getDisplayTitle() returns HTML while getPrefixedText() returns plain text.
2384 # setPageTitle() treats the input as wikitext, which should be safe in either case.
2385 $displayTitle = isset( $this->mParserOutput ) ? $this->mParserOutput->getDisplayTitle() :
false;
2386 if ( $displayTitle ===
false ) {
2387 $displayTitle = $contextTitle->getPrefixedText();
2389 $wgOut->setPageTitle( $this->context->msg( $msg, $displayTitle ) );
2390 # Transmit the name of the message to JavaScript for live preview
2391 # Keep Resources.php/mediawiki.action.edit.preview in sync with the possible keys
2392 $wgOut->addJsConfigVars( [
2393 'wgEditMessage' => $msg,
2394 'wgAjaxEditStash' => $wgAjaxEditStash,
2403 if ( $this->suppressIntro ) {
2407 $namespace = $this->mTitle->getNamespace();
2410 # Show a warning if editing an interface message
2411 $wgOut->wrapWikiMsg(
"<div class='mw-editinginterface'>\n$1\n</div>",
'editinginterface' );
2412 # If this is a default message (but not css or js),
2413 # show a hint that it is translatable on translatewiki.net
2417 $defaultMessageText = $this->mTitle->getDefaultMessageText();
2418 if ( $defaultMessageText !==
false ) {
2419 $wgOut->wrapWikiMsg(
"<div class='mw-translateinterface'>\n$1\n</div>",
2420 'translateinterface' );
2423 } elseif ( $namespace ==
NS_FILE ) {
2424 # Show a hint to shared repo
2426 if ( $file && !$file->isLocal() ) {
2427 $descUrl = $file->getDescriptionUrl();
2428 # there must be a description url to show a hint to shared repo
2430 if ( !$this->mTitle->exists() ) {
2431 $wgOut->wrapWikiMsg(
"<div class=\"mw-sharedupload-desc-create\">\n$1\n</div>", [
2432 'sharedupload-desc-create', $file->getRepo()->getDisplayName(), $descUrl
2435 $wgOut->wrapWikiMsg(
"<div class=\"mw-sharedupload-desc-edit\">\n$1\n</div>", [
2436 'sharedupload-desc-edit', $file->getRepo()->getDisplayName(), $descUrl
2443 # Show a warning message when someone creates/edits a user (talk) page but the user does not exist
2444 # Show log extract when the user is currently blocked
2446 $username = explode(
'/', $this->mTitle->getText(), 2 )[0];
2451 $wgOut->wrapWikiMsg(
"<div class=\"mw-userpage-userdoesnotexist error\">\n$1\n</div>",
2454 # Show log extract if the user is currently blocked
2462 'showIfEmpty' =>
false,
2464 'blocked-notice-logextract',
2465 $user->getName() # Support GENDER
in notice
2471 # Try to add a custom edit intro, or use the standard one if this is not possible.
2474 $this->context->msg(
'helppage' )->inContentLanguage()->text()
2476 if (
$wgUser->isLoggedIn() ) {
2479 "<div class=\"mw-newarticletext plainlinks\">\n$1\n</div>",
2488 "<div class=\"mw-newarticletextanon plainlinks\">\n$1\n</div>",
2490 'newarticletextanon',
2496 # Give a notice if the user is editing a deleted/moved page...
2497 if ( !$this->mTitle->exists() ) {
2504 'conds' => [
'log_action != ' .
$dbr->addQuotes(
'revision' ) ],
2505 'showIfEmpty' =>
false,
2506 'msgKey' => [
'recreate-moveddeleted-warn' ]
2518 if ( $this->editintro ) {
2523 $wgOut->addWikiTextTitleTidy(
2524 '<div class="mw-editintro">{{:' .
$title->getFullText() .
'}}</div>',
2560 return $content->serialize( $this->contentFormat );
2580 if ( $text ===
false || $text ===
null ) {
2585 $this->contentModel, $this->contentFormat );
2605 # need to parse the preview early so that we know which templates are used,
2606 # otherwise users with "show preview after edit box" will get a blank list
2607 # we parse this near the beginning so that setHeaders can do the title
2608 # setting work instead of leaving it in getPreviewText
2609 $previewOutput =
'';
2610 if ( $this->formtype ==
'preview' ) {
2623 if ( !$this->isConflict &&
2624 $this->section !=
'' &&
2629 $wgOut->showErrorPage(
'sectioneditnotsupported-title',
'sectioneditnotsupported-text' );
2635 $wgOut->addHTML( $this->editFormPageTop );
2637 if (
$wgUser->getOption(
'previewontop' ) ) {
2641 $wgOut->addHTML( $this->editFormTextTop );
2643 $showToolbar =
true;
2645 if ( $this->formtype ==
'save' ) {
2648 $showToolbar =
false;
2650 $wgOut->wrapWikiMsg(
"<div class='error mw-deleted-while-editing'>\n$1\n</div>",
2651 'deletedwhileediting' );
2660 'class' => $this->oouiEnabled ?
'mw-editform-ooui' :
'mw-editform-legacy',
2661 'id' => self::EDITFORM_ID,
2662 'name' => self::EDITFORM_ID,
2665 'enctype' =>
'multipart/form-data'
2669 if ( is_callable( $formCallback ) ) {
2670 wfWarn(
'The $formCallback parameter to ' . __METHOD__ .
'is deprecated' );
2671 call_user_func_array( $formCallback, [ &
$wgOut ] );
2676 Xml::openElement(
'div', [
'id' =>
'antispam-container',
'style' =>
'display: none;' ] )
2679 [
'for' =>
'wpAntispam' ],
2680 $this->context->msg(
'simpleantispam-label' )->parse()
2686 'name' =>
'wpAntispam',
2687 'id' =>
'wpAntispam',
2702 $username = $this->lastDelete->user_name;
2703 $comment = $this->lastDelete->log_comment;
2707 $key = $comment ===
''
2708 ?
'confirmrecreate-noreason'
2709 :
'confirmrecreate';
2711 '<div class="mw-confirm-recreate">' .
2712 $this->context->msg( $key,
$username,
"<nowiki>$comment</nowiki>" )->parse() .
2713 Xml::checkLabel( $this->context->msg(
'recreate' )->text(),
'wpRecreate',
'wpRecreate',
false,
2720 # When the summary is hidden, also hide them on preview/show changes
2721 if ( $this->nosummary ) {
2725 # If a blank edit summary was previously provided, and the appropriate
2726 # user preference is active, pass a hidden tag as wpIgnoreBlankSummary. This will stop the
2727 # user being bounced back more than once in the event that a summary
2730 # For a bit more sophisticated detection of blank summaries, hash the
2731 # automatic one and pass that in the hidden field wpAutoSummary.
2732 if ( $this->missingSummary || ( $this->section ==
'new' && $this->nosummary ) ) {
2736 if ( $this->undidRev ) {
2740 if ( $this->selfRedirect ) {
2744 if ( $this->hasPresetSummary ) {
2748 $this->autoSumm = md5(
'' );
2751 $autosumm = $this->autoSumm ? $this->autoSumm : md5( $this->summary );
2757 $wgOut->addHTML(
Html::hidden(
'format', $this->contentFormat ) );
2761 if ( $this->oouiEnabled ) {
2765 if ( $this->section ==
'new' ) {
2770 $wgOut->addHTML( $this->editFormTextBeforeContent );
2772 if ( !$this->isCssJsSubpage && $showToolbar &&
$wgUser->getOption(
'showtoolbar' ) ) {
2776 if ( $this->blankArticle ) {
2780 if ( $this->isConflict ) {
2795 $wgOut->addHTML( $this->editFormTextAfterContent );
2805 $wgOut->addHTML( $this->editFormTextAfterTools .
"\n" );
2813 self::getPreviewLimitReport( $this->mParserOutput ) ) );
2815 $wgOut->addModules(
'mediawiki.action.edit.collapsibleFooter' );
2817 if ( $this->isConflict ) {
2822 $msg = $this->context->msg(
2823 'content-failed-to-parse',
2824 $this->contentModel,
2825 $this->contentFormat,
2828 $wgOut->addWikiText(
'<div class="error">' . $msg->text() .
'</div>' );
2833 if ( $this->isConflict ) {
2835 } elseif ( $this->preview ) {
2837 } elseif ( $this->
diff ) {
2847 $wgOut->addHTML( $this->editFormTextBottom .
"\n</form>\n" );
2849 if ( !
$wgUser->getOption(
'previewontop' ) ) {
2863 $this->context, MediaWikiServices::getInstance()->getLinkRenderer()
2868 if ( $this->preview ) {
2870 } elseif ( $this->section !=
'' ) {
2875 $templateListFormatter->format( $templates,
$type )
2886 preg_match(
"/^(=+)(.+)\\1\\s*(\n|$)/i", $text,
$matches );
2897 global $wgAllowUserCss, $wgAllowUserJs;
2899 if ( $this->isConflict ) {
2901 $this->editRevId = $this->
page->getLatest();
2903 if ( $this->section !=
'' && $this->section !=
'new' ) {
2904 if ( !$this->summary && !$this->preview && !$this->
diff ) {
2906 if ( $sectionTitle !==
false ) {
2907 $this->summary =
"/* $sectionTitle */ ";
2912 if ( $this->missingComment ) {
2913 $wgOut->wrapWikiMsg(
"<div id='mw-missingcommenttext'>\n$1\n</div>",
'missingcommenttext' );
2916 if ( $this->missingSummary && $this->section !=
'new' ) {
2917 $wgOut->wrapWikiMsg(
"<div id='mw-missingsummary'>\n$1\n</div>",
'missingsummary' );
2920 if ( $this->missingSummary && $this->section ==
'new' ) {
2921 $wgOut->wrapWikiMsg(
"<div id='mw-missingcommentheader'>\n$1\n</div>",
'missingcommentheader' );
2924 if ( $this->blankArticle ) {
2925 $wgOut->wrapWikiMsg(
"<div id='mw-blankarticle'>\n$1\n</div>",
'blankarticle' );
2928 if ( $this->selfRedirect ) {
2929 $wgOut->wrapWikiMsg(
"<div id='mw-selfredirect'>\n$1\n</div>",
'selfredirect' );
2932 if ( $this->hookError !==
'' ) {
2933 $wgOut->addWikiText( $this->hookError );
2937 $wgOut->addWikiMsg(
'nonunicodebrowser' );
2940 if ( $this->section !=
'new' ) {
2941 $revision = $this->mArticle->getRevisionFetched();
2947 "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
2948 'rev-deleted-text-permission'
2952 "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
2953 'rev-deleted-text-view'
2957 if ( !$revision->isCurrent() ) {
2958 $this->mArticle->setOldSubtitle( $revision->getId() );
2959 $wgOut->addWikiMsg(
'editingold' );
2960 $this->isOldRev =
true;
2962 } elseif ( $this->mTitle->exists() ) {
2965 $wgOut->wrapWikiMsg(
"<div class='errorbox'>\n$1\n</div>\n",
2966 [
'missing-revision', $this->oldid ] );
2973 "<div id=\"mw-read-only-warning\">\n$1\n</div>",
2976 } elseif (
$wgUser->isAnon() ) {
2977 if ( $this->formtype !=
'preview' ) {
2979 "<div id='mw-anon-edit-warning' class='warningbox'>\n$1\n</div>",
2980 [
'anoneditwarning',
2983 'returnto' => $this->
getTitle()->getPrefixedDBkey()
2987 'returnto' => $this->
getTitle()->getPrefixedDBkey()
2992 $wgOut->wrapWikiMsg(
"<div id=\"mw-anon-preview-warning\" class=\"warningbox\">\n$1</div>",
2993 'anonpreviewwarning'
2997 if ( $this->isCssJsSubpage ) {
2998 # Check the skin exists
3000 $wgOut->wrapWikiMsg(
3001 "<div class='error' id='mw-userinvalidcssjstitle'>\n$1\n</div>",
3002 [
'userinvalidcssjstitle', $this->mTitle->getSkinFromCssJsSubpage() ]
3005 if ( $this->
getTitle()->isSubpageOf( $wgUser->getUserPage() ) ) {
3006 $wgOut->wrapWikiMsg(
'<div class="mw-usercssjspublic">$1</div>',
3007 $this->isCssSubpage ?
'usercssispublic' :
'userjsispublic'
3009 if ( $this->formtype !==
'preview' ) {
3010 if ( $this->isCssSubpage && $wgAllowUserCss ) {
3012 "<div id='mw-usercssyoucanpreview'>\n$1\n</div>",
3013 [
'usercssyoucanpreview' ]
3017 if ( $this->isJsSubpage && $wgAllowUserJs ) {
3019 "<div id='mw-userjsyoucanpreview'>\n$1\n</div>",
3020 [
'userjsyoucanpreview' ]
3032 # Add header copyright warning
3045 return ( is_array( $inputAttrs ) ? $inputAttrs : [] ) + [
3046 'id' =>
'wpSummary',
3047 'name' =>
'wpSummary',
3048 'maxlength' =>
'200',
3051 'spellcheck' =>
'true',
3070 $inputAttrs =
null, $spanLabelAttrs =
null
3074 $spanLabelAttrs = ( is_array( $spanLabelAttrs ) ? $spanLabelAttrs : [] ) + [
3075 'class' => $this->missingSummary ?
'mw-summarymissed' :
'mw-summary',
3076 'id' =>
"wpSummaryLabel"
3083 $inputAttrs[
'id'] ? [
'for' => $inputAttrs[
'id'] ] :
null,
3086 $label =
Xml::tags(
'span', $spanLabelAttrs, $label );
3091 return [ $label,
$input ];
3105 $inputAttrs = OOUI\Element::configFromHtmlAttributes(
3109 return new OOUI\FieldLayout(
3110 new OOUI\TextInputWidget( [
3112 'infusable' =>
true,
3115 'label' =>
new OOUI\HtmlSnippet( $labelText ),
3117 'id' =>
'wpSummaryLabel',
3118 'classes' => [ $this->missingSummary ?
'mw-summarymissed' :
'mw-summary' ],
3132 # Add a class if 'missingsummary' is triggered to allow styling of the summary line
3133 $summaryClass = $this->missingSummary ?
'mw-summarymissed' :
'mw-summary';
3134 if ( $isSubjectPreview ) {
3135 if ( $this->nosummary ) {
3139 if ( !$this->mShowSummaryField ) {
3144 $labelText = $this->context->msg( $isSubjectPreview ?
'subject' :
'summary' )->parse();
3145 if ( $this->oouiEnabled ) {
3149 [
'class' => $summaryClass ]
3155 [
'class' => $summaryClass ]
3157 $wgOut->addHTML(
"{$label} {$input}" );
3172 if ( !
$summary || ( !$this->preview && !$this->
diff ) ) {
3178 if ( $isSubjectPreview ) {
3179 $summary = $this->context->msg(
'newsectionsummary' )
3181 ->inContentLanguage()->text();
3184 $message = $isSubjectPreview ?
'subject-preview' :
'summary-preview';
3186 $summary = $this->context->msg( $message )->parse()
3193 $section = htmlspecialchars( $this->section );
3196 <input
type=
'hidden' value=
"{$this->starttime}" name=
"wpStarttime" />
3197 <input
type=
'hidden' value=
"{$this->edittime}" name=
"wpEdittime" />
3198 <input
type=
'hidden' value=
"{$this->editRevId}" name=
"editRevId" />
3199 <input
type=
'hidden' value=
"{$this->scrolltop}" name=
"wpScrolltop" id=
"wpScrolltop" />
3247 $attribs = [
'style' =>
'display:none;' ];
3250 if ( $this->mTitle->isProtected(
'edit' ) &&
3253 # Is the title semi-protected?
3254 if ( $this->mTitle->isSemiProtected() ) {
3255 $classes[] =
'mw-textarea-sprotected';
3257 # Then it must be protected based on static groups (regular)
3258 $classes[] =
'mw-textarea-protected';
3260 # Is the title cascade-protected?
3261 if ( $this->mTitle->isCascadeProtected() ) {
3262 $classes[] =
'mw-textarea-cprotected';
3265 # Is an old revision being edited?
3266 if ( $this->isOldRev ) {
3267 $classes[] =
'mw-textarea-oldrev';
3276 if (
count( $classes ) ) {
3277 if ( isset(
$attribs[
'class'] ) ) {
3280 $attribs[
'class'] = implode(
' ', $classes );
3285 $textoverride !==
null ? $textoverride : $this->textbox1,
3292 $this->
showTextbox( $this->textbox2,
'wpTextbox2', [
'tabindex' => 6,
'readonly' ] );
3310 $classes[] =
'ontop';
3313 $attribs = [
'id' =>
'wikiPreview',
'class' => implode(
' ', $classes ) ];
3315 if ( $this->formtype !=
'preview' ) {
3316 $attribs[
'style'] =
'display: none;';
3321 if ( $this->formtype ==
'preview' ) {
3325 $pageViewLang = $this->mTitle->getPageViewLanguage();
3326 $attribs = [
'lang' => $pageViewLang->getHtmlCode(),
'dir' => $pageViewLang->getDir(),
3327 'class' =>
'mw-content-' . $pageViewLang->getDir() ];
3331 $wgOut->addHTML(
'</div>' );
3333 if ( $this->formtype ==
'diff' ) {
3337 $msg = $this->context->msg(
3338 'content-failed-to-parse',
3339 $this->contentModel,
3340 $this->contentFormat,
3343 $wgOut->addWikiText(
'<div class="error">' . $msg->text() .
'</div>' );
3357 $this->mArticle->openShowCategory();
3359 # This hook seems slightly odd here, but makes things more
3360 # consistent for extensions.
3362 $wgOut->addHTML( $text );
3364 $this->mArticle->closeShowCategory();
3378 $oldtitlemsg =
'currentrev';
3379 # if message does not exist, show diff against the preloaded default
3380 if ( $this->mTitle->getNamespace() ==
NS_MEDIAWIKI && !$this->mTitle->exists() ) {
3381 $oldtext = $this->mTitle->getDefaultMessageText();
3382 if ( $oldtext !==
false ) {
3383 $oldtitlemsg =
'defaultmessagetext';
3393 if ( $this->editRevId !==
null ) {
3394 $newContent = $this->
page->replaceSectionAtRev(
3395 $this->section, $textboxContent, $this->summary, $this->editRevId
3398 $newContent = $this->
page->replaceSectionContent(
3399 $this->section, $textboxContent, $this->summary, $this->edittime
3403 if ( $newContent ) {
3404 Hooks::run(
'EditPageGetDiffContent', [ $this, &$newContent ] );
3407 $newContent = $newContent->preSaveTransform( $this->mTitle,
$wgUser, $popts );
3410 if ( ( $oldContent && !$oldContent->isEmpty() ) || ( $newContent && !$newContent->isEmpty() ) ) {
3411 $oldtitle = $this->context->msg( $oldtitlemsg )->parse();
3412 $newtitle = $this->context->msg(
'yourtext' )->parse();
3414 if ( !$oldContent ) {
3415 $oldContent = $newContent->getContentHandler()->makeEmptyContent();
3418 if ( !$newContent ) {
3419 $newContent = $oldContent->getContentHandler()->makeEmptyContent();
3422 $de = $oldContent->getContentHandler()->createDifferenceEngine( $this->mArticle->getContext() );
3423 $de->setContent( $oldContent, $newContent );
3425 $difftext = $de->getDiff( $oldtitle, $newtitle );
3426 $de->showDiffStyle();
3431 $wgOut->addHTML(
'<div id="wikiDiff">' . $difftext .
'</div>' );
3438 $msg =
'editpage-head-copy-warn';
3439 if ( !$this->context->msg( $msg )->isDisabled() ) {
3441 $wgOut->wrapWikiMsg(
"<div class='editpage-head-copywarn'>\n$1\n</div>",
3442 'editpage-head-copy-warn' );
3455 $msg =
'editpage-tos-summary';
3456 Hooks::run(
'EditPageTosSummary', [ $this->mTitle, &$msg ] );
3457 if ( !$this->context->msg( $msg )->isDisabled() ) {
3459 $wgOut->addHTML(
'<div class="mw-tos-summary">' );
3460 $wgOut->addWikiMsg( $msg );
3461 $wgOut->addHTML(
'</div>' );
3467 $wgOut->addHTML(
'<div class="mw-editTools">' .
3468 $this->context->msg(
'edittools' )->inContentLanguage()->parse() .
3492 if ( $wgRightsText ) {
3493 $copywarnMsg = [
'copyrightwarning',
3494 '[[' .
wfMessage(
'copyrightpage' )->inContentLanguage()->text() .
']]',
3497 $copywarnMsg = [
'copyrightwarning2',
3498 '[[' .
wfMessage(
'copyrightpage' )->inContentLanguage()->text() .
']]' ];
3503 $msg = call_user_func_array(
'wfMessage', $copywarnMsg )->title(
$title );
3505 $msg->inLanguage( $langcode );
3507 return "<div id=\"editpage-copywarn\">\n" .
3508 $msg->$format() .
"\n</div>";
3523 $limitReport =
Html::rawElement(
'div', [
'class' =>
'mw-limitReportExplanation' ],
3524 wfMessage(
'limitreport-title' )->parseAsBlock()
3528 $limitReport .=
Html::openElement(
'div', [
'class' =>
'preview-limit-report-wrapper' ] );
3531 'class' =>
'preview-limit-report wikitable'
3537 [ $key, &
$value, &$limitReport,
true,
true ]
3540 $valueMsg =
wfMessage( [
"$key-value-html",
"$key-value" ] );
3541 if ( !$valueMsg->exists() ) {
3542 $valueMsg =
new RawMessage(
'$1' );
3544 if ( !$keyMsg->isDisabled() && !$valueMsg->isDisabled() ) {
3557 return $limitReport;
3562 $wgOut->addHTML(
"<div class='editOptions'>\n" );
3564 if ( $this->section !=
'new' ) {
3569 if ( $this->oouiEnabled ) {
3572 [
'minor' => $this->minoredit,
'watch' => $this->watchthis ]
3574 $checkboxesHTML =
new OOUI\HorizontalLayout( [
'items' => $checkboxes ] );
3578 [
'minor' => $this->minoredit,
'watch' => $this->watchthis ]
3580 $checkboxesHTML = implode( $checkboxes,
"\n" );
3583 $wgOut->addHTML(
"<div class='editCheckboxes'>" . $checkboxesHTML .
"</div>\n" );
3587 $wgOut->addHTML( $this->editFormTextAfterWarn );
3589 $wgOut->addHTML(
"<div class='editButtons'>\n" );
3593 if ( $cancel !==
'' ) {
3595 [
'class' =>
'mw-editButtons-pipe-separator' ],
3596 $this->context->msg(
'pipe-separator' )->text() );
3599 $message = $this->context->msg(
'edithelppage' )->inContentLanguage()->text();
3603 $this->context->msg(
'edithelp' )->text(),
3604 [
'target' =>
'helpwindow',
'href' => $edithelpurl ],
3607 $this->context->msg(
'word-separator' )->escaped() .
3608 $this->context->msg(
'newwindow' )->parse();
3610 $wgOut->addHTML(
" <span class='cancelLink'>{$cancel}</span>\n" );
3611 $wgOut->addHTML(
" <span class='editHelp'>{$edithelp}</span>\n" );
3612 $wgOut->addHTML(
"</div><!-- editButtons -->\n" );
3616 $wgOut->addHTML(
"</div><!-- editOptions -->\n" );
3628 if (
Hooks::run(
'EditPageBeforeConflictDiff', [ &$editPage, &
$wgOut ] ) ) {
3631 $wgOut->wrapWikiMsg(
'<h2>$1</h2>',
"yourdiff" );
3637 $de =
$handler->createDifferenceEngine( $this->mArticle->getContext() );
3638 $de->setContent( $content2, $content1 );
3640 $this->context->msg(
'yourtext' )->parse(),
3641 $this->context->msg(
'storedversion' )->text()
3644 $wgOut->wrapWikiMsg(
'<h2>$1</h2>',
"yourtext" );
3650 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
3651 $stats->increment(
'edit.failures.conflict' );
3654 $this->mTitle->getNamespace() >=
NS_MAIN &&
3657 $stats->increment(
'edit.failures.conflict.byNamespaceId.' . $this->mTitle->getNamespace() );
3666 if ( !$this->isConflict && $this->oldid > 0 ) {
3669 $cancelParams[
'redirect'] =
'no';
3671 if ( $this->oouiEnabled ) {
3672 return new OOUI\ButtonWidget( [
3673 'id' =>
'mw-editform-cancel',
3675 'label' =>
new OOUI\HtmlSnippet( $this->context->msg(
'cancel' )->parse() ),
3677 'infusable' =>
true,
3678 'flags' =>
'destructive',
3681 return MediaWikiServices::getInstance()->getLinkRenderer()->makeKnownLink(
3683 new HtmlArmor( $this->context->msg(
'cancel' )->parse() ),
3700 return $title->getLocalURL( [
'action' => $this->
action ] );
3711 if ( $this->deletedSinceEdit !==
null ) {
3715 $this->deletedSinceEdit =
false;
3717 if ( !$this->mTitle->exists() && $this->mTitle->isDeletedQuick() ) {
3719 if ( $this->lastDelete ) {
3720 $deleteTime =
wfTimestamp( TS_MW, $this->lastDelete->log_timestamp );
3721 if ( $deleteTime > $this->starttime ) {
3722 $this->deletedSinceEdit =
true;
3735 $data =
$dbr->selectRow(
3736 [
'logging',
'user' ],
3749 'log_namespace' => $this->mTitle->getNamespace(),
3750 'log_title' => $this->mTitle->getDBkey(),
3751 'log_type' =>
'delete',
3752 'log_action' =>
'delete',
3756 [
'LIMIT' => 1,
'ORDER BY' =>
'log_timestamp DESC' ]
3759 if ( is_object( $data ) ) {
3761 $data->user_name = $this->context->msg(
'rev-deleted-user' )->escaped();
3765 $data->log_comment = $this->context->msg(
'rev-deleted-comment' )->escaped();
3779 global $wgAllowUserCss, $wgAllowUserJs;
3781 if ( $wgRawHtml && !$this->mTokenOk ) {
3785 if ( $this->textbox1 !==
'' ) {
3789 $parsedNote =
$wgOut->parse(
"<div class='previewnote'>" .
3790 $this->context->msg(
'session_fail_preview_html' )->text() .
"</div>",
3804 'AlternateEditPreview',
3805 [ $this, &
$content, &$previewHTML, &$this->mParserOutput ] )
3807 return $previewHTML;
3810 # provide a anchor link to the editform
3811 $continueEditing =
'<span class="mw-continue-editing">' .
3812 '[[#' . self::EDITFORM_ID .
'|' .
$wgLang->getArrow() .
' ' .
3813 $this->context->
msg(
'continue-editing' )->text() .
']]</span>';
3814 if ( $this->mTriedSave && !$this->mTokenOk ) {
3815 if ( $this->mTokenOkExceptSuffix ) {
3816 $note = $this->context->msg(
'token_suffix_mismatch' )->plain();
3819 $note = $this->context->msg(
'session_fail_preview' )->plain();
3822 } elseif ( $this->incompleteForm ) {
3823 $note = $this->context->msg(
'edit_form_incomplete' )->plain();
3824 if ( $this->mTriedSave ) {
3828 $note = $this->context->msg(
'previewnote' )->plain() .
' ' . $continueEditing;
3831 # don't parse non-wikitext pages, show message about preview
3832 if ( $this->mTitle->isCssJsSubpage() || $this->mTitle->isCssOrJsPage() ) {
3833 if ( $this->mTitle->isCssJsSubpage() ) {
3835 } elseif ( $this->mTitle->isCssOrJsPage() ) {
3843 if ( $level ===
'user' && !$wgAllowUserCss ) {
3848 if ( $level ===
'user' && !$wgAllowUserJs ) {
3855 # Used messages to make sure grep find them:
3856 # Messages: usercsspreview, userjspreview, sitecsspreview, sitejspreview
3857 if ( $level && $format ) {
3858 $note =
"<div id='mw-{$level}{$format}preview'>" .
3859 $this->context->msg(
"{$level}{$format}preview" )->text() .
3860 ' ' . $continueEditing .
"</div>";
3864 # If we're adding a comment, we need to show the
3865 # summary as the headline
3866 if ( $this->section ===
"new" && $this->summary !==
"" ) {
3871 Hooks::run(
'EditPageGetPreviewContent', $hook_args );
3875 $previewHTML = $parserResult[
'html'];
3880 $note .=
"\n\n" . implode(
"\n\n",
$parserOutput->getWarnings() );
3884 $m = $this->context->msg(
3885 'content-failed-to-parse',
3886 $this->contentModel,
3887 $this->contentFormat,
3890 $note .=
"\n\n" . $m->parse();
3894 if ( $this->isConflict ) {
3895 $conflict =
'<h2 id="mw-previewconflict">'
3896 . $this->context->msg(
'previewconflict' )->escaped() .
"</h2>\n";
3898 $conflict =
'<hr />';
3901 $previewhead =
"<div class='previewnote'>\n" .
3902 '<h2 id="mw-previewheader">' . $this->context->msg(
'preview' )->escaped() .
"</h2>" .
3903 $wgOut->parse( $note,
true,
true ) . $conflict .
"</div>\n";
3905 $pageViewLang = $this->mTitle->getPageViewLanguage();
3906 $attribs = [
'lang' => $pageViewLang->getHtmlCode(),
'dir' => $pageViewLang->getDir(),
3907 'class' =>
'mw-content-' . $pageViewLang->getDir() ];
3914 $stats = MediaWikiServices::getInstance()->getStatsdDataFactory();
3915 $stats->increment(
'edit.failures.' . $failureType );
3923 $parserOptions = $this->
page->makeParserOptions( $this->mArticle->getContext() );
3924 $parserOptions->setIsPreview(
true );
3925 $parserOptions->setIsSectionPreview( !is_null( $this->section ) && $this->section !==
'' );
3926 $parserOptions->enableLimitReport();
3927 return $parserOptions;
3942 $pstContent =
$content->preSaveTransform( $this->mTitle,
$wgUser, $parserOptions );
3943 $scopedCallback = $parserOptions->setupFakeRevision(
3944 $this->mTitle, $pstContent,
$wgUser );
3945 $parserOutput = $pstContent->getParserOutput( $this->mTitle,
null, $parserOptions );
3946 ScopedCallback::consume( $scopedCallback );
3957 if ( $this->preview || $this->section !=
'' ) {
3959 if ( !isset( $this->mParserOutput ) ) {
3962 foreach ( $this->mParserOutput->getTemplates()
as $ns =>
$template ) {
3969 return $this->mTitle->getTemplateLinksFrom();
3985 $showSignature =
true;
4001 'id' =>
'mw-editbutton-bold',
4003 'close' =>
'\'\
'\'',
4004 'sample' =>
wfMessage(
'bold_sample' )->text(),
4005 'tip' =>
wfMessage(
'bold_tip' )->text(),
4008 'id' =>
'mw-editbutton-italic',
4011 'sample' =>
wfMessage(
'italic_sample' )->text(),
4012 'tip' =>
wfMessage(
'italic_tip' )->text(),
4015 'id' =>
'mw-editbutton-link',
4018 'sample' =>
wfMessage(
'link_sample' )->text(),
4019 'tip' =>
wfMessage(
'link_tip' )->text(),
4022 'id' =>
'mw-editbutton-extlink',
4025 'sample' =>
wfMessage(
'extlink_sample' )->text(),
4026 'tip' =>
wfMessage(
'extlink_tip' )->text(),
4029 'id' =>
'mw-editbutton-headline',
4032 'sample' =>
wfMessage(
'headline_sample' )->text(),
4033 'tip' =>
wfMessage(
'headline_tip' )->text(),
4035 $imagesAvailable ? [
4036 'id' =>
'mw-editbutton-image',
4039 'sample' =>
wfMessage(
'image_sample' )->text(),
4040 'tip' =>
wfMessage(
'image_tip' )->text(),
4042 $imagesAvailable ? [
4043 'id' =>
'mw-editbutton-media',
4046 'sample' =>
wfMessage(
'media_sample' )->text(),
4047 'tip' =>
wfMessage(
'media_tip' )->text(),
4050 'id' =>
'mw-editbutton-nowiki',
4051 'open' =>
"<nowiki>",
4052 'close' =>
"</nowiki>",
4053 'sample' =>
wfMessage(
'nowiki_sample' )->text(),
4054 'tip' =>
wfMessage(
'nowiki_tip' )->text(),
4057 'id' =>
'mw-editbutton-signature',
4058 'open' =>
wfMessage(
'sig-text',
'~~~~' )->inContentLanguage()->text(),
4061 'tip' =>
wfMessage(
'sig_tip' )->text(),
4064 'id' =>
'mw-editbutton-hr',
4065 'open' =>
"\n----\n",
4072 $script =
'mw.loader.using("mediawiki.toolbar", function () {';
4073 foreach ( $toolarray
as $tool ) {
4093 'mw.toolbar.addButton',
4095 ResourceLoader::inDebugMode()
4101 $toolbar =
'<div id="toolbar"></div>';
4103 if (
Hooks::run(
'EditPageBeforeEditToolbar', [ &$toolbar ] ) ) {
4106 $wgOut->addScript( ResourceLoader::makeInlineScript( $script ) );
4135 if ( !$this->isNew &&
$wgUser->isAllowed(
'minoredit' ) ) {
4136 $checkboxes[
'wpMinoredit'] = [
4137 'id' =>
'wpMinoredit',
4138 'label-message' =>
'minoredit',
4140 'tooltip' =>
'minoredit',
4141 'label-id' =>
'mw-editpage-minoredit',
4142 'legacy-name' =>
'minor',
4143 'default' => $checked[
'minor'],
4147 if (
$wgUser->isLoggedIn() ) {
4148 $checkboxes[
'wpWatchthis'] = [
4149 'id' =>
'wpWatchthis',
4150 'label-message' =>
'watchthis',
4152 'tooltip' =>
'watch',
4153 'label-id' =>
'mw-editpage-watch',
4154 'legacy-name' =>
'watch',
4155 'default' => $checked[
'watch'],
4160 Hooks::run(
'EditPageGetCheckboxesDefinition', [ $editPage, &$checkboxes ] );
4174 global $wgUseMediaWikiUIEverywhere;
4180 if ( !$this->isNew ) {
4181 $checkboxes[
'minor'] =
'';
4183 $checkboxes[
'watch'] =
'';
4187 $label = $this->context->msg(
$options[
'label-message'] )->parse();
4195 if ( isset(
$options[
'tooltip'] ) ) {
4196 $attribs[
'accesskey'] = $this->context->msg(
"accesskey-{$options['tooltip']}" )->text();
4199 if ( isset(
$options[
'title-message'] ) ) {
4200 $labelAttribs[
'title'] = $this->context->msg(
$options[
'title-message'] )->text();
4202 if ( isset(
$options[
'label-id'] ) ) {
4203 $labelAttribs[
'id'] =
$options[
'label-id'];
4208 Xml::tags(
'label', $labelAttribs, $label );
4210 if ( $wgUseMediaWikiUIEverywhere ) {
4211 $checkboxHtml =
Html::rawElement(
'div', [
'class' =>
'mw-ui-checkbox' ], $checkboxHtml );
4214 $checkboxes[ $legacyName ] = $checkboxHtml;
4219 Hooks::run(
'EditPageBeforeEditChecks', [ &$editPage, &$checkboxes, &
$tabindex ],
'1.29' );
4244 if ( isset(
$options[
'tooltip'] ) ) {
4245 $accesskey = $this->context->msg(
"accesskey-{$options['tooltip']}" )->text();
4248 if ( isset(
$options[
'title-message'] ) ) {
4249 $title = $this->context->msg(
$options[
'title-message'] )->text();
4251 if ( isset(
$options[
'label-id'] ) ) {
4252 $labelAttribs[
'id'] =
$options[
'label-id'];
4255 $checkboxes[ $legacyName ] =
new OOUI\FieldLayout(
4256 new OOUI\CheckboxInputWidget( [
4258 'accessKey' => $accesskey,
4262 'infusable' =>
true,
4265 'align' =>
'inline',
4266 'label' =>
new OOUI\HtmlSnippet( $this->context->msg(
$options[
'label-message'] )->parse() ),
4276 $legacyCheckboxes = $this->
getCheckboxes( $origTabindex, $checked );
4278 if (
$html && !isset( $checkboxes[
$name] ) ) {
4279 $checkboxes[
$name] =
new OOUI\Widget( [
'content' =>
new OOUI\HtmlSnippet(
$html ) ] );
4298 $this->mArticle->getContext()->getConfig()->get(
'EditSubmitButtonLabelPublish' );
4301 if ( $labelAsPublish ) {
4302 $buttonLabelKey = !$this->mTitle->exists() ?
'publishpage' :
'publishchanges';
4304 $buttonLabelKey = !$this->mTitle->exists() ?
'savearticle' :
'savechanges';
4312 if ( $this->oouiEnabled ) {
4313 $saveConfig = OOUI\Element::configFromHtmlAttributes(
$attribs );
4314 $buttons[
'save'] =
new OOUI\ButtonInputWidget( [
4316 'useInputTag' =>
true,
4317 'flags' => [
'constructive',
'primary' ],
4318 'label' => $this->context->msg( $buttonLabelKey )->text(),
4319 'infusable' =>
true,
4324 $this->context->msg( $buttonLabelKey )->text(),
4326 [
'mw-ui-progressive' ]
4331 'id' =>
'wpPreview',
4332 'name' =>
'wpPreview',
4335 if ( $this->oouiEnabled ) {
4336 $previewConfig = OOUI\Element::configFromHtmlAttributes(
$attribs );
4337 $buttons[
'preview'] =
new OOUI\ButtonInputWidget( [
4339 'useInputTag' =>
true,
4340 'label' => $this->context->msg(
'showpreview' )->text(),
4341 'infusable' =>
true,
4343 ] + $previewConfig );
4346 $this->context->msg(
'showpreview' )->text(),
4355 if ( $this->oouiEnabled ) {
4356 $diffConfig = OOUI\Element::configFromHtmlAttributes(
$attribs );
4357 $buttons[
'diff'] =
new OOUI\ButtonInputWidget( [
4359 'useInputTag' =>
true,
4360 'label' => $this->context->msg(
'showdiff' )->text(),
4361 'infusable' =>
true,
4366 $this->context->msg(
'showdiff' )->text(),
4385 $wgOut->prepareErrorPage( $this->context->msg(
'nosuchsectiontitle' ) );
4387 $res = $this->context->msg(
'nosuchsectiontext', $this->section )->parseAsBlock();
4394 $wgOut->returnToMain(
false, $this->mTitle );
4406 if ( is_array( $match ) ) {
4407 $match =
$wgLang->listToText( $match );
4409 $wgOut->prepareErrorPage( $this->context->msg(
'spamprotectiontitle' ) );
4411 $wgOut->addHTML(
'<div id="spamprotected">' );
4412 $wgOut->addWikiMsg(
'spamprotectiontext' );
4416 $wgOut->addHTML(
'</div>' );
4418 $wgOut->wrapWikiMsg(
'<h2>$1</h2>',
"yourdiff" );
4421 $wgOut->wrapWikiMsg(
'<h2>$1</h2>',
"yourtext" );
4424 $wgOut->addReturnTo( $this->
getContextTitle(), [
'action' =>
'edit' ] );
4436 $currentbrowser =
$wgRequest->getHeader(
'User-Agent' );
4437 if ( $currentbrowser ===
false ) {
4442 foreach ( $wgBrowserBlackList
as $browser ) {
4443 if ( preg_match( $browser, $currentbrowser ) ) {
4459 $text = rtrim(
$request->getText( $field ) );
4460 return $request->getBool(
'safemode' )
4492 $invalue = strtr( $invalue, [
"&#x" =>
"�" ] );
4497 $valueLength = strlen( $invalue );
4498 for ( $i = 0; $i < $valueLength; $i++ ) {
4499 $bytevalue = ord( $invalue[$i] );
4500 if ( $bytevalue <= 0x7F ) {
4503 } elseif ( $bytevalue <= 0xBF ) {
4504 $working = $working << 6;
4505 $working += ( $bytevalue & 0x3F );
4507 if ( $bytesleft <= 0 ) {
4508 $result .=
"&#x" . strtoupper( dechex( $working ) ) .
";";
4510 } elseif ( $bytevalue <= 0xDF ) {
4511 $working = $bytevalue & 0x1F;
4513 } elseif ( $bytevalue <= 0xEF ) {
4514 $working = $bytevalue & 0x0F;
4517 $working = $bytevalue & 0x07;
4534 $valueLength = strlen( $invalue );
4535 for ( $i = 0; $i < $valueLength; $i++ ) {
4536 if ( ( substr( $invalue, $i, 3 ) ==
"&#x" ) && ( $invalue[$i + 3] !=
'0' ) ) {
4540 $hexstring .= $invalue[$i];
4542 }
while ( ctype_xdigit( $invalue[$i] ) && ( $i < strlen( $invalue ) ) );
4547 if ( ( substr( $invalue, $i, 1 ) ==
";" ) && ( strlen( $hexstring ) <= 6 ) ) {
4548 $codepoint = hexdec( $hexstring );
4551 $result .=
"&#x" . $hexstring . substr( $invalue, $i, 1 );
4554 $result .= substr( $invalue, $i, 1 );
4558 return strtr(
$result, [
"�" =>
"&#x" ] );
4567 $editNotices = $this->mTitle->getEditNotices( $this->oldid );
4568 if (
count( $editNotices ) ) {
4569 $wgOut->addHTML( implode(
"\n", $editNotices ) );
4571 $msg = $this->context->msg(
'editnotice-notext' );
4572 if ( !$msg->isDisabled() ) {
4574 '<div class="mw-editnotice-notext">'
4575 . $msg->parseAsBlock()
4588 if ( $this->mTitle->isTalkPage() ) {
4589 $wgOut->addWikiMsg(
'talkpagetext' );
4599 if ( $this->contentLength ===
false ) {
4600 $this->contentLength = strlen( $this->textbox1 );
4604 $wgOut->wrapWikiMsg(
"<div class='error' id='mw-edit-longpageerror'>\n$1\n</div>",
4607 $wgLang->formatNum( round( $this->contentLength / 1024, 3 ) ),
4612 if ( !$this->context->msg(
'longpage-hint' )->isDisabled() ) {
4613 $wgOut->wrapWikiMsg(
"<div id='mw-edit-longpage-hint'>\n$1\n</div>",
4616 $wgLang->formatSize( strlen( $this->textbox1 ) ),
4617 strlen( $this->textbox1 )
4630 if ( $this->mTitle->isProtected(
'edit' ) &&
4633 # Is the title semi-protected?
4634 if ( $this->mTitle->isSemiProtected() ) {
4635 $noticeMsg =
'semiprotectedpagewarning';
4637 # Then it must be protected based on static groups (regular)
4638 $noticeMsg =
'protectedpagewarning';
4641 [
'lim' => 1,
'msgKey' => [ $noticeMsg ] ] );
4643 if ( $this->mTitle->isCascadeProtected() ) {
4644 # Is this page under cascading protection from some source pages?
4646 list( $cascadeSources, ) = $this->mTitle->getCascadeProtectionSources();
4647 $notice =
"<div class='mw-cascadeprotectedwarning'>\n$1\n";
4648 $cascadeSourcesCount =
count( $cascadeSources );
4649 if ( $cascadeSourcesCount > 0 ) {
4650 # Explain, and list the titles responsible
4651 foreach ( $cascadeSources
as $page ) {
4652 $notice .=
'* [[:' .
$page->getPrefixedText() .
"]]\n";
4655 $notice .=
'</div>';
4656 $wgOut->wrapWikiMsg( $notice, [
'cascadeprotectedwarning', $cascadeSourcesCount ] );
4658 if ( !$this->mTitle->exists() && $this->mTitle->getRestrictions(
'create' ) ) {
4661 'showIfEmpty' =>
false,
4662 'msgKey' => [
'titleprotectedwarning' ],
4663 'wrap' =>
"<div class=\"mw-titleprotectedwarning\">\n$1</div>" ] );
4672 $out->wrapWikiMsg(
"<div class='mw-explainconflict'>\n$1\n</div>",
'explainconflict' );
4698 $class =
'mw-editfont-' .
$user->getOption(
'editfont' );
4700 if ( isset(
$attribs[
'class'] ) ) {
4701 if ( is_string(
$attribs[
'class'] ) ) {
4703 } elseif ( is_array(
$attribs[
'class'] ) ) {
4710 $pageLang = $this->mTitle->getPageLanguage();
4711 $attribs[
'lang'] = $pageLang->getHtmlCode();
4712 $attribs[
'dir'] = $pageLang->getDir();
4723 if ( strval( $wikitext ) !==
'' ) {