42 const AS_SUCCESS_UPDATE = 200;
47 const AS_SUCCESS_NEW_ARTICLE = 201;
52 const AS_HOOK_ERROR = 210;
57 const AS_HOOK_ERROR_EXPECTED = 212;
62 const AS_BLOCKED_PAGE_FOR_USER = 215;
67 const AS_CONTENT_TOO_BIG = 216;
72 const AS_USER_CANNOT_EDIT = 217;
77 const AS_READ_ONLY_PAGE_ANON = 218;
82 const AS_READ_ONLY_PAGE_LOGGED = 219;
87 const AS_READ_ONLY_PAGE = 220;
92 const AS_RATE_LIMITED = 221;
98 const AS_ARTICLE_WAS_DELETED = 222;
104 const AS_NO_CREATE_PERMISSION = 223;
109 const AS_BLANK_ARTICLE = 224;
114 const AS_CONFLICT_DETECTED = 225;
120 const AS_SUMMARY_NEEDED = 226;
125 const AS_TEXTBOX_EMPTY = 228;
130 const AS_MAX_ARTICLE_SIZE_EXCEEDED = 229;
145 const AS_SPAM_ERROR = 232;
150 const AS_IMAGE_REDIRECT_ANON = 233;
155 const AS_IMAGE_REDIRECT_LOGGED = 234;
160 const AS_PARSE_ERROR = 240;
165 const EDITFORM_ID =
'editform';
171 const POST_EDIT_COOKIE_KEY_PREFIX =
'PostEditRevision';
185 const POST_EDIT_COOKIE_DURATION = 1200;
196 private $mContextTitle =
null;
197 var $action =
'submit';
198 var $isConflict =
false;
199 var $isCssJsSubpage =
false;
200 var $isCssSubpage =
false;
201 var $isJsSubpage =
false;
202 var $isWrongCaseCssJsPage =
false;
204 var $deletedSinceEdit;
208 var $mTokenOk =
false;
209 var $mTokenOkExceptSuffix =
false;
210 var $mTriedSave =
false;
211 var $incompleteForm =
false;
213 var $kblength =
false;
214 var $missingComment =
false;
215 var $missingSummary =
false;
216 var $allowBlankSummary =
false;
219 #var $mPreviewTemplates;
230 var $hasPresetSummary =
false;
232 var $mBaseRevision =
false;
233 var $mShowSummaryField =
true;
236 var $save =
false, $preview =
false, $diff =
false;
237 var $minoredit =
false, $watchthis =
false, $recreate =
false;
238 var $textbox1 =
'', $textbox2 =
'',
$summary =
'', $nosummary =
false;
240 var $oldid = 0, $editintro =
'', $scrolltop =
null, $bot =
true;
241 var $contentModel =
null, $contentFormat =
null;
243 # Placeholders for text injection by hooks (must be HTML)
244 # extensions should take care to _append_ to the present value
245 public $editFormPageTop =
'';
246 public $editFormTextTop =
'';
247 public $editFormTextBeforeContent =
'';
248 public $editFormTextAfterWarn =
'';
249 public $editFormTextAfterTools =
'';
250 public $editFormTextBottom =
'';
251 public $editFormTextAfterContent =
'';
252 public $previewTextAfterContent =
'';
253 public $mPreloadContent =
null;
256 public $didSave =
false;
257 public $undidRev = 0;
259 public $suppressIntro =
false;
266 public $allowNonTextContent =
false;
273 $this->mTitle =
$article->getTitle();
275 $this->contentModel = $this->mTitle->getContentModel();
278 $this->contentFormat = $handler->getDefaultFormat();
284 public function getArticle() {
285 return $this->mArticle;
292 public function getTitle() {
293 return $this->mTitle;
301 public function setContextTitle(
$title ) {
302 $this->mContextTitle =
$title;
312 public function getContextTitle() {
313 if ( is_null( $this->mContextTitle ) ) {
317 return $this->mContextTitle;
328 public function isSupportedContentModel( $modelId ) {
329 return $this->allowNonTextContent ||
356 wfDebug( __METHOD__ .
": enter\n" );
359 if ( $wgRequest->getBool(
'redlink' ) && $this->mTitle->exists() ) {
360 $wgOut->redirect( $this->mTitle->getFullURL() );
365 $this->importFormData( $wgRequest );
366 $this->firsttime =
false;
369 $this->livePreview();
377 $this->preview =
true;
381 $this->formtype =
'save';
382 } elseif ( $this->preview ) {
383 $this->formtype =
'preview';
384 } elseif ( $this->diff ) {
385 $this->formtype =
'diff';
386 }
else { # First time through
387 $this->firsttime =
true;
388 if ( $this->previewOnOpen() ) {
389 $this->formtype =
'preview';
391 $this->formtype =
'initial';
395 $permErrors = $this->getEditPermissionErrors();
397 wfDebug( __METHOD__ .
": User can't edit\n" );
401 $this->displayPermissionsError( $permErrors );
409 $this->isConflict =
false;
411 $this->isCssJsSubpage = $this->mTitle->isCssJsSubpage();
412 $this->isCssSubpage = $this->mTitle->isCssSubpage();
413 $this->isJsSubpage = $this->mTitle->isJsSubpage();
414 $this->isWrongCaseCssJsPage = $this->isWrongCaseCssJsPage();
416 # Show applicable editing introductions
417 if ( $this->formtype ==
'initial' || $this->firsttime ) {
421 # Attempt submission here. This will check for edit conflicts,
422 # and redundantly check for locked database, blocked IPs, etc.
423 # that edit() already checked just in case someone tries to sneak
424 # in the back door with a hand-edited submission URL.
426 if (
'save' == $this->formtype ) {
427 if ( !$this->attemptSave() ) {
434 # First time through: get contents, set time for conflict
436 if (
'initial' == $this->formtype || $this->firsttime ) {
437 if ( $this->initialiseForm() ===
false ) {
438 $this->noSuchSectionPage();
444 if ( !$this->mTitle->getArticleID() ) {
445 wfRunHooks(
'EditFormPreloadText',
array( &$this->textbox1, &$this->mTitle ) );
452 $this->showEditForm();
460 protected function getEditPermissionErrors() {
462 $permErrors = $this->mTitle->getUserPermissionsErrors(
'edit',
$wgUser );
463 # Can this title be created?
464 if ( !$this->mTitle->exists() ) {
465 $permErrors = array_merge( $permErrors,
466 wfArrayDiff2( $this->mTitle->getUserPermissionsErrors(
'create',
$wgUser ), $permErrors ) );
468 # Ignore some permissions errors when a user is just previewing/viewing diffs
471 if ( ( $this->preview || $this->diff )
472 && (
$error[0] ==
'blockedtext' ||
$error[0] ==
'autoblockedtext' )
494 protected function displayPermissionsError(
array $permErrors ) {
497 if ( $wgRequest->getBool(
'redlink' ) ) {
501 $wgOut->redirect( $this->mTitle->getFullURL() );
505 $content = $this->getContentObject();
507 # Use the normal message if there's nothing to display
508 if ( $this->firsttime && ( !$content || $content->
isEmpty() ) ) {
509 $action = $this->mTitle->exists() ?
'edit' :
510 ( $this->mTitle->isTalkPage() ?
'createtalk' :
'createpage' );
514 $wgOut->setRobotPolicy(
'noindex,nofollow' );
515 $wgOut->setPageTitle(
wfMessage(
'viewsource-title', $this->getContextTitle()->getPrefixedText() ) );
516 $wgOut->addBacklinkSubtitle( $this->getContextTitle() );
517 $wgOut->addWikiText(
$wgOut->formatPermissionsErrorMessage( $permErrors,
'edit' ) );
518 $wgOut->addHTML(
"<hr />\n" );
520 # If the user made changes, preserve them when showing the markup
521 # (This happens when a user is blocked during edit, for instance)
522 if ( !$this->firsttime ) {
523 $text = $this->textbox1;
524 $wgOut->addWikiMsg(
'viewyourtext' );
526 $text = $this->toEditText( $content );
527 $wgOut->addWikiMsg(
'viewsourcetext' );
530 $this->showTextbox( $text,
'wpTextbox1',
array(
'readonly' ) );
535 $wgOut->addModules(
'mediawiki.action.edit.collapsibleFooter' );
537 if ( $this->mTitle->exists() ) {
538 $wgOut->returnToMain(
null, $this->mTitle );
548 function readOnlyPage(
$source =
null, $protected =
false, $reasons =
array(), $action =
null ) {
552 if ( $wgRequest->getBool(
'redlink' ) ) {
556 $wgOut->redirect( $this->mTitle->getFullURL() );
558 $wgOut->readOnlyPage(
$source, $protected, $reasons, $action );
567 protected function previewOnOpen() {
569 if ( $wgRequest->getVal(
'preview' ) ==
'yes' ) {
572 } elseif ( $wgRequest->getVal(
'preview' ) ==
'no' ) {
575 } elseif ( $this->
section ==
'new' ) {
578 } elseif ( ( $wgRequest->getVal(
'preload' ) !==
null || $this->mTitle->exists() ) &&
$wgUser->getOption(
'previewonfirst' ) ) {
581 } elseif ( !$this->mTitle->exists()
582 && isset( $wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()] )
583 && $wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()]
598 protected function isWrongCaseCssJsPage() {
599 if ( $this->mTitle->isCssJsSubpage() ) {
600 $name = $this->mTitle->getSkinFromCssJsSubpage();
601 $skins = array_merge(
605 return !in_array(
$name, $skins )
606 && in_array( strtolower(
$name ), $skins );
619 protected function isSectionEditSupported() {
621 return $contentHandler->supportsSections();
629 function importFormData( &$request ) {
634 # Section edit can come from either the form or a link
635 $this->
section = $request->getVal(
'wpSection', $request->getVal(
'section' ) );
637 if ( $this->
section !==
null && $this->
section !==
'' && !$this->isSectionEditSupported() ) {
639 throw new ErrorPageError(
'sectioneditnotsupported-title',
'sectioneditnotsupported-text' );
642 $this->isNew = !$this->mTitle->exists() || $this->
section ==
'new';
644 if ( $request->wasPosted() ) {
645 # These fields need to be checked for encoding.
646 # Also remove trailing whitespace, but don't remove _initial_
647 # whitespace from the text boxes. This may be significant formatting.
648 $this->textbox1 = $this->safeUnicodeInput( $request,
'wpTextbox1' );
649 if ( !$request->getCheck(
'wpTextbox2' ) ) {
653 wfProfileIn( get_class( $this ) .
"::importContentFormData" );
654 $textbox1 = $this->importContentFormData( $request );
655 if ( $textbox1 !==
null ) {
656 $this->textbox1 = $textbox1;
659 wfProfileOut( get_class( $this ) .
"::importContentFormData" );
662 # Truncate for whole multibyte characters
663 $this->summary =
$wgContLang->truncate( $request->getText(
'wpSummary' ), 255 );
665 # If the summary consists of a heading, e.g. '==Foobar==', extract the title from the
666 # header syntax, e.g. 'Foobar'. This is mainly an issue when we are using wpSummary for
668 $this->summary = preg_replace(
'/^\s*=+\s*(.*?)\s*=+\s*$/',
'$1', $this->summary );
670 # Treat sectiontitle the same way as summary.
671 # Note that wpSectionTitle is not yet a part of the actual edit form, as wpSummary is
672 # currently doing double duty as both edit summary and section title. Right now this
673 # is just to allow API edits to work around this limitation, but this should be
674 # incorporated into the actual edit form when EditPage is rewritten (Bugs 18654, 26312).
675 $this->sectiontitle =
$wgContLang->truncate( $request->getText(
'wpSectionTitle' ), 255 );
676 $this->sectiontitle = preg_replace(
'/^\s*=+\s*(.*?)\s*=+\s*$/',
'$1', $this->sectiontitle );
678 $this->edittime = $request->getVal(
'wpEdittime' );
679 $this->starttime = $request->getVal(
'wpStarttime' );
681 $undidRev = $request->getInt(
'wpUndidRevision' );
683 $this->undidRev = $undidRev;
686 $this->scrolltop = $request->getIntOrNull(
'wpScrolltop' );
688 if ( $this->textbox1 ===
'' && $request->getVal(
'wpTextbox1' ) === null ) {
692 $this->incompleteForm =
true;
696 $this->incompleteForm = is_null( $this->edittime );
698 if ( $this->incompleteForm ) {
699 # If the form is incomplete, force to preview.
700 wfDebug( __METHOD__ .
": Form data appears to be incomplete\n" );
701 wfDebug(
"POST DATA: " . var_export( $_POST,
true ) .
"\n" );
702 $this->preview =
true;
705 $this->preview = $request->getCheck(
'wpPreview' ) || $request->getCheck(
'wpLivePreview' );
706 $this->diff = $request->getCheck(
'wpDiff' );
710 $this->mTriedSave = !$this->preview;
712 if ( $this->tokenOk( $request ) ) {
713 # Some browsers will not report any submit button
714 # if the user hits enter in the comment box.
715 # The unmarked state will be assumed to be a save,
716 # if the form seems otherwise complete.
717 wfDebug( __METHOD__ .
": Passed token check.\n" );
718 } elseif ( $this->diff ) {
719 # Failed token check, but only requested "Show Changes".
720 wfDebug( __METHOD__ .
": Failed token check; Show Changes requested.\n" );
722 # Page might be a hack attempt posted from
723 # an external site. Preview instead of saving.
724 wfDebug( __METHOD__ .
": Failed token check; forcing preview\n" );
725 $this->preview =
true;
728 $this->
save = !$this->preview && !$this->diff;
729 if ( !preg_match(
'/^\d{14}$/', $this->edittime ) ) {
730 $this->edittime =
null;
733 if ( !preg_match(
'/^\d{14}$/', $this->starttime ) ) {
734 $this->starttime =
null;
737 $this->recreate = $request->getCheck(
'wpRecreate' );
739 $this->minoredit = $request->getCheck(
'wpMinoredit' );
740 $this->watchthis = $request->getCheck(
'wpWatchthis' );
742 # Don't force edit summaries when a user is editing their own user or talk page
744 && $this->mTitle->getText() ==
$wgUser->getName()
746 $this->allowBlankSummary =
true;
748 $this->allowBlankSummary = $request->getBool(
'wpIgnoreBlankSummary' ) || !
$wgUser->getOption(
'forceeditsummary' );
751 $this->autoSumm = $request->getText(
'wpAutoSummary' );
753 # Not a posted form? Start with nothing.
754 wfDebug( __METHOD__ .
": Not a posted form.\n" );
755 $this->textbox1 =
'';
757 $this->sectiontitle =
'';
758 $this->edittime =
'';
761 $this->preview =
false;
764 $this->minoredit =
false;
765 $this->watchthis = $request->getBool(
'watchthis',
false );
766 $this->recreate =
false;
770 if ( $this->
section ==
'new' && $request->getVal(
'preloadtitle' ) ) {
771 $this->sectiontitle = $request->getVal(
'preloadtitle' );
773 $this->summary = $request->getVal(
'preloadtitle' );
774 } elseif ( $this->
section !=
'new' && $request->getVal(
'summary' ) ) {
775 $this->summary = $request->getText(
'summary' );
776 if ( $this->summary !==
'' ) {
777 $this->hasPresetSummary =
true;
781 if ( $request->getVal(
'minor' ) ) {
782 $this->minoredit =
true;
786 $this->oldid = $request->getInt(
'oldid' );
788 $this->bot = $request->getBool(
'bot',
true );
789 $this->nosummary = $request->getBool(
'nosummary' );
791 $this->contentModel = $request->getText(
'model', $this->contentModel ); #may be overridden by revision
792 $this->contentFormat = $request->getText(
'format', $this->contentFormat ); #may be overridden by revision
796 'editpage-notsupportedcontentformat-title',
797 'editpage-notsupportedcontentformat-text',
801 #TODO: check if the desired model is allowed in this namespace, and if a transition from the page's current model to the new model is allowed
803 $this->live = $request->getCheck(
'live' );
804 $this->editintro = $request->getText(
'editintro',
806 $this->
section ===
'new' ?
'MediaWiki:addsection-editintro' :
'' );
822 protected function importContentFormData( &$request ) {
831 function initialiseForm() {
833 $this->edittime = $this->mArticle->getTimestamp();
835 $content = $this->getContentObject(
false ); #TODO: track
content object?!
836 if ( $content ===
false ) {
839 $this->textbox1 = $this->toEditText( $content );
842 # Sort out the "watch" checkbox
843 if (
$wgUser->getOption(
'watchdefault' ) ) {
845 $this->watchthis =
true;
846 } elseif (
$wgUser->getOption(
'watchcreations' ) && !$this->mTitle->exists() ) {
848 $this->watchthis =
true;
849 } elseif (
$wgUser->isWatched( $this->mTitle ) ) {
851 $this->watchthis =
true;
853 if (
$wgUser->getOption(
'minordefault' ) && !$this->isNew ) {
854 $this->minoredit =
true;
856 if ( $this->textbox1 ===
false ) {
870 function getContent( $def_text =
false ) {
873 if ( $def_text !==
null && $def_text !==
false && $def_text !==
'' ) {
874 $def_content = $this->toEditContent( $def_text );
876 $def_content =
false;
879 $content = $this->getContentObject( $def_content );
882 return $this->toEditText( $content );
892 protected function getContentObject( $def_content =
null ) {
901 if ( !$this->mTitle->exists() || $this->
section ==
'new' ) {
903 # If this is a system message, get the default text.
904 $msg = $this->mTitle->getDefaultMessageText();
906 $content = $this->toEditContent( $msg );
908 if ( $content ===
false ) {
909 # If requested, preload some text.
910 $preload = $wgRequest->getVal(
'preload',
912 $this->
section ===
'new' ?
'MediaWiki:addsection-preload' :
'' );
913 $params = $wgRequest->getArray(
'preloadparams',
array() );
915 $content = $this->getPreloadedContent( $preload,
$params );
921 $orig = $this->getOriginalContent(
$wgUser );
925 $content = $def_content;
928 $undoafter = $wgRequest->getInt(
'undoafter' );
929 $undo = $wgRequest->getInt(
'undo' );
931 if ( $undo > 0 && $undoafter > 0 ) {
936 # Sanity check, make sure it's the right page,
937 # the revisions exist and they were not deleted.
938 # Otherwise, $content will be left as-is.
939 if ( !is_null( $undorev ) && !is_null( $oldrev ) &&
943 $content = $this->mArticle->getUndoContent( $undorev, $oldrev );
945 if ( $content ===
false ) {
946 # Warn the user that something went wrong
947 $undoMsg =
'failure';
949 $oldContent = $this->mArticle->getPage()->getContent(
Revision::RAW );
953 if ( $newContent->equals( $oldContent ) ) {
954 # Tell the user that the undo results in no change,
955 # i.e. the revisions were already undone.
956 $undoMsg =
'nochange';
959 # Inform the user of our success and set an automatic edit summary
960 $undoMsg =
'success';
962 # If we just undid one rev, use an autosummary
963 $firstrev = $oldrev->getNext();
964 if ( $firstrev && $firstrev->getId() == $undo ) {
965 $userText = $undorev->getUserText();
966 if ( $userText ===
'' ) {
968 'undo-summary-username-hidden',
970 )->inContentLanguage()->text();
976 )->inContentLanguage()->text();
978 if ( $this->summary ===
'' ) {
979 $this->summary = $undoSummary;
981 $this->summary = $undoSummary .
wfMessage(
'colon-separator' )
984 $this->undidRev = $undo;
986 $this->formtype =
'diff';
997 $class = ( $undoMsg ==
'success' ?
'' :
'error ' ) .
"mw-undo-{$undoMsg}";
998 $this->editFormPageTop .=
$wgOut->parse(
"<div class=\"{$class}\">" .
999 wfMessage(
'undo-' . $undoMsg )->plain() .
'</div>',
true,
true );
1002 if ( $content ===
false ) {
1003 $content = $this->getOriginalContent(
$wgUser );
1027 private function getOriginalContent(
User $user ) {
1028 if ( $this->
section ==
'new' ) {
1029 return $this->getCurrentContent();
1031 $revision = $this->mArticle->getRevisionFetched();
1032 if ( $revision ===
null ) {
1033 if ( !$this->contentModel ) {
1034 $this->contentModel = $this->getTitle()->getContentModel();
1038 return $handler->makeEmptyContent();
1052 protected function getCurrentContent() {
1053 $rev = $this->mArticle->getRevision();
1056 if ( $content ===
false || $content ===
null ) {
1057 if ( !$this->contentModel ) {
1058 $this->contentModel = $this->getTitle()->getContentModel();
1062 return $handler->makeEmptyContent();
1064 # nasty side-effect, but needed for consistency
1065 $this->contentModel =
$rev->getContentModel();
1066 $this->contentFormat =
$rev->getContentFormat();
1078 public function setPreloadedText( $text ) {
1081 $content = $this->toEditContent( $text );
1083 $this->setPreloadedContent( $content );
1093 public function setPreloadedContent(
Content $content ) {
1094 $this->mPreloadContent = $content;
1107 protected function getPreloadedText( $preload ) {
1110 $content = $this->getPreloadedContent( $preload );
1111 $text = $this->toEditText( $content );
1127 protected function getPreloadedContent( $preload,
$params =
array() ) {
1130 if ( !empty( $this->mPreloadContent ) ) {
1131 return $this->mPreloadContent;
1136 if ( $preload ===
'' ) {
1137 return $handler->makeEmptyContent();
1141 # Check for existence to avoid getting MediaWiki:Noarticletext
1144 return $handler->makeEmptyContent();
1148 if ( $page->isRedirect() ) {
1149 $title = $page->getRedirectTarget();
1153 return $handler->makeEmptyContent();
1163 return $handler->makeEmptyContent();
1166 if ( $content->
getModel() !== $handler->getModelID() ) {
1167 $converted = $content->
convert( $handler->getModelID() );
1169 if ( !$converted ) {
1171 wfDebug(
"Attempt to preload incompatible content: "
1172 .
"can't convert " . $content->
getModel()
1173 .
" to " . $handler->getModelID() );
1175 return $handler->makeEmptyContent();
1178 $content = $converted;
1191 function tokenOk( &$request ) {
1193 $token = $request->getVal(
'wpEditToken' );
1194 $this->mTokenOk =
$wgUser->matchEditToken( $token );
1195 $this->mTokenOkExceptSuffix =
$wgUser->matchEditTokenNoSuffix( $token );
1196 return $this->mTokenOk;
1215 protected function setPostEditCookie() {
1216 $revisionId = $this->mArticle->getLatest();
1217 $postEditKey = self::POST_EDIT_COOKIE_KEY_PREFIX . $revisionId;
1220 $response->setcookie( $postEditKey,
'1', time() + self::POST_EDIT_COOKIE_DURATION,
array(
1222 'httpOnly' =>
false,
1231 public function attemptSave() {
1234 $resultDetails =
false;
1235 # Allow bots to exempt some edits from bot flagging
1236 $bot =
$wgUser->isAllowed(
'bot' ) && $this->bot;
1237 $status = $this->internalAttemptSave( $resultDetails, $bot );
1239 return $this->handleStatus( $status, $resultDetails );
1251 private function handleStatus(
Status $status, $resultDetails ) {
1255 if ( $status->value == self::AS_SUCCESS_UPDATE || $status->value == self::AS_SUCCESS_NEW_ARTICLE ) {
1256 $this->didSave =
true;
1257 if ( !$resultDetails[
'nullEdit'] ) {
1258 $this->setPostEditCookie();
1262 switch ( $status->value ) {
1263 case self::AS_HOOK_ERROR_EXPECTED:
1264 case self::AS_CONTENT_TOO_BIG:
1265 case self::AS_ARTICLE_WAS_DELETED:
1266 case self::AS_CONFLICT_DETECTED:
1267 case self::AS_SUMMARY_NEEDED:
1268 case self::AS_TEXTBOX_EMPTY:
1269 case self::AS_MAX_ARTICLE_SIZE_EXCEEDED:
1273 case self::AS_HOOK_ERROR:
1276 case self::AS_PARSE_ERROR:
1277 $wgOut->addWikiText(
'<div class="error">' . $status->
getWikiText() .
'</div>' );
1280 case self::AS_SUCCESS_NEW_ARTICLE:
1281 $query = $resultDetails[
'redirect'] ?
'redirect=no' :
'';
1282 $anchor = isset( $resultDetails[
'sectionanchor'] ) ? $resultDetails[
'sectionanchor'] :
'';
1283 $wgOut->redirect( $this->mTitle->getFullURL(
$query ) . $anchor );
1286 case self::AS_SUCCESS_UPDATE:
1288 $sectionanchor = $resultDetails[
'sectionanchor'];
1291 wfRunHooks(
'ArticleUpdateBeforeRedirect',
array( $this->mArticle, &$sectionanchor, &$extraQuery ) );
1293 if ( $resultDetails[
'redirect'] ) {
1294 if ( $extraQuery ==
'' ) {
1295 $extraQuery =
'redirect=no';
1297 $extraQuery =
'redirect=no&' . $extraQuery;
1300 $wgOut->redirect( $this->mTitle->getFullURL( $extraQuery ) . $sectionanchor );
1303 case self::AS_BLANK_ARTICLE:
1304 $wgOut->redirect( $this->getContextTitle()->getFullURL() );
1307 case self::AS_SPAM_ERROR:
1308 $this->spamPageWithContent( $resultDetails[
'spam'] );
1311 case self::AS_BLOCKED_PAGE_FOR_USER:
1314 case self::AS_IMAGE_REDIRECT_ANON:
1315 case self::AS_IMAGE_REDIRECT_LOGGED:
1318 case self::AS_READ_ONLY_PAGE_ANON:
1319 case self::AS_READ_ONLY_PAGE_LOGGED:
1322 case self::AS_READ_ONLY_PAGE:
1325 case self::AS_RATE_LIMITED:
1328 case self::AS_NO_CREATE_PERMISSION:
1329 $permission = $this->mTitle->isTalkPage() ?
'createtalk' :
'createpage';
1337 $this->hookError =
'<div class="error">' . $status->getWikitext() .
1355 array( $this, $content, &$this->hookError, $this->summary ) ) ) {
1357 # Error messages etc. could be handled within the hook...
1358 $status->
fatal(
'hookaborted' );
1359 $status->value = self::AS_HOOK_ERROR;
1361 } elseif ( $this->hookError !=
'' ) {
1362 # ...or the hook could be expecting us to produce an error
1363 $status->
fatal(
'hookaborted' );
1364 $status->value = self::AS_HOOK_ERROR_EXPECTED;
1371 $user, $this->minoredit ) ) ) {
1373 # Error messages etc. could be handled within the hook...
1376 $status->fatal(
'hookaborted' );
1377 $status->value = self::AS_HOOK_ERROR;
1379 } elseif ( !$status->isOK() ) {
1380 # ...or the hook could be expecting us to produce an error
1382 $this->hookError = $status->getWikiText();
1383 $status->fatal(
'hookaborted' );
1384 $status->value = self::AS_HOOK_ERROR_EXPECTED;
1407 function internalAttemptSave( &
$result, $bot =
false ) {
1416 wfDebug(
"Hook 'EditPage::attemptSave' aborted article saving\n" );
1417 $status->fatal(
'hookaborted' );
1418 $status->value = self::AS_HOOK_ERROR;
1424 $spam = $wgRequest->getText(
'wpAntispam' );
1425 if ( $spam !==
'' ) {
1430 $this->mTitle->getPrefixedText() .
1431 '" submitted bogus field "' .
1435 $status->fatal(
'spamprotectionmatch',
false );
1436 $status->value = self::AS_SPAM_ERROR;
1443 # Construct Content object
1444 $textbox_content = $this->toEditContent( $this->textbox1 );
1446 $status->fatal(
'content-failed-to-parse', $this->contentModel, $this->contentFormat, $ex->getMessage() );
1447 $status->value = self::AS_PARSE_ERROR;
1453 # Check image redirect
1454 if ( $this->mTitle->getNamespace() ==
NS_FILE &&
1455 $textbox_content->isRedirect() &&
1456 !
$wgUser->isAllowed(
'upload' ) ) {
1457 $code =
$wgUser->isAnon() ? self::AS_IMAGE_REDIRECT_ANON : self::AS_IMAGE_REDIRECT_LOGGED;
1458 $status->setResult(
false, $code );
1467 $match = self::matchSummarySpamRegex( $this->summary );
1468 if ( $match ===
false && $this->
section ==
'new' ) {
1469 # $wgSpamRegex is enforced on this new heading/summary because, unlike
1470 # regular summaries, it is added to the actual wikitext.
1471 if ( $this->sectiontitle !==
'' ) {
1472 # This branch is taken when the API is used with the 'sectiontitle' parameter.
1473 $match = self::matchSpamRegex( $this->sectiontitle );
1475 # This branch is taken when the "Add Topic" user interface is used, or the API
1476 # is used with the 'summary' parameter.
1477 $match = self::matchSpamRegex( $this->summary );
1480 if ( $match ===
false ) {
1481 $match = self::matchSpamRegex( $this->textbox1 );
1483 if ( $match !==
false ) {
1485 $ip = $wgRequest->getIP();
1486 $pdbk = $this->mTitle->getPrefixedDBkey();
1487 $match = str_replace(
"\n",
'', $match );
1488 wfDebugLog(
'SpamRegex',
"$ip spam regex hit [[$pdbk]]: \"$match\"" );
1489 $status->fatal(
'spamprotectionmatch', $match );
1490 $status->value = self::AS_SPAM_ERROR;
1495 if ( !
wfRunHooks(
'EditFilter',
array( $this, $this->textbox1, $this->
section, &$this->hookError, $this->summary ) ) ) {
1496 # Error messages etc. could be handled within the hook...
1497 $status->fatal(
'hookaborted' );
1498 $status->value = self::AS_HOOK_ERROR;
1502 } elseif ( $this->hookError !=
'' ) {
1503 # ...or the hook could be expecting us to produce an error
1504 $status->fatal(
'hookaborted' );
1505 $status->value = self::AS_HOOK_ERROR_EXPECTED;
1511 if (
$wgUser->isBlockedFrom( $this->mTitle,
false ) ) {
1513 $wgUser->spreadAnyEditBlock();
1514 # Check block state against master, thus 'false'.
1515 $status->setResult(
false, self::AS_BLOCKED_PAGE_FOR_USER );
1521 $this->kblength = (int)( strlen( $this->textbox1 ) / 1024 );
1522 if ( $this->kblength > $wgMaxArticleSize ) {
1524 $this->tooBig =
true;
1525 $status->setResult(
false, self::AS_CONTENT_TOO_BIG );
1531 if ( !
$wgUser->isAllowed(
'edit' ) ) {
1533 $status->setResult(
false, self::AS_READ_ONLY_PAGE_ANON );
1538 $status->fatal(
'readonlytext' );
1539 $status->value = self::AS_READ_ONLY_PAGE_LOGGED;
1547 $status->fatal(
'readonlytext' );
1548 $status->value = self::AS_READ_ONLY_PAGE;
1553 if (
$wgUser->pingLimiter() ||
$wgUser->pingLimiter(
'linkpurge', 0 ) ) {
1554 $status->fatal(
'actionthrottledtext' );
1555 $status->value = self::AS_RATE_LIMITED;
1561 # If the article has been deleted while editing, don't save it without
1563 if ( $this->wasDeletedSinceLastEdit() && !$this->recreate ) {
1564 $status->setResult(
false, self::AS_ARTICLE_WAS_DELETED );
1572 # Load the page data from the master. If anything changes in the meantime,
1573 # we detect it by using page_latest like a token in a 1 try compare-and-swap.
1574 $this->mArticle->loadPageData(
'fromdbmaster' );
1575 $new = !$this->mArticle->exists();
1579 if ( !$this->mTitle->userCan(
'create',
$wgUser ) ) {
1580 $status->fatal(
'nocreatetext' );
1581 $status->value = self::AS_NO_CREATE_PERMISSION;
1582 wfDebug( __METHOD__ .
": no create permission\n" );
1590 $defaultMessageText = $this->mTitle->getDefaultMessageText();
1591 if ( $this->mTitle->getNamespace() ===
NS_MEDIAWIKI && $defaultMessageText !==
false ) {
1592 $defaultText = $defaultMessageText;
1597 if ( $this->textbox1 === $defaultText ) {
1598 $status->setResult(
false, self::AS_BLANK_ARTICLE );
1603 if ( !$this->runPostMergeFilters( $textbox_content, $status,
$wgUser ) ) {
1608 $content = $textbox_content;
1610 $result[
'sectionanchor'] =
'';
1611 if ( $this->
section ==
'new' ) {
1612 if ( $this->sectiontitle !==
'' ) {
1617 $result[
'sectionanchor'] =
$wgParser->guessLegacySectionNameFromWikiText( $this->sectiontitle );
1622 if ( $this->summary ===
'' ) {
1623 $cleanSectionTitle =
$wgParser->stripSectionName( $this->sectiontitle );
1624 $this->summary =
wfMessage(
'newsectionsummary' )
1625 ->rawParams( $cleanSectionTitle )->inContentLanguage()->text();
1627 } elseif ( $this->summary !==
'' ) {
1632 $result[
'sectionanchor'] =
$wgParser->guessLegacySectionNameFromWikiText( $this->summary );
1635 $cleanSummary =
$wgParser->stripSectionName( $this->summary );
1636 $this->summary =
wfMessage(
'newsectionsummary' )
1637 ->rawParams( $cleanSummary )->inContentLanguage()->text();
1641 $status->value = self::AS_SUCCESS_NEW_ARTICLE;
1645 # Article exists. Check for edit conflict.
1647 $this->mArticle->clear(); # Force reload
of dates,
etc.
1648 $timestamp = $this->mArticle->getTimestamp();
1650 wfDebug(
"timestamp: {$timestamp}, edittime: {$this->edittime}\n" );
1653 $this->isConflict =
true;
1654 if ( $this->
section ==
'new' ) {
1655 if ( $this->mArticle->getUserText() ==
$wgUser->getName() &&
1660 wfDebug( __METHOD__ .
": duplicate new section submission; trigger edit conflict!\n" );
1663 $this->isConflict =
false;
1664 wfDebug( __METHOD__ .
": conflict suppressed; new section\n" );
1667 $wgUser->getId(), $this->edittime ) ) {
1668 # Suppress edit conflict with self, except for section edits where merging is required.
1669 wfDebug( __METHOD__ .
": Suppressing edit conflict, same user.\n" );
1670 $this->isConflict =
false;
1675 if ( $this->sectiontitle !==
'' ) {
1676 $sectionTitle = $this->sectiontitle;
1683 if ( $this->isConflict ) {
1684 wfDebug( __METHOD__ .
": conflict! getting section '{$this->section}' for time '{$this->edittime}'"
1685 .
" (article time '{$timestamp}')\n" );
1687 $content = $this->mArticle->replaceSectionContent( $this->
section, $textbox_content, $sectionTitle, $this->edittime );
1689 wfDebug( __METHOD__ .
": getting section '{$this->section}'\n" );
1690 $content = $this->mArticle->replaceSectionContent( $this->
section, $textbox_content, $sectionTitle );
1693 if ( is_null( $content ) ) {
1694 wfDebug( __METHOD__ .
": activating conflict; section replace failed.\n" );
1695 $this->isConflict =
true;
1696 $content = $textbox_content;
1697 } elseif ( $this->isConflict ) {
1699 if ( $this->mergeChangesIntoContent( $content ) ) {
1701 $this->isConflict =
false;
1702 wfDebug( __METHOD__ .
": Suppressing edit conflict, successful merge.\n" );
1706 wfDebug( __METHOD__ .
": Keeping edit conflict, failed merge.\n" );
1710 if ( $this->isConflict ) {
1711 $status->setResult(
false, self::AS_CONFLICT_DETECTED );
1716 if ( !$this->runPostMergeFilters( $content, $status,
$wgUser ) ) {
1721 if ( $this->
section ==
'new' ) {
1723 if ( !$this->allowBlankSummary && trim( $this->summary ) ==
'' ) {
1724 $this->missingSummary =
true;
1725 $status->fatal(
'missingsummary' );
1726 $status->value = self::AS_SUMMARY_NEEDED;
1732 if ( $this->textbox1 ==
'' ) {
1733 $this->missingComment =
true;
1734 $status->fatal(
'missingcommenttext' );
1735 $status->value = self::AS_TEXTBOX_EMPTY;
1739 } elseif ( !$this->allowBlankSummary
1740 && !$content->
equals( $this->getOriginalContent(
$wgUser ) )
1742 && md5( $this->summary ) == $this->autoSumm
1744 $this->missingSummary =
true;
1745 $status->fatal(
'missingsummary' );
1746 $status->value = self::AS_SUMMARY_NEEDED;
1753 $sectionanchor =
'';
1754 if ( $this->
section ==
'new' ) {
1755 if ( $this->sectiontitle !==
'' ) {
1756 $sectionanchor =
$wgParser->guessLegacySectionNameFromWikiText( $this->sectiontitle );
1760 if ( $this->summary ===
'' ) {
1761 $cleanSectionTitle =
$wgParser->stripSectionName( $this->sectiontitle );
1762 $this->summary =
wfMessage(
'newsectionsummary' )
1763 ->rawParams( $cleanSectionTitle )->inContentLanguage()->text();
1765 } elseif ( $this->summary !==
'' ) {
1766 $sectionanchor =
$wgParser->guessLegacySectionNameFromWikiText( $this->summary );
1767 # This is a new section, so create a link to the new section
1768 # in the revision summary.
1769 $cleanSummary =
$wgParser->stripSectionName( $this->summary );
1770 $this->summary =
wfMessage(
'newsectionsummary' )
1771 ->rawParams( $cleanSummary )->inContentLanguage()->text();
1773 } elseif ( $this->
section !=
'' ) {
1774 # Try to get a section anchor from the section source, redirect to edited section if header found
1775 # XXX: might be better to integrate this into Article::replaceSection
1776 # for duplicate heading checking and maybe parsing
1777 $hasmatch = preg_match(
"/^ *([=]{1,6})(.*?)(\\1) *\\n/i", $this->textbox1,
$matches );
1778 # we can't deal with anchors, includes, html etc in the header for now,
1779 # headline would need to be parsed to improve this
1780 if ( $hasmatch && strlen(
$matches[2] ) > 0 ) {
1784 $result[
'sectionanchor'] = $sectionanchor;
1791 $this->textbox1 = $this->toEditText( $content );
1794 $status->value = self::AS_SUCCESS_UPDATE;
1798 $this->kblength = (int)( strlen( $this->toEditText( $content ) ) / 1024 );
1799 if ( $this->kblength > $wgMaxArticleSize ) {
1800 $this->tooBig =
true;
1801 $status->setResult(
false, self::AS_MAX_ARTICLE_SIZE_EXCEEDED );
1808 ( ( $this->minoredit && !$this->isNew ) ?
EDIT_MINOR : 0 ) |
1811 $doEditStatus = $this->mArticle->doEditContent( $content, $this->summary,
$flags,
1812 false,
null, $this->contentFormat );
1814 if ( !$doEditStatus->isOK() ) {
1818 $errors = $doEditStatus->getErrorsArray();
1819 if ( in_array( $errors[0][0],
1820 array(
'edit-gone-missing',
'edit-conflict',
'edit-already-exists' ) )
1822 $this->isConflict =
true;
1824 $doEditStatus->value = self::AS_END;
1827 return $doEditStatus;
1830 $result[
'nullEdit'] = $doEditStatus->hasMessage(
'edit-no-change' );
1833 $wgUser->pingLimiter(
'linkpurge' );
1836 $this->updateWatchlist();
1844 protected function updateWatchlist() {
1852 $watch = $this->watchthis;
1872 function mergeChangesInto( &$editText ) {
1875 $editContent = $this->toEditContent( $editText );
1877 $ok = $this->mergeChangesIntoContent( $editContent );
1880 $editText = $this->toEditText( $editContent );
1897 private function mergeChangesIntoContent( &$editContent ) {
1903 $baseRevision = $this->getBaseRevision();
1904 $baseContent = $baseRevision ? $baseRevision->getContent() :
null;
1906 if ( is_null( $baseContent ) ) {
1913 $currentContent = $currentRevision ? $currentRevision->getContent() :
null;
1915 if ( is_null( $currentContent ) ) {
1922 $result = $handler->merge3( $baseContent, $editContent, $currentContent );
1937 function getBaseRevision() {
1938 if ( !$this->mBaseRevision ) {
1941 $db, $this->mTitle, $this->edittime );
1943 return $this->mBaseRevision;
1953 public static function matchSpamRegex( $text ) {
1956 $regexes = (
array)$wgSpamRegex;
1957 return self::matchSpamRegexInternal( $text, $regexes );
1967 public static function matchSummarySpamRegex( $text ) {
1968 global $wgSummarySpamRegex;
1969 $regexes = (
array)$wgSummarySpamRegex;
1970 return self::matchSpamRegexInternal( $text, $regexes );
1978 protected static function matchSpamRegexInternal( $text, $regexes ) {
1979 foreach ( $regexes
as $regex ) {
1981 if ( preg_match( $regex, $text,
$matches ) ) {
1988 function setHeaders() {
1991 $wgOut->addModules(
'mediawiki.action.edit' );
1992 $wgOut->addModuleStyles(
'mediawiki.action.edit.styles' );
1994 if (
$wgUser->getOption(
'uselivepreview',
false ) ) {
1995 $wgOut->addModules(
'mediawiki.action.edit.preview' );
1998 if (
$wgUser->getOption(
'useeditwarning',
false ) ) {
1999 $wgOut->addModules(
'mediawiki.action.edit.editWarning' );
2002 $wgOut->setRobotPolicy(
'noindex,nofollow' );
2004 # Enabled article-related sidebar, toplinks, etc.
2005 $wgOut->setArticleRelated(
true );
2007 $contextTitle = $this->getContextTitle();
2008 if ( $this->isConflict ) {
2009 $msg =
'editconflict';
2010 } elseif ( $contextTitle->exists() && $this->
section !=
'' ) {
2011 $msg = $this->
section ==
'new' ?
'editingcomment' :
'editingsection';
2013 $msg = $contextTitle->exists() || ( $contextTitle->getNamespace() ==
NS_MEDIAWIKI && $contextTitle->getDefaultMessageText() !==
false ) ?
2014 'editing' :
'creating';
2016 # Use the title defined by DISPLAYTITLE magic word when present
2017 $displayTitle = isset( $this->mParserOutput ) ? $this->mParserOutput->getDisplayTitle() :
false;
2018 if ( $displayTitle ===
false ) {
2019 $displayTitle = $contextTitle->getPrefixedText();
2027 protected function showIntro() {
2029 if ( $this->suppressIntro ) {
2033 $namespace = $this->mTitle->getNamespace();
2036 # Show a warning if editing an interface message
2037 $wgOut->wrapWikiMsg(
"<div class='mw-editinginterface'>\n$1\n</div>",
'editinginterface' );
2038 } elseif ( $namespace ==
NS_FILE ) {
2039 # Show a hint to shared repo
2042 $descUrl =
$file->getDescriptionUrl();
2043 # there must be a description url to show a hint to shared repo
2045 if ( !$this->mTitle->exists() ) {
2046 $wgOut->wrapWikiMsg(
"<div class=\"mw-sharedupload-desc-create\">\n$1\n</div>",
array(
2047 'sharedupload-desc-create',
$file->getRepo()->getDisplayName(), $descUrl
2050 $wgOut->wrapWikiMsg(
"<div class=\"mw-sharedupload-desc-edit\">\n$1\n</div>",
array(
2051 'sharedupload-desc-edit',
$file->getRepo()->getDisplayName(), $descUrl
2058 # Show a warning message when someone creates/edits a user (talk) page but the user does not exist
2059 # Show log extract when the user is currently blocked
2061 $parts = explode(
'/', $this->mTitle->getText(), 2 );
2062 $username = $parts[0];
2065 if ( !(
$user &&
$user->isLoggedIn() ) && !$ip ) { #
User does not exist
2066 $wgOut->wrapWikiMsg(
"<div class=\"mw-userpage-userdoesnotexist error\">\n$1\n</div>",
2068 } elseif (
$user->isBlocked() ) { # Show log extract
if the
user is currently blocked
2072 $user->getUserPage(),
2076 'showIfEmpty' =>
false,
2078 'blocked-notice-logextract',
2079 $user->getName() # Support GENDER
in notice
2085 # Try to add a custom edit intro, or use the standard one if this is not possible.
2086 if ( !$this->showCustomIntro() && !$this->mTitle->exists() ) {
2090 if (
$wgUser->isLoggedIn() ) {
2093 "<div class=\"mw-newarticletext plainlinks\">\n$1\n</div>",
2102 "<div class=\"mw-newarticletextanon plainlinks\">\n$1\n</div>",
2104 'newarticletextanon',
2110 # Give a notice if the user is editing a deleted/moved page...
2111 if ( !$this->mTitle->exists() ) {
2116 'conds' =>
array(
"log_action != 'revision'" ),
2117 'showIfEmpty' =>
false,
2118 'msgKey' =>
array(
'recreate-moveddeleted-warn' )
2129 protected function showCustomIntro() {
2130 if ( $this->editintro ) {
2135 $wgOut->addWikiTextTitleTidy(
'{{:' .
$title->getFullText() .
'}}', $this->mTitle );
2158 protected function toEditText( $content ) {
2159 if ( $content ===
null || $content ===
false ) {
2163 if ( is_string( $content ) ) {
2167 if ( !$this->isSupportedContentModel( $content->
getModel() ) ) {
2168 throw new MWException(
'This content model is not supported: '
2172 return $content->
serialize( $this->contentFormat );
2189 protected function toEditContent( $text ) {
2190 if ( $text ===
false || $text ===
null ) {
2195 $this->contentModel, $this->contentFormat );
2197 if ( !$this->isSupportedContentModel( $content->
getModel() ) ) {
2198 throw new MWException(
'This content model is not supported: '
2210 function showEditForm( $formCallback =
null ) {
2215 # need to parse the preview early so that we know which templates are used,
2216 # otherwise users with "show preview after edit box" will get a blank list
2217 # we parse this near the beginning so that setHeaders can do the title
2218 # setting work instead of leaving it in getPreviewText
2219 $previewOutput =
'';
2220 if ( $this->formtype ==
'preview' ) {
2221 $previewOutput = $this->getPreviewText();
2226 $this->setHeaders();
2228 if ( $this->showHeader() ===
false ) {
2233 $wgOut->addHTML( $this->editFormPageTop );
2235 if (
$wgUser->getOption(
'previewontop' ) ) {
2236 $this->displayPreviewArea( $previewOutput,
true );
2239 $wgOut->addHTML( $this->editFormTextTop );
2241 $showToolbar =
true;
2242 if ( $this->wasDeletedSinceLastEdit() ) {
2243 if ( $this->formtype ==
'save' ) {
2246 $showToolbar =
false;
2248 $wgOut->wrapWikiMsg(
"<div class='error mw-deleted-while-editing'>\n$1\n</div>",
2249 'deletedwhileediting' );
2256 'method' =>
'post',
'action' => $this->getActionURL( $this->getContextTitle() ),
2257 'enctype' =>
'multipart/form-data' ) ) );
2259 if ( is_callable( $formCallback ) ) {
2260 call_user_func_array( $formCallback,
array( &
$wgOut ) );
2267 .
Xml::element(
'input',
array(
'type' =>
'text',
'name' =>
'wpAntispam',
'id' =>
'wpAntispam',
'value' =>
'' ) )
2274 $this->showFormBeforeText();
2276 if ( $this->wasDeletedSinceLastEdit() &&
'save' == $this->formtype ) {
2277 $username = $this->lastDelete->user_name;
2278 $comment = $this->lastDelete->log_comment;
2283 ?
'confirmrecreate-noreason'
2284 :
'confirmrecreate';
2286 '<div class="mw-confirm-recreate">' .
2287 wfMessage( $key, $username,
"<nowiki>$comment</nowiki>" )->parse() .
2295 # When the summary is hidden, also hide them on preview/show changes
2296 if ( $this->nosummary ) {
2300 # If a blank edit summary was previously provided, and the appropriate
2301 # user preference is active, pass a hidden tag as wpIgnoreBlankSummary. This will stop the
2302 # user being bounced back more than once in the event that a summary
2305 # For a bit more sophisticated detection of blank summaries, hash the
2306 # automatic one and pass that in the hidden field wpAutoSummary.
2307 if ( $this->missingSummary || ( $this->
section ==
'new' && $this->nosummary ) ) {
2311 if ( $this->undidRev ) {
2315 if ( $this->hasPresetSummary ) {
2319 $this->autoSumm = md5(
'' );
2322 $autosumm = $this->autoSumm ? $this->autoSumm : md5( $this->summary );
2330 if ( $this->
section ==
'new' ) {
2331 $this->showSummaryInput(
true, $this->summary );
2332 $wgOut->addHTML( $this->getSummaryPreview(
true, $this->summary ) );
2335 $wgOut->addHTML( $this->editFormTextBeforeContent );
2337 if ( !$this->isCssJsSubpage && $showToolbar &&
$wgUser->getOption(
'showtoolbar' ) ) {
2338 $wgOut->addHTML( EditPage::getEditToolbar() );
2341 if ( $this->isConflict ) {
2346 $this->textbox2 = $this->textbox1;
2348 $content = $this->getCurrentContent();
2349 $this->textbox1 = $this->toEditText( $content );
2351 $this->showTextbox1();
2353 $this->showContentForm();
2356 $wgOut->addHTML( $this->editFormTextAfterContent );
2358 $this->showStandardInputs();
2360 $this->showFormAfterText();
2362 $this->showTosSummary();
2364 $this->showEditTools();
2366 $wgOut->addHTML( $this->editFormTextAfterTools .
"\n" );
2375 self::getPreviewLimitReport( $this->mParserOutput ) ) );
2377 $wgOut->addModules(
'mediawiki.action.edit.collapsibleFooter' );
2379 if ( $this->isConflict ) {
2381 $this->showConflict();
2384 $msg =
wfMessage(
'content-failed-to-parse', $this->contentModel, $this->contentFormat, $ex->getMessage() );
2385 $wgOut->addWikiText(
'<div class="error">' . $msg->text() .
'</div>' );
2389 $wgOut->addHTML( $this->editFormTextBottom .
"\n</form>\n" );
2391 if ( !
$wgUser->getOption(
'previewontop' ) ) {
2392 $this->displayPreviewArea( $previewOutput,
false );
2404 public static function extractSectionTitle( $text ) {
2405 preg_match(
"/^(=+)(.+)\\1\\s*(\n|$)/i", $text,
$matches );
2414 protected function showHeader() {
2417 if ( $this->mTitle->isTalkPage() ) {
2418 $wgOut->addWikiMsg(
'talkpagetext' );
2422 $wgOut->addHTML( implode(
"\n", $this->mTitle->getEditNotices( $this->oldid ) ) );
2424 if ( $this->isConflict ) {
2425 $wgOut->wrapWikiMsg(
"<div class='mw-explainconflict'>\n$1\n</div>",
'explainconflict' );
2426 $this->edittime = $this->mArticle->getTimestamp();
2428 if ( $this->
section !=
'' && !$this->isSectionEditSupported() ) {
2432 $wgOut->showErrorPage(
'sectioneditnotsupported-title',
'sectioneditnotsupported-text' );
2437 if ( !$this->summary && !$this->preview && !$this->diff ) {
2438 $sectionTitle = self::extractSectionTitle( $this->textbox1 );
2439 if ( $sectionTitle !==
false ) {
2440 $this->summary =
"/* $sectionTitle */ ";
2445 if ( $this->missingComment ) {
2446 $wgOut->wrapWikiMsg(
"<div id='mw-missingcommenttext'>\n$1\n</div>",
'missingcommenttext' );
2449 if ( $this->missingSummary && $this->
section !=
'new' ) {
2450 $wgOut->wrapWikiMsg(
"<div id='mw-missingsummary'>\n$1\n</div>",
'missingsummary' );
2453 if ( $this->missingSummary && $this->
section ==
'new' ) {
2454 $wgOut->wrapWikiMsg(
"<div id='mw-missingcommentheader'>\n$1\n</div>",
'missingcommentheader' );
2457 if ( $this->hookError !==
'' ) {
2458 $wgOut->addWikiText( $this->hookError );
2461 if ( !$this->checkUnicodeCompliantBrowser() ) {
2462 $wgOut->addWikiMsg(
'nonunicodebrowser' );
2465 if ( $this->
section !=
'new' ) {
2466 $revision = $this->mArticle->getRevisionFetched();
2471 $wgOut->wrapWikiMsg(
"<div class='mw-warning plainlinks'>\n$1\n</div>\n",
'rev-deleted-text-permission' );
2473 $wgOut->wrapWikiMsg(
"<div class='mw-warning plainlinks'>\n$1\n</div>\n",
'rev-deleted-text-view' );
2476 if ( !$revision->isCurrent() ) {
2477 $this->mArticle->setOldSubtitle( $revision->getId() );
2478 $wgOut->addWikiMsg(
'editingold' );
2480 } elseif ( $this->mTitle->exists() ) {
2483 $wgOut->wrapWikiMsg(
"<div class='errorbox'>\n$1\n</div>\n",
2484 array(
'missing-revision', $this->oldid ) );
2491 } elseif (
$wgUser->isAnon() ) {
2492 if ( $this->formtype !=
'preview' ) {
2493 $wgOut->wrapWikiMsg(
"<div id=\"mw-anon-edit-warning\">\n$1</div>",
'anoneditwarning' );
2495 $wgOut->wrapWikiMsg(
"<div id=\"mw-anon-preview-warning\">\n$1</div>",
'anonpreviewwarning' );
2498 if ( $this->isCssJsSubpage ) {
2499 # Check the skin exists
2500 if ( $this->isWrongCaseCssJsPage ) {
2501 $wgOut->wrapWikiMsg(
"<div class='error' id='mw-userinvalidcssjstitle'>\n$1\n</div>",
array(
'userinvalidcssjstitle', $this->mTitle->getSkinFromCssJsSubpage() ) );
2503 if ( $this->formtype !==
'preview' ) {
2504 if ( $this->isCssSubpage ) {
2505 $wgOut->wrapWikiMsg(
"<div id='mw-usercssyoucanpreview'>\n$1\n</div>",
array(
'usercssyoucanpreview' ) );
2508 if ( $this->isJsSubpage ) {
2509 $wgOut->wrapWikiMsg(
"<div id='mw-userjsyoucanpreview'>\n$1\n</div>",
array(
'userjsyoucanpreview' ) );
2515 if ( $this->mTitle->isProtected(
'edit' ) &&
2518 # Is the title semi-protected?
2519 if ( $this->mTitle->isSemiProtected() ) {
2520 $noticeMsg =
'semiprotectedpagewarning';
2522 # Then it must be protected based on static groups (regular)
2523 $noticeMsg =
'protectedpagewarning';
2526 array(
'lim' => 1,
'msgKey' =>
array( $noticeMsg ) ) );
2528 if ( $this->mTitle->isCascadeProtected() ) {
2529 # Is this page under cascading protection from some source pages?
2530 list( $cascadeSources, ) = $this->mTitle->getCascadeProtectionSources();
2531 $notice =
"<div class='mw-cascadeprotectedwarning'>\n$1\n";
2532 $cascadeSourcesCount = count( $cascadeSources );
2533 if ( $cascadeSourcesCount > 0 ) {
2534 # Explain, and list the titles responsible
2535 foreach ( $cascadeSources
as $page ) {
2536 $notice .=
'* [[:' . $page->getPrefixedText() .
"]]\n";
2539 $notice .=
'</div>';
2540 $wgOut->wrapWikiMsg( $notice,
array(
'cascadeprotectedwarning', $cascadeSourcesCount ) );
2542 if ( !$this->mTitle->exists() && $this->mTitle->getRestrictions(
'create' ) ) {
2545 'showIfEmpty' =>
false,
2546 'msgKey' =>
array(
'titleprotectedwarning' ),
2547 'wrap' =>
"<div class=\"mw-titleprotectedwarning\">\n$1</div>" ) );
2550 if ( $this->kblength ===
false ) {
2551 $this->kblength = (int)( strlen( $this->textbox1 ) / 1024 );
2554 if ( $this->tooBig || $this->kblength > $wgMaxArticleSize ) {
2555 $wgOut->wrapWikiMsg(
"<div class='error' id='mw-edit-longpageerror'>\n$1\n</div>",
2556 array(
'longpageerror',
$wgLang->formatNum( $this->kblength ),
$wgLang->formatNum( $wgMaxArticleSize ) ) );
2558 if ( !
wfMessage(
'longpage-hint' )->isDisabled() ) {
2559 $wgOut->wrapWikiMsg(
"<div id='mw-edit-longpage-hint'>\n$1\n</div>",
2560 array(
'longpage-hint',
$wgLang->formatSize( strlen( $this->textbox1 ) ), strlen( $this->textbox1 ) )
2564 # Add header copyright warning
2565 $this->showHeaderCopyrightWarning();
2582 function getSummaryInput(
$summary =
"", $labelText =
null, $inputAttrs =
null, $spanLabelAttrs =
null ) {
2584 $inputAttrs = ( is_array( $inputAttrs ) ? $inputAttrs :
array() ) +
array(
2585 'id' =>
'wpSummary',
2586 'maxlength' =>
'200',
2589 'spellcheck' =>
'true',
2592 $spanLabelAttrs = ( is_array( $spanLabelAttrs ) ? $spanLabelAttrs :
array() ) +
array(
2593 'class' => $this->missingSummary ?
'mw-summarymissed' :
'mw-summary',
2594 'id' =>
"wpSummaryLabel"
2599 $label =
Xml::tags(
'label', $inputAttrs[
'id'] ?
array(
'for' => $inputAttrs[
'id'] ) :
null, $labelText );
2600 $label =
Xml::tags(
'span', $spanLabelAttrs, $label );
2605 return array( $label, $input );
2615 protected function showSummaryInput( $isSubjectPreview,
$summary =
"" ) {
2617 # Add a class if 'missingsummary' is triggered to allow styling of the summary line
2618 $summaryClass = $this->missingSummary ?
'mw-summarymissed' :
'mw-summary';
2619 if ( $isSubjectPreview ) {
2620 if ( $this->nosummary ) {
2624 if ( !$this->mShowSummaryField ) {
2629 $labelText =
wfMessage( $isSubjectPreview ?
'subject' :
'summary' )->parse();
2630 list( $label, $input ) = $this->getSummaryInput(
$summary, $labelText,
array(
'class' => $summaryClass ),
array() );
2631 $wgOut->addHTML(
"{$label} {$input}" );
2641 protected function getSummaryPreview( $isSubjectPreview,
$summary =
"" ) {
2644 if ( !
$summary || ( !$this->preview && !$this->diff ) ) {
2650 if ( $isSubjectPreview ) {
2652 ->inContentLanguage()->text();
2655 $message = $isSubjectPreview ?
'subject-preview' :
'summary-preview';
2661 protected function showFormBeforeText() {
2664 $wgOut->addHTML( <<<HTML
2665 <input
type=
'hidden' value=
"{$section}" name=
"wpSection" />
2666 <input
type=
'hidden' value=
"{$this->starttime}" name=
"wpStarttime" />
2667 <input
type=
'hidden' value=
"{$this->edittime}" name=
"wpEdittime" />
2668 <input
type=
'hidden' value=
"{$this->scrolltop}" name=
"wpScrolltop" id=
"wpScrolltop" />
2672 if ( !$this->checkUnicodeCompliantBrowser() ) {
2677 protected function showFormAfterText() {
2702 protected function showContentForm() {
2703 $this->showTextbox1();
2714 protected function showTextbox1(
$customAttribs =
null, $textoverride =
null ) {
2715 if ( $this->wasDeletedSinceLastEdit() && $this->formtype ==
'save' ) {
2719 if ( $this->mTitle->isProtected(
'edit' ) &&
2722 # Is the title semi-protected?
2723 if ( $this->mTitle->isSemiProtected() ) {
2724 $classes[] =
'mw-textarea-sprotected';
2726 # Then it must be protected based on static groups (regular)
2727 $classes[] =
'mw-textarea-protected';
2729 # Is the title cascade-protected?
2730 if ( $this->mTitle->isCascadeProtected() ) {
2731 $classes[] =
'mw-textarea-cprotected';
2741 if ( count( $classes ) ) {
2742 if ( isset(
$attribs[
'class'] ) ) {
2745 $attribs[
'class'] = implode(
' ', $classes );
2749 $this->showTextbox( $textoverride !==
null ? $textoverride : $this->textbox1,
'wpTextbox1',
$attribs );
2752 protected function showTextbox2() {
2753 $this->showTextbox( $this->textbox2,
'wpTextbox2',
array(
'tabindex' => 6,
'readonly' ) );
2759 $wikitext = $this->safeUnicodeOutput( $text );
2760 if ( strval( $wikitext ) !==
'' ) {
2771 'cols' =>
$wgUser->getIntOption(
'cols' ),
2772 'rows' =>
$wgUser->getIntOption(
'rows' ),
2776 $pageLang = $this->mTitle->getPageLanguage();
2777 $attribs[
'lang'] = $pageLang->getCode();
2778 $attribs[
'dir'] = $pageLang->getDir();
2783 protected function displayPreviewArea( $previewOutput, $isOnTop =
false ) {
2787 $classes[] =
'ontop';
2790 $attribs =
array(
'id' =>
'wikiPreview',
'class' => implode(
' ', $classes ) );
2792 if ( $this->formtype !=
'preview' ) {
2793 $attribs[
'style'] =
'display: none;';
2798 if ( $this->formtype ==
'preview' ) {
2799 $this->showPreview( $previewOutput );
2802 $wgOut->addHTML(
'</div>' );
2804 if ( $this->formtype ==
'diff' ) {
2808 $msg =
wfMessage(
'content-failed-to-parse', $this->contentModel, $this->contentFormat, $ex->getMessage() );
2809 $wgOut->addWikiText(
'<div class="error">' . $msg->text() .
'</div>' );
2820 protected function showPreview( $text ) {
2822 if ( $this->mTitle->getNamespace() ==
NS_CATEGORY ) {
2823 $this->mArticle->openShowCategory();
2825 # This hook seems slightly odd here, but makes things more
2826 # consistent for extensions.
2828 $wgOut->addHTML( $text );
2829 if ( $this->mTitle->getNamespace() ==
NS_CATEGORY ) {
2830 $this->mArticle->closeShowCategory();
2841 function showDiff() {
2844 $oldtitlemsg =
'currentrev';
2845 # if message does not exist, show diff against the preloaded default
2846 if ( $this->mTitle->getNamespace() ==
NS_MEDIAWIKI && !$this->mTitle->exists() ) {
2847 $oldtext = $this->mTitle->getDefaultMessageText();
2848 if ( $oldtext !==
false ) {
2849 $oldtitlemsg =
'defaultmessagetext';
2850 $oldContent = $this->toEditContent( $oldtext );
2855 $oldContent = $this->getCurrentContent();
2858 $textboxContent = $this->toEditContent( $this->textbox1 );
2860 $newContent = $this->mArticle->replaceSectionContent(
2861 $this->
section, $textboxContent,
2862 $this->summary, $this->edittime );
2864 if ( $newContent ) {
2869 $newContent = $newContent->preSaveTransform( $this->mTitle,
$wgUser, $popts );
2872 if ( ( $oldContent && !$oldContent->isEmpty() ) || ( $newContent && !$newContent->isEmpty() ) ) {
2873 $oldtitle =
wfMessage( $oldtitlemsg )->parse();
2874 $newtitle =
wfMessage(
'yourtext' )->parse();
2876 if ( !$oldContent ) {
2877 $oldContent = $newContent->getContentHandler()->makeEmptyContent();
2880 if ( !$newContent ) {
2881 $newContent = $oldContent->getContentHandler()->makeEmptyContent();
2884 $de = $oldContent->getContentHandler()->createDifferenceEngine( $this->mArticle->getContext() );
2885 $de->setContent( $oldContent, $newContent );
2887 $difftext = $de->getDiff( $oldtitle, $newtitle );
2888 $de->showDiffStyle();
2893 $wgOut->addHTML(
'<div id="wikiDiff">' . $difftext .
'</div>' );
2899 protected function showHeaderCopyrightWarning() {
2900 $msg =
'editpage-head-copy-warn';
2901 if ( !
wfMessage( $msg )->isDisabled() ) {
2903 $wgOut->wrapWikiMsg(
"<div class='editpage-head-copywarn'>\n$1\n</div>",
2904 'editpage-head-copy-warn' );
2916 protected function showTosSummary() {
2917 $msg =
'editpage-tos-summary';
2919 if ( !
wfMessage( $msg )->isDisabled() ) {
2921 $wgOut->addHTML(
'<div class="mw-tos-summary">' );
2922 $wgOut->addWikiMsg( $msg );
2923 $wgOut->addHTML(
'</div>' );
2927 protected function showEditTools() {
2929 $wgOut->addHTML(
'<div class="mw-editTools">' .
2930 wfMessage(
'edittools' )->inContentLanguage()->parse() .
2939 protected function getCopywarn() {
2940 return self::getCopyrightWarning( $this->mTitle );
2951 public static function getCopyrightWarning(
$title, $format =
'plain' ) {
2953 if ( $wgRightsText ) {
2954 $copywarnMsg =
array(
'copyrightwarning',
2955 '[[' .
wfMessage(
'copyrightpage' )->inContentLanguage()->
text() .
']]',
2958 $copywarnMsg =
array(
'copyrightwarning2',
2959 '[[' .
wfMessage(
'copyrightpage' )->inContentLanguage()->
text() .
']]' );
2964 return "<div id=\"editpage-copywarn\">\n" .
2965 call_user_func_array(
'wfMessage', $copywarnMsg )->$format() .
"\n</div>";
2975 public static function getPreviewLimitReport(
$output ) {
2983 wfMessage(
'limitreport-title' )->parseAsBlock()
2990 'class' =>
'preview-limit-report wikitable'
2999 $valueMsg =
wfMessage(
array(
"$key-value-html",
"$key-value" ) );
3000 if ( !$valueMsg->exists() ) {
3001 $valueMsg =
new RawMessage(
'$1' );
3003 if ( !$keyMsg->isDisabled() && !$valueMsg->isDisabled() ) {
3018 return $limitReport;
3021 protected function showStandardInputs( &
$tabindex = 2 ) {
3023 $wgOut->addHTML(
"<div class='editOptions'>\n" );
3025 if ( $this->
section !=
'new' ) {
3026 $this->showSummaryInput(
false, $this->summary );
3027 $wgOut->addHTML( $this->getSummaryPreview(
false, $this->summary ) );
3030 $checkboxes = $this->getCheckboxes(
$tabindex,
3031 array(
'minor' => $this->minoredit,
'watch' => $this->watchthis ) );
3032 $wgOut->addHTML(
"<div class='editCheckboxes'>" . implode( $checkboxes,
"\n" ) .
"</div>\n" );
3035 $wgOut->addWikiText( $this->getCopywarn() );
3036 $wgOut->addHTML( $this->editFormTextAfterWarn );
3038 $wgOut->addHTML(
"<div class='editButtons'>\n" );
3039 $wgOut->addHTML( implode( $this->getEditButtons(
$tabindex ),
"\n" ) .
"\n" );
3041 $cancel = $this->getCancelLink();
3042 if ( $cancel !==
'' ) {
3044 array(
'class' =>
'mw-editButtons-pipe-separator' ),
3048 $edithelp =
'<a target="helpwindow" href="' . $edithelpurl .
'">' .
3049 wfMessage(
'edithelp' )->escaped() .
'</a> ' .
3051 $wgOut->addHTML(
" <span class='cancelLink'>{$cancel}</span>\n" );
3052 $wgOut->addHTML(
" <span class='editHelp'>{$edithelp}</span>\n" );
3053 $wgOut->addHTML(
"</div><!-- editButtons -->\n" );
3055 $wgOut->addHTML(
"</div><!-- editOptions -->\n" );
3062 protected function showConflict() {
3066 $wgOut->wrapWikiMsg(
'<h2>$1</h2>',
"yourdiff" );
3068 $content1 = $this->toEditContent( $this->textbox1 );
3069 $content2 = $this->toEditContent( $this->textbox2 );
3072 $de = $handler->createDifferenceEngine( $this->mArticle->getContext() );
3073 $de->setContent( $content2, $content1 );
3079 $wgOut->wrapWikiMsg(
'<h2>$1</h2>',
"yourtext" );
3080 $this->showTextbox2();
3087 public function getCancelLink() {
3088 $cancelParams =
array();
3089 if ( !$this->isConflict && $this->oldid > 0 ) {
3090 $cancelParams[
'oldid'] = $this->oldid;
3094 $this->getContextTitle(),
3096 array(
'id' =>
'mw-editform-cancel' ),
3120 protected function wasDeletedSinceLastEdit() {
3121 if ( $this->deletedSinceEdit !==
null ) {
3122 return $this->deletedSinceEdit;
3125 $this->deletedSinceEdit =
false;
3127 if ( $this->mTitle->isDeletedQuick() ) {
3128 $this->lastDelete = $this->getLastDelete();
3129 if ( $this->lastDelete ) {
3131 if ( $deleteTime > $this->starttime ) {
3132 $this->deletedSinceEdit =
true;
3137 return $this->deletedSinceEdit;
3140 protected function getLastDelete() {
3142 $data =
$dbr->selectRow(
3143 array(
'logging',
'user' ),
3156 'log_namespace' => $this->mTitle->getNamespace(),
3157 'log_title' => $this->mTitle->getDBkey(),
3158 'log_type' =>
'delete',
3159 'log_action' =>
'delete',
3163 array(
'LIMIT' => 1,
'ORDER BY' =>
'log_timestamp DESC' )
3166 if ( is_object( $data ) ) {
3168 $data->user_name =
wfMessage(
'rev-deleted-user' )->escaped();
3172 $data->log_comment =
wfMessage(
'rev-deleted-comment' )->escaped();
3183 function getPreviewText() {
3188 if ( $wgRawHtml && !$this->mTokenOk ) {
3192 if ( $this->textbox1 !==
'' ) {
3196 $parsedNote =
$wgOut->parse(
"<div class='previewnote'>" .
3197 wfMessage(
'session_fail_preview_html' )->
text() .
"</div>",
true,
true );
3206 $content = $this->toEditContent( $this->textbox1 );
3209 if ( !
wfRunHooks(
'AlternateEditPreview',
array( $this, &$content, &$previewHTML, &$this->mParserOutput ) ) ) {
3211 return $previewHTML;
3214 # provide a anchor link to the editform
3215 $continueEditing =
'<span class="mw-continue-editing">' .
3216 '[[#' . self::EDITFORM_ID .
'|' .
$wgLang->getArrow() .
' ' .
3217 wfMessage(
'continue-editing' )->text() .
']]</span>';
3218 if ( $this->mTriedSave && !$this->mTokenOk ) {
3219 if ( $this->mTokenOkExceptSuffix ) {
3220 $note =
wfMessage(
'token_suffix_mismatch' )->plain();
3223 $note =
wfMessage(
'session_fail_preview' )->plain();
3225 } elseif ( $this->incompleteForm ) {
3226 $note =
wfMessage(
'edit_form_incomplete' )->plain();
3228 $note =
wfMessage(
'previewnote' )->plain() .
' ' . $continueEditing;
3231 $parserOptions = $this->mArticle->makeParserOptions( $this->mArticle->getContext() );
3232 $parserOptions->setEditSection(
false );
3233 $parserOptions->setIsPreview(
true );
3234 $parserOptions->setIsSectionPreview( !is_null( $this->
section ) && $this->
section !==
'' );
3236 # don't parse non-wikitext pages, show message about preview
3237 if ( $this->mTitle->isCssJsSubpage() || $this->mTitle->isCssOrJsPage() ) {
3238 if ( $this->mTitle->isCssJsSubpage() ) {
3240 } elseif ( $this->mTitle->isCssOrJsPage() ) {
3254 # Used messages to make sure grep find them:
3255 # Messages: usercsspreview, userjspreview, sitecsspreview, sitejspreview
3256 if ( $level && $format ) {
3257 $note =
"<div id='mw-{$level}{$format}preview'>" .
3258 wfMessage(
"{$level}{$format}preview" )->text() .
3259 ' ' . $continueEditing .
"</div>";
3265 $previewHTML = $this->mArticle->viewRedirect( $rt,
false );
3268 # If we're adding a comment, we need to show the
3269 # summary as the headline
3270 if ( $this->
section ===
"new" && $this->summary !==
"" ) {
3274 $hook_args =
array( $this, &$content );
3276 wfRunHooks(
'EditPageGetPreviewContent', $hook_args );
3278 $parserOptions->enableLimitReport();
3280 # For CSS/JS pages, we should have called the ShowRawCssJs hook here.
3281 # But it's now deprecated, so never mind
3284 $parserOutput = $content->
getParserOutput( $this->getArticle()->getTitle(),
null, $parserOptions );
3286 $previewHTML = $parserOutput->getText();
3287 $this->mParserOutput = $parserOutput;
3288 $wgOut->addParserOutputNoText( $parserOutput );
3290 if ( count( $parserOutput->getWarnings() ) ) {
3291 $note .=
"\n\n" . implode(
"\n\n", $parserOutput->getWarnings() );
3295 $m =
wfMessage(
'content-failed-to-parse', $this->contentModel, $this->contentFormat, $ex->getMessage() );
3296 $note .=
"\n\n" . $m->parse();
3300 if ( $this->isConflict ) {
3301 $conflict =
'<h2 id="mw-previewconflict">' .
wfMessage(
'previewconflict' )->escaped() .
"</h2>\n";
3303 $conflict =
'<hr />';
3306 $previewhead =
"<div class='previewnote'>\n" .
3307 '<h2 id="mw-previewheader">' .
wfMessage(
'preview' )->escaped() .
"</h2>" .
3308 $wgOut->parse( $note,
true,
true ) . $conflict .
"</div>\n";
3310 $pageViewLang = $this->mTitle->getPageViewLanguage();
3311 $attribs =
array(
'lang' => $pageViewLang->getHtmlCode(),
'dir' => $pageViewLang->getDir(),
3312 'class' =>
'mw-content-' . $pageViewLang->getDir() );
3316 return $previewhead . $previewHTML . $this->previewTextAfterContent;
3322 function getTemplates() {
3323 if ( $this->preview || $this->
section !=
'' ) {
3324 $templates =
array();
3325 if ( !isset( $this->mParserOutput ) ) {
3328 foreach ( $this->mParserOutput->getTemplates()
as $ns => $template ) {
3329 foreach ( array_keys( $template )
as $dbk ) {
3335 return $this->mTitle->getTemplateLinksFrom();
3346 static function getEditToolbar() {
3348 global $wgEnableUploads, $wgForeignFileRepos;
3350 $imagesAvailable = $wgEnableUploads || count( $wgForeignFileRepos );
3367 'image' =>
$wgLang->getImageFile(
'button-bold' ),
3368 'id' =>
'mw-editbutton-bold',
3370 'close' =>
'\'\
'\'',
3371 'sample' =>
wfMessage(
'bold_sample' )->text(),
3372 'tip' =>
wfMessage(
'bold_tip' )->text(),
3376 'image' =>
$wgLang->getImageFile(
'button-italic' ),
3377 'id' =>
'mw-editbutton-italic',
3380 'sample' =>
wfMessage(
'italic_sample' )->text(),
3381 'tip' =>
wfMessage(
'italic_tip' )->text(),
3385 'image' =>
$wgLang->getImageFile(
'button-link' ),
3386 'id' =>
'mw-editbutton-link',
3389 'sample' =>
wfMessage(
'link_sample' )->text(),
3390 'tip' =>
wfMessage(
'link_tip' )->text(),
3394 'image' =>
$wgLang->getImageFile(
'button-extlink' ),
3395 'id' =>
'mw-editbutton-extlink',
3398 'sample' =>
wfMessage(
'extlink_sample' )->text(),
3399 'tip' =>
wfMessage(
'extlink_tip' )->text(),
3403 'image' =>
$wgLang->getImageFile(
'button-headline' ),
3404 'id' =>
'mw-editbutton-headline',
3407 'sample' =>
wfMessage(
'headline_sample' )->text(),
3408 'tip' =>
wfMessage(
'headline_tip' )->text(),
3411 $imagesAvailable ?
array(
3412 'image' =>
$wgLang->getImageFile(
'button-image' ),
3413 'id' =>
'mw-editbutton-image',
3416 'sample' =>
wfMessage(
'image_sample' )->text(),
3417 'tip' =>
wfMessage(
'image_tip' )->text(),
3420 $imagesAvailable ?
array(
3421 'image' =>
$wgLang->getImageFile(
'button-media' ),
3422 'id' =>
'mw-editbutton-media',
3425 'sample' =>
wfMessage(
'media_sample' )->text(),
3426 'tip' =>
wfMessage(
'media_tip' )->text(),
3430 'image' =>
$wgLang->getImageFile(
'button-nowiki' ),
3431 'id' =>
'mw-editbutton-nowiki',
3432 'open' =>
"<nowiki>",
3433 'close' =>
"</nowiki>",
3434 'sample' =>
wfMessage(
'nowiki_sample' )->text(),
3435 'tip' =>
wfMessage(
'nowiki_tip' )->text(),
3439 'image' =>
$wgLang->getImageFile(
'button-sig' ),
3440 'id' =>
'mw-editbutton-signature',
3444 'tip' =>
wfMessage(
'sig_tip' )->text(),
3448 'image' =>
$wgLang->getImageFile(
'button-hr' ),
3449 'id' =>
'mw-editbutton-hr',
3450 'open' =>
"\n----\n",
3458 $script =
'mw.loader.using("mediawiki.action.edit", function() {';
3459 foreach ( $toolarray
as $tool ) {
3465 $wgStylePath .
'/common/images/' . $tool[
'image'],
3483 "// Create button bar\n" .
3484 "$(function() { mw.toolbar.init(); } );\n";
3489 $toolbar =
'<div id="toolbar"></div>';
3506 public function getCheckboxes( &
$tabindex, $checked ) {
3509 $checkboxes =
array();
3512 if ( !$this->isNew ) {
3513 $checkboxes[
'minor'] =
'';
3514 $minorLabel =
wfMessage(
'minoredit' )->parse();
3515 if (
$wgUser->isAllowed(
'minoredit' ) ) {
3519 'id' =>
'wpMinoredit',
3521 $checkboxes[
'minor'] =
3523 " <label for='wpMinoredit' id='mw-editpage-minoredit'" .
3525 ">{$minorLabel}</label>";
3529 $watchLabel =
wfMessage(
'watchthis' )->parse();
3530 $checkboxes[
'watch'] =
'';
3531 if (
$wgUser->isLoggedIn() ) {
3535 'id' =>
'wpWatchthis',
3537 $checkboxes[
'watch'] =
3539 " <label for='wpWatchthis' id='mw-editpage-watch'" .
3541 ">{$watchLabel}</label>";
3555 public function getEditButtons( &
$tabindex ) {
3571 'id' =>
'wpPreview',
3572 'name' =>
'wpPreview',
3579 $buttons[
'preview'] =
Xml::element(
'input', $temp,
'' );
3580 $buttons[
'live'] =
'';
3609 function livePreview() {
3612 header(
'Content-type: text/xml; charset=utf-8' );
3613 header(
'Cache-control: no-cache' );
3615 $previewText = $this->getPreviewText();
3616 #$categories = $skin->getCategoryLinks();
3619 '<?xml version="1.0" encoding="UTF-8" ?>' .
"\n" .
3632 function blockedPage() {
3644 function userNotLoggedInPage() {
3655 function noCreatePermission() {
3657 $permission = $this->mTitle->isTalkPage() ?
'createtalk' :
'createpage';
3665 function noSuchSectionPage() {
3674 $wgOut->returnToMain(
false, $this->mTitle );
3682 public function spamPageWithContent( $match =
false ) {
3684 $this->textbox2 = $this->textbox1;
3686 if ( is_array( $match ) ) {
3687 $match =
$wgLang->listToText( $match );
3691 $wgOut->addHTML(
'<div id="spamprotected">' );
3692 $wgOut->addWikiMsg(
'spamprotectiontext' );
3696 $wgOut->addHTML(
'</div>' );
3698 $wgOut->wrapWikiMsg(
'<h2>$1</h2>',
"yourdiff" );
3701 $wgOut->wrapWikiMsg(
'<h2>$1</h2>',
"yourtext" );
3702 $this->showTextbox2();
3704 $wgOut->addReturnTo( $this->getContextTitle(),
array(
'action' =>
'edit' ) );
3713 private function checkUnicodeCompliantBrowser() {
3714 global $wgBrowserBlackList, $wgRequest;
3716 $currentbrowser = $wgRequest->getHeader(
'User-Agent' );
3717 if ( $currentbrowser ===
false ) {
3722 foreach ( $wgBrowserBlackList
as $browser ) {
3723 if ( preg_match( $browser, $currentbrowser ) ) {
3738 protected function safeUnicodeInput( $request, $field ) {
3739 $text = rtrim( $request->getText( $field ) );
3740 return $request->getBool(
'safemode' )
3741 ? $this->unmakeSafe( $text )
3752 protected function safeUnicodeOutput( $text ) {
3755 return $this->checkUnicodeCompliantBrowser()
3757 : $this->makeSafe( $codedText );
3772 private function makeSafe( $invalue ) {
3774 $invalue = strtr( $invalue,
array(
"&#x" =>
"�" ) );
3779 for ( $i = 0; $i < strlen( $invalue ); $i++ ) {
3780 $bytevalue = ord( $invalue[$i] );
3781 if ( $bytevalue <= 0x7F ) {
3784 } elseif ( $bytevalue <= 0xBF ) {
3785 $working = $working << 6;
3786 $working += ( $bytevalue & 0x3F );
3788 if ( $bytesleft <= 0 ) {
3789 $result .=
"&#x" . strtoupper( dechex( $working ) ) .
";";
3791 } elseif ( $bytevalue <= 0xDF ) {
3792 $working = $bytevalue & 0x1F;
3794 } elseif ( $bytevalue <= 0xEF ) {
3795 $working = $bytevalue & 0x0F;
3798 $working = $bytevalue & 0x07;
3813 private function unmakeSafe( $invalue ) {
3815 $valueLength = strlen( $invalue );
3816 for ( $i = 0; $i < $valueLength; $i++ ) {
3817 if ( ( substr( $invalue, $i, 3 ) ==
"&#x" ) && ( $invalue[$i + 3] !=
'0' ) ) {
3821 $hexstring .= $invalue[$i];
3823 }
while ( ctype_xdigit( $invalue[$i] ) && ( $i < strlen( $invalue ) ) );
3828 if ( ( substr( $invalue, $i, 1 ) ==
";" ) and ( strlen( $hexstring ) <= 6 ) ) {
3829 $codepoint = hexdec( $hexstring );
3832 $result .=
"&#x" . $hexstring . substr( $invalue, $i, 1 );
3835 $result .= substr( $invalue, $i, 1 );