X-Git-Url: http://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2FEditPage.php;h=a362dd78e6a105bfb0289bfdfe98f998a10bf740;hb=c815ad86969fd182a7e3f3d0105c6e45c92aa0e4;hp=794072ca73ff513de6c7cfdcdbb076b99d039dbb;hpb=d0f249e16808feda4d6658fad1ea75114481778c;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/EditPage.php b/includes/EditPage.php index 794072ca73..a362dd78e6 100644 --- a/includes/EditPage.php +++ b/includes/EditPage.php @@ -17,35 +17,34 @@ * usually the same, but they are now allowed to be different. */ class EditPage { - const AS_SUCCESS_UPDATE = 200; - const AS_SUCCESS_NEW_ARTICLE = 201; - const AS_HOOK_ERROR = 210; - const AS_FILTERING = 211; - const AS_HOOK_ERROR_EXPECTED = 212; - const AS_BLOCKED_PAGE_FOR_USER = 215; - const AS_CONTENT_TOO_BIG = 216; - const AS_USER_CANNOT_EDIT = 217; - const AS_READ_ONLY_PAGE_ANON = 218; - const AS_READ_ONLY_PAGE_LOGGED = 219; - const AS_READ_ONLY_PAGE = 220; - const AS_RATE_LIMITED = 221; - const AS_ARTICLE_WAS_DELETED = 222; - const AS_NO_CREATE_PERMISSION = 223; - const AS_BLANK_ARTICLE = 224; - const AS_CONFLICT_DETECTED = 225; - const AS_SUMMARY_NEEDED = 226; - const AS_TEXTBOX_EMPTY = 228; - const AS_MAX_ARTICLE_SIZE_EXCEEDED = 229; - const AS_OK = 230; - const AS_END = 231; - const AS_SPAM_ERROR = 232; - const AS_IMAGE_REDIRECT_ANON = 233; - const AS_IMAGE_REDIRECT_LOGGED = 234; + const AS_SUCCESS_UPDATE = 200; + const AS_SUCCESS_NEW_ARTICLE = 201; + const AS_HOOK_ERROR = 210; + const AS_FILTERING = 211; + const AS_HOOK_ERROR_EXPECTED = 212; + const AS_BLOCKED_PAGE_FOR_USER = 215; + const AS_CONTENT_TOO_BIG = 216; + const AS_USER_CANNOT_EDIT = 217; + const AS_READ_ONLY_PAGE_ANON = 218; + const AS_READ_ONLY_PAGE_LOGGED = 219; + const AS_READ_ONLY_PAGE = 220; + const AS_RATE_LIMITED = 221; + const AS_ARTICLE_WAS_DELETED = 222; + const AS_NO_CREATE_PERMISSION = 223; + const AS_BLANK_ARTICLE = 224; + const AS_CONFLICT_DETECTED = 225; + const AS_SUMMARY_NEEDED = 226; + const AS_TEXTBOX_EMPTY = 228; + const AS_MAX_ARTICLE_SIZE_EXCEEDED = 229; + const AS_OK = 230; + const AS_END = 231; + const AS_SPAM_ERROR = 232; + const AS_IMAGE_REDIRECT_ANON = 233; + const AS_IMAGE_REDIRECT_LOGGED = 234; var $mArticle; var $mTitle; var $action; - var $mMetaData = ''; var $isConflict = false; var $isCssJsSubpage = false; var $isCssSubpage = false; @@ -67,13 +66,14 @@ class EditPage { #var $mPreviewTemplates; var $mParserOutput; var $mBaseRevision = false; + var $mShowSummaryField = true; # Form values var $save = false, $preview = false, $diff = false; var $minoredit = false, $watchthis = false, $recreate = false; - var $textbox1 = '', $textbox2 = '', $summary = ''; + var $textbox1 = '', $textbox2 = '', $summary = '', $nosummary = false; var $edittime = '', $section = '', $starttime = ''; - var $oldid = 0, $editintro = '', $scrolltop = null; + var $oldid = 0, $editintro = '', $scrolltop = null, $bot = true; # Placeholders for text injection by hooks (must be HTML) # extensions should take care to _append_ to the present value @@ -83,6 +83,8 @@ class EditPage { public $editFormTextAfterWarn; public $editFormTextAfterTools; public $editFormTextBottom; + public $editFormTextAfterContent; + public $previewTextAfterContent; /* $didSave should be set to true whenever an article was succesfully altered. */ public $didSave = false; @@ -106,9 +108,11 @@ class EditPage { $this->editFormTextAfterWarn = $this->editFormTextAfterTools = $this->editFormTextBottom = + $this->editFormTextAfterContent = + $this->previewTextAfterContent = $this->mPreloadText = ""; } - + function getArticle() { return $this->mArticle; } @@ -116,6 +120,7 @@ class EditPage { /** * Fetch initial editing page content. + * @returns mixed string on success, $def_text for invalid sections * @private */ function getContent( $def_text = '' ) { @@ -124,7 +129,10 @@ class EditPage { wfProfileIn( __METHOD__ ); # Get variables from query string :P $section = $wgRequest->getVal( 'section' ); - $preload = $wgRequest->getVal( 'preload' ); + + $preload = $wgRequest->getVal( 'preload', + // Custom preload text for new sections + $section === 'new' ? 'MediaWiki:addsection-preload' : '' ); $undoafter = $wgRequest->getVal( 'undoafter' ); $undo = $wgRequest->getVal( 'undo' ); @@ -135,10 +143,9 @@ class EditPage { if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) { # If this is a system message, get the default text. list( $message, $lang ) = $wgMessageCache->figureMessage( $wgContLang->lcfirst( $this->mTitle->getText() ) ); - $wgMessageCache->loadAllMessages( $lang ); $text = wfMsgGetKey( $message, false, $lang, false ); if( wfEmptyMsg( $message, $text ) ) - $text = ''; + $text = $this->getPreloadedText( $preload ); } else { # If requested, preload some text. $text = $this->getPreloadedText( $preload ); @@ -154,10 +161,10 @@ class EditPage { # Undoing a specific edit overrides section editing; section-editing # doesn't work with undoing. if ( $undoafter ) { - $undorev = Revision::newFromId($undo); - $oldrev = Revision::newFromId($undoafter); + $undorev = Revision::newFromId( $undo ); + $oldrev = Revision::newFromId( $undoafter ); } else { - $undorev = Revision::newFromId($undo); + $undorev = Revision::newFromId( $undo ); $oldrev = $undorev ? $undorev->getPrevious() : null; } @@ -169,7 +176,7 @@ class EditPage { $undorev->getPage() == $this->mArticle->getID() && !$undorev->isDeleted( Revision::DELETED_TEXT ) && !$oldrev->isDeleted( Revision::DELETED_TEXT ) ) { - + $undotext = $this->mArticle->getUndoText( $undorev, $oldrev ); if ( $undotext === false ) { # Warn the user that something went wrong @@ -196,6 +203,7 @@ class EditPage { if ( $section == 'new' ) { $text = $this->getPreloadedText( $preload ); } else { + // Get section edit text (returns $def_text for invalid sections) $text = $wgParser->getSection( $text, $section, $def_text ); } } @@ -204,129 +212,49 @@ class EditPage { wfProfileOut( __METHOD__ ); return $text; } - + /** Use this method before edit() to preload some text into the edit box */ public function setPreloadedText( $text ) { $this->mPreloadText = $text; } /** - * Get the contents of a page from its title and remove includeonly tags + * Get the contents to be preloaded into the box, either set by + * an earlier setPreloadText() or by loading the given page. * - * @param $preload String: the title of the page. - * @return string The contents of the page. + * @param $preload String: representing the title to preload from. + * @return String */ protected function getPreloadedText( $preload ) { - if ( !empty($this->mPreloadText) ) { + global $wgUser, $wgParser; + if ( !empty( $this->mPreloadText ) ) { return $this->mPreloadText; - } elseif ( $preload === '' ) { - return ''; - } else { - $preloadTitle = Title::newFromText( $preload ); - if ( isset( $preloadTitle ) && $preloadTitle->userCanRead() ) { - $rev = Revision::newFromTitle($preloadTitle); - if ( is_object( $rev ) ) { - $text = $rev->getText(); - // TODO FIXME: AAAAAAAAAAA, this shouldn't be implementing - // its own mini-parser! -ævar - $text = preg_replace( '~~', '', $text ); - return $text; - } else - return ''; - } - } - } - - /** - * This is the function that extracts metadata from the article body on the first view. - * To turn the feature on, set $wgUseMetadataEdit = true ; in LocalSettings - * and set $wgMetadataWhitelist to the *full* title of the template whitelist - */ - function extractMetaDataFromArticle () { - global $wgUseMetadataEdit, $wgMetadataWhitelist, $wgContLang; - $this->mMetaData = ''; - if ( !$wgUseMetadataEdit ) return; - if ( $wgMetadataWhitelist == '' ) return; - $s = ''; - $t = $this->getContent(); - - # MISSING : filtering - - # Categories and language links - $t = explode ( "\n" , $t ); - $catlow = strtolower ( $wgContLang->getNsText( NS_CATEGORY ) ); - $cat = $ll = array(); - foreach ( $t AS $key => $x ) { - $y = trim ( strtolower ( $x ) ); - while ( substr ( $y , 0 , 2 ) == '[[' ) { - $y = explode ( ']]' , trim ( $x ) ); - $first = array_shift ( $y ); - $first = explode ( ':' , $first ); - $ns = array_shift ( $first ); - $ns = trim ( str_replace ( '[' , '' , $ns ) ); - if ( $wgContLang->getLanguageName( $ns ) || strtolower ( $ns ) == $catlow ) { - $add = '[[' . $ns . ':' . implode ( ':' , $first ) . ']]'; - if ( strtolower ( $ns ) == $catlow ) $cat[] = $add; - else $ll[] = $add; - $x = implode ( ']]' , $y ); - $t[$key] = $x; - $y = trim ( strtolower ( $x ) ); - } else { - $x = implode ( ']]' , $y ); - $y = trim ( strtolower ( $x ) ); - } - } - } - if ( count ( $cat ) ) $s .= implode ( ' ' , $cat ) . "\n"; - if ( count ( $ll ) ) $s .= implode ( ' ' , $ll ) . "\n"; - $t = implode ( "\n" , $t ); - - # Load whitelist - $sat = array () ; # stand-alone-templates; must be lowercase - $wl_title = Title::newFromText ( $wgMetadataWhitelist ); - $wl_article = new Article ( $wl_title ); - $wl = explode ( "\n" , $wl_article->getContent() ); - foreach ( $wl AS $x ) { - $isentry = false; - $x = trim ( $x ); - while ( substr ( $x , 0 , 1 ) == '*' ) { - $isentry = true; - $x = trim ( substr ( $x , 1 ) ); - } - if ( $isentry ) { - $sat[] = strtolower ( $x ); - } - - } - - # Templates, but only some - $t = explode ( '{{' , $t ); - $tl = array () ; - foreach ( $t AS $key => $x ) { - $y = explode ( '}}' , $x , 2 ); - if ( count ( $y ) == 2 ) { - $z = $y[0]; - $z = explode ( '|' , $z ); - $tn = array_shift ( $z ); - if ( in_array ( strtolower ( $tn ) , $sat ) ) { - $tl[] = '{{' . $y[0] . '}}'; - $t[$key] = $y[1]; - $y = explode ( '}}' , $y[1] , 2 ); + } elseif ( $preload !== '' ) { + $title = Title::newFromText( $preload ); + # Check for existence to avoid getting MediaWiki:Noarticletext + if ( isset( $title ) && $title->exists() && $title->userCanRead() ) { + $article = new Article( $title ); + + if ( $article->isRedirect() ) { + $title = Title::newFromRedirectRecurse( $article->getContent() ); + # Redirects to missing titles are displayed, to hidden pages are followed + # Copying observed behaviour from ?action=view + if ( $title->exists() ) { + if ($title->userCanRead() ) { + $article = new Article( $title ); + } else { + return ""; + } + } } - else $t[$key] = '{{' . $x; + $parserOptions = ParserOptions::newFromUser( $wgUser ); + return $wgParser->getPreloadText( $article->getContent(), $title, $parserOptions ); } - else if ( $key != 0 ) $t[$key] = '{{' . $x; - else $t[$key] = $x; } - if ( count ( $tl ) ) $s .= implode ( ' ' , $tl ); - $t = implode ( '' , $t ); - - $t = str_replace ( "\n\n\n" , "\n" , $t ); - $this->mArticle->mContent = $t; - $this->mMetaData = $s; + return ''; } - /* + /* * Check if a page was deleted while the user was editing it, before submit. * Note that we rely on the logging table, which hasn't been always there, * but that doesn't matter, because this only applies to brand new @@ -363,7 +291,7 @@ class EditPage { * the newly-edited page. */ function edit() { - global $wgOut, $wgRequest, $wgEnableJS2system; + global $wgOut, $wgRequest, $wgUser; // Allow extensions to modify/prevent this form or submission if ( !wfRunHooks( 'AlternateEdit', array( $this ) ) ) { return; @@ -392,13 +320,17 @@ class EditPage { $wgOut->addScriptFile( 'edit.js' ); - if( $wgEnableJS2system ) - $wgOut->addScriptClass( 'editPage' ); + if ( $wgUser->getOption( 'uselivepreview', false ) ) { + $wgOut->includeJQuery(); + $wgOut->addScriptFile( 'preview.js' ); + } + // Bug #19334: textarea jumps when editing articles in IE8 + $wgOut->addStyle( 'common/IE80Fixes.css', 'screen', 'IE 8' ); $permErrors = $this->getEditPermissionErrors(); if ( $permErrors ) { - wfDebug( __METHOD__.": User can't edit\n" ); - $this->readOnlyPage( $this->getContent(), true, $permErrors, 'edit' ); + wfDebug( __METHOD__ . ": User can't edit\n" ); + $this->readOnlyPage( $this->getContent( false ), true, $permErrors, 'edit' ); wfProfileOut( __METHOD__ ); return; } else { @@ -413,16 +345,15 @@ class EditPage { if ( $this->previewOnOpen() ) { $this->formtype = 'preview'; } else { - $this->extractMetaDataFromArticle () ; $this->formtype = 'initial'; } } } - # Evaluate if the edit interface should be shown or - # if the page should be shown, in case redlink=true - if ( $wgRequest->getBool( 'redlink' ) ) - $this->showPageOnRedlink(); + // If they used redlink=1 and the page exists, redirect to the main article + if ( $wgRequest->getBool( 'redlink' ) && $this->mTitle->exists() ) { + $wgOut->redirect( $this->mTitle->getFullURL() ); + } wfProfileIn( __METHOD__."-business-end" ); @@ -473,7 +404,7 @@ class EditPage { # First time through: get contents, set time for conflict # checking, etc. if ( 'initial' == $this->formtype || $this->firsttime ) { - if ( $this->initialiseForm() === false) { + if ( $this->initialiseForm() === false ) { $this->noSuchSectionPage(); wfProfileOut( __METHOD__."-business-end" ); wfProfileOut( __METHOD__ ); @@ -481,6 +412,8 @@ class EditPage { } if ( !$this->mTitle->getArticleId() ) wfRunHooks( 'EditFormPreloadText', array( &$this->textbox1, &$this->mTitle ) ); + else + wfRunHooks( 'EditFormInitialText', array( $this ) ); } $this->showEditForm(); @@ -488,44 +421,6 @@ class EditPage { wfProfileOut( __METHOD__ ); } - /* - * Evaluate if the edit interface should be shown or the page, in case redlink=true. - * If the page exists, it is always shown. If it doesn't, it depends on the settings - * of $wgShowPageOnRedlink (see DefaultSettings for documentation). - */ - protected function showPageOnRedlink() { - global $wgShowPageOnRedlink, $wgUser, $wgRequest, $wgOut; - $redirectToPage = false; - # If the page exists (it has been created after the link has been emerged), - # redirect to the page instead of editing the current page - if ( $this->mTitle->exists() ) - $wgOut->redirect( $this->mTitle->getFullURL() ); - # Check site configuration ($wgShowPageOnRedlink) - if ( is_array( $wgShowPageOnRedlink ) ) { - $ns = $this->mTitle->getNamespace(); - $groups = $wgUser->getEffectiveGroups(); - # Gets overwritten if user is member of a group that has been specified: - $redirectToPage = true; - foreach ( $groups as $i => $group ) { - # Test if there is a rule for a specific usergroup and a specific namespace - if ( isset( $wgShowPageOnRedlink[$group][$ns] ) && $wgShowPageOnRedlink[$group][$ns] == false ) { - $redirectToPage = false; - } - # Test if there is a rule for a specific usergroup in all namespaces - elseif ( isset( $wgShowPageOnRedlink[$group] ) && !is_array( $wgShowPageOnRedlink[$group] ) - && $wgShowPageOnRedlink[$group] == false ) { - $redirectToPage = false; - } - } - } - else { - $redirectToPage = $wgShowPageOnRedlink; - } - if ( $redirectToPage ) { - $wgOut->redirect( $this->mTitle->getFullURL() ); - } - } - protected function getEditPermissionErrors() { global $wgUser; $permErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser ); @@ -537,8 +432,8 @@ class EditPage { # Ignore some permissions errors when a user is just previewing/viewing diffs $remove = array(); foreach( $permErrors as $error ) { - if ( ($this->preview || $this->diff) && - ($error[0] == 'blockedtext' || $error[0] == 'autoblockedtext') ) + if ( ( $this->preview || $this->diff ) && + ( $error[0] == 'blockedtext' || $error[0] == 'autoblockedtext' ) ) { $remove[] = $error; } @@ -570,7 +465,7 @@ class EditPage { * @return bool */ protected function previewOnOpen() { - global $wgRequest, $wgUser; + global $wgRequest, $wgUser, $wgPreviewOnOpenNamespaces; if ( $wgRequest->getVal( 'preview' ) == 'yes' ) { // Explicit override from request return true; @@ -583,7 +478,10 @@ class EditPage { } elseif ( ( $wgRequest->getVal( 'preload' ) !== null || $this->mTitle->exists() ) && $wgUser->getOption( 'previewonfirst' ) ) { // Standard preference behaviour return true; - } elseif ( !$this->mTitle->exists() && $this->mTitle->getNamespace() == NS_CATEGORY ) { + } elseif ( !$this->mTitle->exists() && + isset($wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()]) && + $wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()] ) + { // Categories are special return true; } else { @@ -591,14 +489,37 @@ class EditPage { } } + /** + * Does this EditPage class support section editing? + * This is used by EditPage subclasses to indicate their ui cannot handle section edits + * + * @return bool + */ + protected function isSectionEditSupported() { + return true; + } + + /** + * Returns the URL to use in the form's action attribute. + * This is used by EditPage subclasses when simply customizing the action + * variable in the constructor is not enough. This can be used when the + * EditPage lives inside of a Special page rather than a custom page action. + * + * @param $title Title object for which is being edited (where we go to for &action= links) + * @return string + */ + protected function getActionURL( Title $title ) { + return $title->getLocalURL( array( 'action' => $this->action ) ); + } + /** * @todo document * @param $request */ function importFormData( &$request ) { global $wgLang, $wgUser; - $fname = 'EditPage::importFormData'; - wfProfileIn( $fname ); + + wfProfileIn( __METHOD__ ); # Section edit can come from either the form or a link $this->section = $request->getVal( 'wpSection', $request->getVal( 'section' ) ); @@ -608,8 +529,17 @@ class EditPage { # Also remove trailing whitespace, but don't remove _initial_ # whitespace from the text boxes. This may be significant formatting. $this->textbox1 = $this->safeUnicodeInput( $request, 'wpTextbox1' ); - $this->textbox2 = $this->safeUnicodeInput( $request, 'wpTextbox2' ); - $this->mMetaData = rtrim( $request->getText( 'metadata' ) ); + if ( !$request->getCheck('wpTextbox2') ) { + // Skip this if wpTextbox2 has input, it indicates that we came + // from a conflict page with raw page text, not a custom form + // modified by subclasses + wfProfileIn( get_class($this)."::importContentFormData" ); + $textbox1 = $this->importContentFormData( $request ); + if ( isset($textbox1) ) + $this->textbox1 = $textbox1; + wfProfileOut( get_class($this)."::importContentFormData" ); + } + # Truncate for whole multibyte characters. +5 bytes for ellipsis $this->summary = $wgLang->truncate( $request->getText( 'wpSummary' ), 250, '' ); @@ -623,7 +553,7 @@ class EditPage { if ( is_null( $this->edittime ) ) { # If the form is incomplete, force to preview. - wfDebug( "$fname: Form data appears to be incomplete\n" ); + wfDebug( __METHOD__ . ": Form data appears to be incomplete\n" ); wfDebug( "POST DATA: " . var_export( $_POST, true ) . "\n" ); $this->preview = true; } else { @@ -640,23 +570,23 @@ class EditPage { # if the user hits enter in the comment box. # The unmarked state will be assumed to be a save, # if the form seems otherwise complete. - wfDebug( "$fname: Passed token check.\n" ); + wfDebug( __METHOD__ . ": Passed token check.\n" ); } else if ( $this->diff ) { # Failed token check, but only requested "Show Changes". - wfDebug( "$fname: Failed token check; Show Changes requested.\n" ); + wfDebug( __METHOD__ . ": Failed token check; Show Changes requested.\n" ); } else { # Page might be a hack attempt posted from # an external site. Preview instead of saving. - wfDebug( "$fname: Failed token check; forcing preview\n" ); + wfDebug( __METHOD__ . ": Failed token check; forcing preview\n" ); $this->preview = true; } } $this->save = !$this->preview && !$this->diff; - if ( !preg_match( '/^\d{14}$/', $this->edittime )) { + if ( !preg_match( '/^\d{14}$/', $this->edittime ) ) { $this->edittime = null; } - if ( !preg_match( '/^\d{14}$/', $this->starttime )) { + if ( !preg_match( '/^\d{14}$/', $this->starttime ) ) { $this->starttime = null; } @@ -666,8 +596,8 @@ class EditPage { $this->watchthis = $request->getCheck( 'wpWatchthis' ); # Don't force edit summaries when a user is editing their own user or talk page - if ( ( $this->mTitle->mNamespace == NS_USER || $this->mTitle->mNamespace == NS_USER_TALK ) && - $this->mTitle->getText() == $wgUser->getName() ) + if ( ( $this->mTitle->mNamespace == NS_USER || $this->mTitle->mNamespace == NS_USER_TALK ) && + $this->mTitle->getText() == $wgUser->getName() ) { $this->allowBlankSummary = true; } else { @@ -677,10 +607,8 @@ class EditPage { $this->autoSumm = $request->getText( 'wpAutoSummary' ); } else { # Not a posted form? Start with nothing. - wfDebug( "$fname: Not a posted form.\n" ); + wfDebug( __METHOD__ . ": Not a posted form.\n" ); $this->textbox1 = ''; - $this->textbox2 = ''; - $this->mMetaData = ''; $this->summary = ''; $this->edittime = ''; $this->starttime = wfTimestampNow(); @@ -689,7 +617,7 @@ class EditPage { $this->save = false; $this->diff = false; $this->minoredit = false; - $this->watchthis = false; + $this->watchthis = $request->getBool( 'watchthis', false ); // Watch may be overriden by request parameters $this->recreate = false; if ( $this->section == 'new' && $request->getVal( 'preloadtitle' ) ) { @@ -698,24 +626,41 @@ class EditPage { elseif ( $this->section != 'new' && $request->getVal( 'summary' ) ) { $this->summary = $request->getText( 'summary' ); } - + if ( $request->getVal( 'minor' ) ) { $this->minoredit = true; } } + $this->bot = $request->getBool( 'bot', true ); + $this->nosummary = $request->getBool( 'nosummary' ); + // FIXME: unused variable? $this->oldid = $request->getInt( 'oldid' ); $this->live = $request->getCheck( 'live' ); - $this->editintro = $request->getText( 'editintro' ); + $this->editintro = $request->getText( 'editintro', + // Custom edit intro for new sections + $this->section === 'new' ? 'MediaWiki:addsection-editintro' : '' ); - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); // Allow extensions to modify form data wfRunHooks( 'EditPage::importFormData', array( $this, $request ) ); } + /** + * Subpage overridable method for extracting the page content data from the + * posted form to be placed in $this->textbox1, if using customized input + * this method should be overrided and return the page text that will be used + * for saving, preview parsing and so on... + * + * @param $request WebRequest + */ + protected function importContentFormData( &$request ) { + return; // Don't do anything, EditPage already extracted wpTextbox1 + } + /** * Make sure the form isn't faking a user's credentials. * @@ -744,35 +689,51 @@ class EditPage { if ( $namespace == NS_MEDIAWIKI ) { # Show a warning if editing an interface message - $wgOut->wrapWikiMsg( "
\n$1
", 'editinginterface' ); + $wgOut->wrapWikiMsg( "
\n$1\n
", 'editinginterface' ); } - # Show a warning message when someone creates/edits a user (talk) page but the user does not exists + # Show a warning message when someone creates/edits a user (talk) page but the user does not exist + # Show log extract when the user is currently blocked if ( $namespace == NS_USER || $namespace == NS_USER_TALK ) { $parts = explode( '/', $this->mTitle->getText(), 2 ); $username = $parts[0]; - $id = User::idFromName( $username ); + $user = User::newFromName( $username, false /* allow IP users*/ ); $ip = User::isIP( $username ); - if ( $id == 0 && !$ip ) { - $wgOut->wrapWikiMsg( '
$1
', + if ( !$user->isLoggedIn() && !$ip ) { # User does not exist + $wgOut->wrapWikiMsg( "
\n$1\n
", array( 'userpage-userdoesnotexist', $username ) ); + } else if ( $user->isBlocked() ) { # Show log extract if the user is currently blocked + LogEventsList::showLogExtract( + $wgOut, + 'block', + $user->getUserPage()->getPrefixedText(), + '', + array( + 'lim' => 1, + 'showIfEmpty' => false, + 'msgKey' => array( + 'blocked-notice-logextract', + $user->getName() # Support GENDER in notice + ) + ) + ); } } # Try to add a custom edit intro, or use the standard one if this is not possible. if ( !$this->showCustomIntro() && !$this->mTitle->exists() ) { if ( $wgUser->isLoggedIn() ) { - $wgOut->wrapWikiMsg( '
$1
', 'newarticletext' ); + $wgOut->wrapWikiMsg( "
\n$1\n
", 'newarticletext' ); } else { - $wgOut->wrapWikiMsg( '
$1
', 'newarticletextanon' ); + $wgOut->wrapWikiMsg( "
\n$1\n
", 'newarticletextanon' ); } } # Give a notice if the user is editing a deleted/moved page... if ( !$this->mTitle->exists() ) { - LogEventsList::showLogExtract( $wgOut, array( 'delete', 'move' ), $this->mTitle->getPrefixedText(), - '', array( 'lim' => 10, - 'conds' => array( "log_action != 'revision'" ), - 'showIfEmpty' => false, - 'msgKey' => array( 'recreate-moveddeleted-warn') ) + LogEventsList::showLogExtract( $wgOut, array( 'delete', 'move' ), $this->mTitle->getPrefixedText(), + '', array( 'lim' => 10, + 'conds' => array( "log_action != 'revision'" ), + 'showIfEmpty' => false, + 'msgKey' => array( 'recreate-moveddeleted-warn') ) ); } } @@ -803,15 +764,13 @@ class EditPage { * @return one of the constants describing the result */ function internalAttemptSave( &$result, $bot = false ) { - global $wgFilterCallback, $wgUser, $wgOut, $wgParser; + global $wgFilterCallback, $wgUser, $wgParser; global $wgMaxArticleSize; - $fname = 'EditPage::attemptSave'; - wfProfileIn( $fname ); - wfProfileIn( "$fname-checks" ); + wfProfileIn( __METHOD__ ); + wfProfileIn( __METHOD__ . '-checks' ); - if ( !wfRunHooks( 'EditPage::attemptSave', array( $this ) ) ) - { + if ( !wfRunHooks( 'EditPage::attemptSave', array( $this ) ) ) { wfDebug( "Hook 'EditPage::attemptSave' aborted article saving\n" ); return self::AS_HOOK_ERROR; } @@ -827,10 +786,6 @@ class EditPage { } } - # Reintegrate metadata - if ( $this->mMetaData != '' ) $this->textbox1 .= "\n" . $this->mMetaData ; - $this->mMetaData = '' ; - # Check for spam $match = self::matchSummarySpamRegex( $this->summary ); if ( $match === false ) { @@ -842,108 +797,107 @@ class EditPage { $pdbk = $this->mTitle->getPrefixedDBkey(); $match = str_replace( "\n", '', $match ); wfDebugLog( 'SpamRegex', "$ip spam regex hit [[$pdbk]]: \"$match\"" ); - wfProfileOut( "$fname-checks" ); - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ . '-checks' ); + wfProfileOut( __METHOD__ ); return self::AS_SPAM_ERROR; } if ( $wgFilterCallback && $wgFilterCallback( $this->mTitle, $this->textbox1, $this->section, $this->hookError, $this->summary ) ) { # Error messages or other handling should be performed by the filter function - wfProfileOut( "$fname-checks" ); - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ . '-checks' ); + wfProfileOut( __METHOD__ ); return self::AS_FILTERING; } if ( !wfRunHooks( 'EditFilter', array( $this, $this->textbox1, $this->section, &$this->hookError, $this->summary ) ) ) { # Error messages etc. could be handled within the hook... - wfProfileOut( "$fname-checks" ); - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ . '-checks' ); + wfProfileOut( __METHOD__ ); return self::AS_HOOK_ERROR; } elseif ( $this->hookError != '' ) { # ...or the hook could be expecting us to produce an error - wfProfileOut( "$fname-checks" ); - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ . '-checks' ); + wfProfileOut( __METHOD__ ); return self::AS_HOOK_ERROR_EXPECTED; } if ( $wgUser->isBlockedFrom( $this->mTitle, false ) ) { # Check block state against master, thus 'false'. - wfProfileOut( "$fname-checks" ); - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ . '-checks' ); + wfProfileOut( __METHOD__ ); return self::AS_BLOCKED_PAGE_FOR_USER; } - $this->kblength = (int)(strlen( $this->textbox1 ) / 1024); + $this->kblength = (int)( strlen( $this->textbox1 ) / 1024 ); if ( $this->kblength > $wgMaxArticleSize ) { // Error will be displayed by showEditForm() $this->tooBig = true; - wfProfileOut( "$fname-checks" ); - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ . '-checks' ); + wfProfileOut( __METHOD__ ); return self::AS_CONTENT_TOO_BIG; } - if ( !$wgUser->isAllowed('edit') ) { + if ( !$wgUser->isAllowed( 'edit' ) ) { if ( $wgUser->isAnon() ) { - wfProfileOut( "$fname-checks" ); - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ . '-checks' ); + wfProfileOut( __METHOD__ ); return self::AS_READ_ONLY_PAGE_ANON; - } - else { - wfProfileOut( "$fname-checks" ); - wfProfileOut( $fname ); + } else { + wfProfileOut( __METHOD__ . '-checks' ); + wfProfileOut( __METHOD__ ); return self::AS_READ_ONLY_PAGE_LOGGED; } } if ( wfReadOnly() ) { - wfProfileOut( "$fname-checks" ); - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ . '-checks' ); + wfProfileOut( __METHOD__ ); return self::AS_READ_ONLY_PAGE; } if ( $wgUser->pingLimiter() ) { - wfProfileOut( "$fname-checks" ); - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ . '-checks' ); + wfProfileOut( __METHOD__ ); return self::AS_RATE_LIMITED; } # If the article has been deleted while editing, don't save it without # confirmation if ( $this->wasDeletedSinceLastEdit() && !$this->recreate ) { - wfProfileOut( "$fname-checks" ); - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ . '-checks' ); + wfProfileOut( __METHOD__ ); return self::AS_ARTICLE_WAS_DELETED; } - wfProfileOut( "$fname-checks" ); + wfProfileOut( __METHOD__ . '-checks' ); # If article is new, insert it. $aid = $this->mTitle->getArticleID( GAID_FOR_UPDATE ); if ( 0 == $aid ) { // Late check for create permission, just in case *PARANOIA* if ( !$this->mTitle->userCan( 'create' ) ) { - wfDebug( "$fname: no create permission\n" ); - wfProfileOut( $fname ); + wfDebug( __METHOD__ . ": no create permission\n" ); + wfProfileOut( __METHOD__ ); return self::AS_NO_CREATE_PERMISSION; } # Don't save a new article if it's blank. - if ( '' == $this->textbox1 ) { - wfProfileOut( $fname ); + if ( $this->textbox1 == '' ) { + wfProfileOut( __METHOD__ ); return self::AS_BLANK_ARTICLE; } // Run post-section-merge edit filter if ( !wfRunHooks( 'EditFilterMerged', array( $this, $this->textbox1, &$this->hookError, $this->summary ) ) ) { # Error messages etc. could be handled within the hook... - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return self::AS_HOOK_ERROR; } elseif ( $this->hookError != '' ) { # ...or the hook could be expecting us to produce an error - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return self::AS_HOOK_ERROR_EXPECTED; } - + # Handle the user preference to force summaries here. Check if it's not a redirect. if ( !$this->allowBlankSummary && !Title::newFromRedirect( $this->textbox1 ) ) { if ( md5( $this->summary ) == $this->autoSumm ) { $this->missingSummary = true; - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return self::AS_SUMMARY_NEEDED; } } @@ -953,7 +907,7 @@ class EditPage { $this->mArticle->insertNewArticle( $this->textbox1, $this->summary, $this->minoredit, $this->watchthis, false, $isComment, $bot ); - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return self::AS_SUCCESS_NEW_ARTICLE; } @@ -962,7 +916,7 @@ class EditPage { $this->mArticle->clear(); # Force reload of dates, etc. $this->mArticle->forUpdate( true ); # Lock the article - wfDebug("timestamp: {$this->mArticle->getTimestamp()}, edittime: {$this->edittime}\n"); + wfDebug( "timestamp: {$this->mArticle->getTimestamp()}, edittime: {$this->edittime}\n" ); if ( $this->mArticle->getTimestamp() != $this->edittime ) { $this->isConflict = true; @@ -972,32 +926,32 @@ class EditPage { // Probably a duplicate submission of a new comment. // This can happen when squid resends a request after // a timeout but the first one actually went through. - wfDebug( "EditPage::editForm duplicate new section submission; trigger edit conflict!\n" ); + wfDebug( __METHOD__ . ": duplicate new section submission; trigger edit conflict!\n" ); } else { // New comment; suppress conflict. $this->isConflict = false; - wfDebug( "EditPage::editForm conflict suppressed; new section\n" ); + wfDebug( __METHOD__ .": conflict suppressed; new section\n" ); } } } $userid = $wgUser->getId(); - + # Suppress edit conflict with self, except for section edits where merging is required. - if ( $this->isConflict && $this->section == '' && $this->userWasLastToEdit($userid,$this->edittime) ) { - wfDebug( "EditPage::editForm Suppressing edit conflict, same user.\n" ); + if ( $this->isConflict && $this->section == '' && $this->userWasLastToEdit( $userid, $this->edittime ) ) { + wfDebug( __METHOD__ . ": Suppressing edit conflict, same user.\n" ); $this->isConflict = false; } if ( $this->isConflict ) { - wfDebug( "EditPage::editForm conflict! getting section '$this->section' for time '$this->edittime' (article time '" . + wfDebug( __METHOD__ . ": conflict! getting section '$this->section' for time '$this->edittime' (article time '" . $this->mArticle->getTimestamp() . "')\n" ); $text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $this->summary, $this->edittime ); } else { - wfDebug( "EditPage::editForm getting section '$this->section'\n" ); + wfDebug( __METHOD__ . ": getting section '$this->section'\n" ); $text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $this->summary ); } if ( is_null( $text ) ) { - wfDebug( "EditPage::editForm activating conflict; section replace failed.\n" ); + wfDebug( __METHOD__ . ": activating conflict; section replace failed.\n" ); $this->isConflict = true; $text = $this->textbox1; // do not try to merge here! } else if ( $this->isConflict ) { @@ -1005,16 +959,16 @@ class EditPage { if ( $this->mergeChangesInto( $text ) ) { // Successful merge! Maybe we should tell the user the good news? $this->isConflict = false; - wfDebug( "EditPage::editForm Suppressing edit conflict, successful merge.\n" ); + wfDebug( __METHOD__ . ": Suppressing edit conflict, successful merge.\n" ); } else { $this->section = ''; $this->textbox1 = $text; - wfDebug( "EditPage::editForm Keeping edit conflict, failed merge.\n" ); + wfDebug( __METHOD__ . ": Keeping edit conflict, failed merge.\n" ); } } if ( $this->isConflict ) { - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return self::AS_CONFLICT_DETECTED; } @@ -1023,44 +977,46 @@ class EditPage { // Run post-section-merge edit filter if ( !wfRunHooks( 'EditFilterMerged', array( $this, $text, &$this->hookError, $this->summary ) ) ) { # Error messages etc. could be handled within the hook... - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return self::AS_HOOK_ERROR; } elseif ( $this->hookError != '' ) { # ...or the hook could be expecting us to produce an error - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return self::AS_HOOK_ERROR_EXPECTED; } # Handle the user preference to force summaries here, but not for null edits - if ( $this->section != 'new' && !$this->allowBlankSummary && 0 != strcmp($oldtext,$text) + if ( $this->section != 'new' && !$this->allowBlankSummary && 0 != strcmp( $oldtext, $text ) && !Title::newFromRedirect( $text ) ) # check if it's not a redirect { if ( md5( $this->summary ) == $this->autoSumm ) { $this->missingSummary = true; - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return self::AS_SUMMARY_NEEDED; } } # And a similar thing for new sections if ( $this->section == 'new' && !$this->allowBlankSummary ) { - if (trim($this->summary) == '') { + if ( trim( $this->summary ) == '' ) { $this->missingSummary = true; - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return self::AS_SUMMARY_NEEDED; } } # All's well - wfProfileIn( "$fname-sectionanchor" ); + wfProfileIn( __METHOD__ . '-sectionanchor' ); $sectionanchor = ''; if ( $this->section == 'new' ) { if ( $this->textbox1 == '' ) { $this->missingComment = true; + wfProfileOut( __METHOD__ . '-sectionanchor' ); + wfProfileOut( __METHOD__ ); return self::AS_TEXTBOX_EMPTY; } if ( $this->summary != '' ) { - $sectionanchor = $wgParser->guessSectionNameFromWikiText( $this->summary ); + $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText( $this->summary ); # This is a new section, so create a link to the new section # in the revision summary. $cleanSummary = $wgParser->stripSectionName( $this->summary ); @@ -1073,11 +1029,11 @@ class EditPage { $hasmatch = preg_match( "/^ *([=]{1,6})(.*?)(\\1) *\\n/i", $this->textbox1, $matches ); # we can't deal with anchors, includes, html etc in the header for now, # headline would need to be parsed to improve this - if ( $hasmatch and strlen($matches[2]) > 0 ) { - $sectionanchor = $wgParser->guessSectionNameFromWikiText( $matches[2] ); + if ( $hasmatch and strlen( $matches[2] ) > 0 ) { + $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText( $matches[2] ); } } - wfProfileOut( "$fname-sectionanchor" ); + wfProfileOut( __METHOD__ . '-sectionanchor' ); // Save errors may fall down to the edit form, but we've now // merged the section into full text. Clear the section field @@ -1087,26 +1043,26 @@ class EditPage { $this->section = ''; // Check for length errors again now that the section is merged in - $this->kblength = (int)(strlen( $text ) / 1024); + $this->kblength = (int)( strlen( $text ) / 1024 ); if ( $this->kblength > $wgMaxArticleSize ) { $this->tooBig = true; - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return self::AS_MAX_ARTICLE_SIZE_EXCEEDED; } # update the article here if ( $this->mArticle->updateArticle( $text, $this->summary, $this->minoredit, - $this->watchthis, $bot, $sectionanchor ) ) + $this->watchthis, $bot, $sectionanchor ) ) { - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return self::AS_SUCCESS_UPDATE; } else { $this->isConflict = true; } - wfProfileOut( $fname ); + wfProfileOut( __METHOD__ ); return self::AS_END; } - + /** * Check if no edits were made by other users since * the time a user started editing the page. Limit to @@ -1117,7 +1073,7 @@ class EditPage { $dbw = wfGetDB( DB_MASTER ); $res = $dbw->select( 'revision', 'rev_user', - array( + array( 'rev_page' => $this->mArticle->getId(), 'rev_timestamp > '.$dbw->addQuotes( $dbw->timestamp($edittime) ) ), @@ -1141,7 +1097,7 @@ class EditPage { $regexes = (array)$wgSpamRegex; return self::matchSpamRegexInternal( $text, $regexes ); } - + /** * Check given input text against $wgSpamRegex, and return the text of the first match. * @return mixed -- matching string or false @@ -1151,7 +1107,7 @@ class EditPage { $regexes = (array)$wgSummarySpamRegex; return self::matchSpamRegexInternal( $text, $regexes ); } - + protected static function matchSpamRegexInternal( $text, $regexes ) { foreach( $regexes as $regex ) { $matches = array(); @@ -1165,10 +1121,24 @@ class EditPage { /** * Initialise form fields in the object * Called on the first invocation, e.g. when a user clicks an edit link + * @returns bool -- if the requested section is valid */ function initialiseForm() { + global $wgUser; $this->edittime = $this->mArticle->getTimestamp(); $this->textbox1 = $this->getContent( false ); + // activate checkboxes if user wants them to be always active + # Sort out the "watch" checkbox + if ( $wgUser->getOption( 'watchdefault' ) ) { + # Watch all edits + $this->watchthis = true; + } elseif ( $wgUser->getOption( 'watchcreations' ) && !$this->mTitle->exists() ) { + # Watch creations + $this->watchthis = true; + } elseif ( $this->mTitle->userIsWatching() ) { + # Already watched + $this->watchthis = true; + } if ( $this->textbox1 === false ) return false; wfProxyCheck(); return true; @@ -1187,7 +1157,7 @@ class EditPage { $wgOut->setPageTitle( wfMsg( $msg, $wgTitle->getPrefixedText() ) ); } else { # Use the title defined by DISPLAYTITLE magic word when present - if ( isset($this->mParserOutput) + if ( isset( $this->mParserOutput ) && ( $dt = $this->mParserOutput->getDisplayTitle() ) !== false ) { $title = $dt; } else { @@ -1204,17 +1174,16 @@ class EditPage { * near the top, for captchas and the like. */ function showEditForm( $formCallback=null ) { - global $wgOut, $wgUser, $wgLang, $wgContLang, $wgMaxArticleSize, $wgTitle, $wgRequest; + global $wgOut, $wgUser, $wgTitle; # If $wgTitle is null, that means we're in API mode. # Some hook probably called this function without checking # for is_null($wgTitle) first. Bail out right here so we don't # do lots of work just to discard it right after. - if (is_null($wgTitle)) + if ( is_null( $wgTitle ) ) return; - $fname = 'EditPage::showEditForm'; - wfProfileIn( $fname ); + wfProfileIn( __METHOD__ ); $sk = $wgUser->getSkin(); @@ -1226,23 +1195,159 @@ class EditPage { if ( $this->formtype == 'preview' ) { $previewOutput = $this->getPreviewText(); } - - wfRunHooks( 'EditPage::showEditForm:initial', array( &$this ) ) ; + + wfRunHooks( 'EditPage::showEditForm:initial', array( &$this ) ); $this->setHeaders(); # Enabled article-related sidebar, toplinks, etc. $wgOut->setArticleRelated( true ); - $cancelParams = array(); + if ( $this->showHeader() === false ) + return; + + $action = htmlspecialchars($this->getActionURL($wgTitle)); + + if ( $wgUser->getOption( 'showtoolbar' ) and !$this->isCssJsSubpage ) { + # prepare toolbar for edit buttons + $toolbar = EditPage::getEditToolbar(); + } else { + $toolbar = ''; + } + + + $wgOut->addHTML( $this->editFormPageTop ); + + if ( $wgUser->getOption( 'previewontop' ) ) { + $this->displayPreviewArea( $previewOutput, true ); + } + + $wgOut->addHTML( $this->editFormTextTop ); + + $templates = $this->getTemplates(); + $formattedtemplates = $sk->formatTemplates( $templates, $this->preview, $this->section != ''); + + $hiddencats = $this->mArticle->getHiddenCategories(); + $formattedhiddencats = $sk->formatHiddenCategories( $hiddencats ); + + if ( $this->wasDeletedSinceLastEdit() && 'save' != $this->formtype ) { + $wgOut->wrapWikiMsg( + "
\n$1\n
", + 'deletedwhileediting' ); + } elseif ( $this->wasDeletedSinceLastEdit() ) { + // Hide the toolbar and edit area, user can click preview to get it back + // Add an confirmation checkbox and explanation. + $toolbar = ''; + // @todo move this to a cleaner conditional instead of blanking a variable + } + $wgOut->addHTML( << +HTML +); + + if ( is_callable( $formCallback ) ) { + call_user_func_array( $formCallback, array( &$wgOut ) ); + } + + wfRunHooks( 'EditPage::showEditForm:fields', array( &$this, &$wgOut ) ); + + // Put these up at the top to ensure they aren't lost on early form submission + $this->showFormBeforeText(); + + if ( $this->wasDeletedSinceLastEdit() && 'save' == $this->formtype ) { + $wgOut->addHTML( + '
' . + $wgOut->parse( wfMsg( 'confirmrecreate', $this->lastDelete->user_name , $this->lastDelete->log_comment ) ) . + Xml::checkLabel( wfMsg( 'recreate' ), 'wpRecreate', 'wpRecreate', false, + array( 'title' => $sk->titleAttrib( 'recreate' ), 'tabindex' => 1, 'id' => 'wpRecreate' ) + ) . + '
' + ); + } + + # If a blank edit summary was previously provided, and the appropriate + # user preference is active, pass a hidden tag as wpIgnoreBlankSummary. This will stop the + # user being bounced back more than once in the event that a summary + # is not required. + ##### + # For a bit more sophisticated detection of blank summaries, hash the + # automatic one and pass that in the hidden field wpAutoSummary. + if ( $this->missingSummary || + ( $this->section == 'new' && $this->nosummary ) ) + $wgOut->addHTML( Xml::hidden( 'wpIgnoreBlankSummary', true ) ); + $autosumm = $this->autoSumm ? $this->autoSumm : md5( $this->summary ); + $wgOut->addHTML( Xml::hidden( 'wpAutoSummary', $autosumm ) ); + + $wgOut->addHTML( Xml::hidden( 'oldid', $this->mArticle->getOldID() ) ); + + if ( $this->section == 'new' ) { + $this->showSummaryInput( true, $this->summary ); + $wgOut->addHTML( $this->getSummaryPreview( true, $this->summary ) ); + } + + $wgOut->addHTML( $this->editFormTextBeforeContent ); if ( $this->isConflict ) { - $wgOut->wrapWikiMsg( "
\n$1
", 'explainconflict' ); + // In an edit conflict bypass the overrideable content form method + // and fallback to the raw wpTextbox1 since editconflicts can't be + // resolved between page source edits and custom ui edits using the + // custom edit ui. + $this->showTextbox1( null, $this->getContent() ); + } else { + $this->showContentForm(); + } + + $wgOut->addHTML( $this->editFormTextAfterContent ); + + $wgOut->addWikiText( $this->getCopywarn() ); + if ( isset($this->editFormTextAfterWarn) && $this->editFormTextAfterWarn !== '' ) + $wgOut->addHTML( $this->editFormTextAfterWarn ); + + $this->showStandardInputs(); + + $this->showFormAfterText(); - $this->textbox2 = $this->textbox1; - $this->textbox1 = $this->getContent(); + $this->showTosSummary(); + $this->showEditTools(); + + $wgOut->addHTML( <<editFormTextAfterTools} +
+{$formattedtemplates} +
+
+{$formattedhiddencats} +
+HTML +); + + if ( $this->isConflict ) + $this->showConflict(); + + $wgOut->addHTML( $this->editFormTextBottom ); + $wgOut->addHTML( "\n" ); + if ( !$wgUser->getOption( 'previewontop' ) ) { + $this->displayPreviewArea( $previewOutput, false ); + } + + wfProfileOut( __METHOD__ ); + } + + protected function showHeader() { + global $wgOut, $wgUser, $wgTitle, $wgMaxArticleSize, $wgLang; + if ( $this->isConflict ) { + $wgOut->wrapWikiMsg( "
\n$1\n
", 'explainconflict' ); $this->edittime = $this->mArticle->getTimestamp(); } else { + if ( $this->section != '' && !$this->isSectionEditSupported() ) { + // We use $this->section to much before this and getVal('wgSection') directly in other places + // at this point we can't reset $this->section to '' to fallback to non-section editing. + // Someone is welcome to try refactoring though + $wgOut->showErrorPage( 'sectioneditnotsupported-title', 'sectioneditnotsupported-text' ); + return false; + } + if ( $this->section != '' && $this->section != 'new' ) { $matches = array(); if ( !$this->summary && !$this->preview && !$this->diff ) { @@ -1257,15 +1362,15 @@ class EditPage { } if ( $this->missingComment ) { - $wgOut->wrapWikiMsg( '
$1
', 'missingcommenttext' ); + $wgOut->wrapWikiMsg( "
\n$1\n
", 'missingcommenttext' ); } if ( $this->missingSummary && $this->section != 'new' ) { - $wgOut->wrapWikiMsg( '
$1
', 'missingsummary' ); + $wgOut->wrapWikiMsg( "
\n$1\n
", 'missingsummary' ); } if ( $this->missingSummary && $this->section == 'new' ) { - $wgOut->wrapWikiMsg( '
$1
', 'missingcommentheader' ); + $wgOut->wrapWikiMsg( "
\n$1\n
", 'missingcommentheader' ); } if ( $this->hookError !== '' ) { @@ -1275,18 +1380,18 @@ class EditPage { if ( !$this->checkUnicodeCompliantBrowser() ) { $wgOut->addWikiMsg( 'nonunicodebrowser' ); } + if ( isset( $this->mArticle ) && isset( $this->mArticle->mRevision ) ) { // Let sysop know that this will make private content public if saved if ( !$this->mArticle->mRevision->userCan( Revision::DELETED_TEXT ) ) { - $wgOut->wrapWikiMsg( "\n", 'rev-deleted-text-permission' ); + $wgOut->wrapWikiMsg( "\n", 'rev-deleted-text-permission' ); } else if ( $this->mArticle->mRevision->isDeleted( Revision::DELETED_TEXT ) ) { - $wgOut->wrapWikiMsg( "\n", 'rev-deleted-text-view' ); + $wgOut->wrapWikiMsg( "\n", 'rev-deleted-text-view' ); } if ( !$this->mArticle->mRevision->isCurrent() ) { $this->mArticle->setOldSubtitle( $this->mArticle->mRevision->getId() ); - $cancelParams['oldid'] = $this->mArticle->mRevision->getId(); $wgOut->addWikiMsg( 'editingold' ); } } @@ -1294,42 +1399,42 @@ class EditPage { if ( wfReadOnly() ) { $wgOut->wrapWikiMsg( "
\n$1\n
", array( 'readonlywarning', wfReadOnlyReason() ) ); - } elseif ( $wgUser->isAnon() && $this->formtype != 'preview' ) { - $wgOut->wrapWikiMsg( '
$1
', 'anoneditwarning' ); + } elseif ( $wgUser->isAnon() ) { + if ( $this->formtype != 'preview' ) { + $wgOut->wrapWikiMsg( "
\n$1
", 'anoneditwarning' ); + } else { + $wgOut->wrapWikiMsg( "
\n$1
", 'anonpreviewwarning' ); + } } else { if ( $this->isCssJsSubpage ) { # Check the skin exists if ( !$this->isValidCssJsSubpage ) { - $wgOut->addWikiMsg( 'userinvalidcssjstitle', $wgTitle->getSkinFromCssJsSubpage() ); + $wgOut->wrapWikiMsg( "
\n$1\n
", array( 'userinvalidcssjstitle', $wgTitle->getSkinFromCssJsSubpage() ) ); } if ( $this->formtype !== 'preview' ) { if ( $this->isCssSubpage ) - $wgOut->addWikiMsg( 'usercssyoucanpreview' ); + $wgOut->wrapWikiMsg( "
\n$1\n
", array( 'usercssyoucanpreview' ) ); if ( $this->isJsSubpage ) - $wgOut->addWikiMsg( 'userjsyoucanpreview' ); + $wgOut->wrapWikiMsg( "
\n$1\n
", array( 'userjsyoucanpreview' ) ); } } } - $classes = array(); // Textarea CSS - if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) { - } elseif ( $this->mTitle->isProtected( 'edit' ) ) { + if ( $this->mTitle->getNamespace() != NS_MEDIAWIKI && $this->mTitle->isProtected( 'edit' ) ) { # Is the title semi-protected? if ( $this->mTitle->isSemiProtected() ) { $noticeMsg = 'semiprotectedpagewarning'; - $classes[] = 'mw-textarea-sprotected'; } else { # Then it must be protected based on static groups (regular) $noticeMsg = 'protectedpagewarning'; - $classes[] = 'mw-textarea-protected'; } - LogEventsList::showLogExtract( $wgOut, 'protect', $this->mTitle->getPrefixedText(), '', + LogEventsList::showLogExtract( $wgOut, 'protect', $this->mTitle->getPrefixedText(), '', array( 'lim' => 1, 'msgKey' => array( $noticeMsg ) ) ); } if ( $this->mTitle->isCascadeProtected() ) { # Is this page under cascading protection from some source pages? list($cascadeSources, /* $restrictions */) = $this->mTitle->getCascadeProtectionSources(); - $notice = "
$1\n"; + $notice = "
\n$1\n"; $cascadeSourcesCount = count( $cascadeSources ); if ( $cascadeSourcesCount > 0 ) { # Explain, and list the titles responsible @@ -1341,12 +1446,17 @@ class EditPage { $wgOut->wrapWikiMsg( $notice, array( 'cascadeprotectedwarning', $cascadeSourcesCount ) ); } if ( !$this->mTitle->exists() && $this->mTitle->getRestrictions( 'create' ) ) { - $wgOut->wrapWikiMsg( '
$1
', 'titleprotectedwarning' ); + LogEventsList::showLogExtract( $wgOut, 'protect', $this->mTitle->getPrefixedText(), '', + array( 'lim' => 1, + 'showIfEmpty' => false, + 'msgKey' => array( 'titleprotectedwarning' ), + 'wrap' => "
\n$1
" ) ); } if ( $this->kblength === false ) { - $this->kblength = (int)(strlen( $this->textbox1 ) / 1024); + $this->kblength = (int)( strlen( $this->textbox1 ) / 1024 ); } + if ( $this->tooBig || $this->kblength > $wgMaxArticleSize ) { $wgOut->addHTML( "
\n" ); $wgOut->addWikiMsg( 'longpageerror', $wgLang->formatNum( $this->kblength ), $wgLang->formatNum( $wgMaxArticleSize ) ); @@ -1356,254 +1466,113 @@ class EditPage { $wgOut->addWikiMsg( 'longpagewarning', $wgLang->formatNum( $this->kblength ) ); $wgOut->addHTML( "
\n" ); } + } - $action = $wgTitle->escapeLocalURL( array( 'action' => $this->action ) ); - - $summary = wfMsgExt( 'summary', 'parseinline' ); - $subject = wfMsgExt( 'subject', 'parseinline' ); - - $cancel = $sk->link( - $wgTitle, - wfMsgExt( 'cancel', array( 'parseinline' ) ), - array( 'id' => 'mw-editform-cancel' ), - $cancelParams, - array( 'known', 'noclasses' ) + /** + * Standard summary input and label (wgSummary), abstracted so EditPage + * subclasses may reorganize the form. + * Note that you do not need to worry about the label's for=, it will be + * inferred by the id given to the input. You can remove them both by + * passing array( 'id' => false ) to $userInputAttrs. + * + * @param $summary The value of the summary input + * @param $labelText The html to place inside the label + * @param $inputAttrs An array of attrs to use on the input + * @param $spanLabelAttrs An array of attrs to use on the span inside the label + * + * @return array An array in the format array( $label, $input ) + */ + function getSummaryInput($summary = "", $labelText = null, $inputAttrs = null, $spanLabelAttrs = null) { + global $wgUser; + //Note: the maxlength is overriden in JS to 250 and to make it use UTF-8 bytes, not characters. + $inputAttrs = ( is_array($inputAttrs) ? $inputAttrs : array() ) + array( + 'id' => 'wpSummary', + 'maxlength' => '200', + 'tabindex' => '1', + 'size' => 60, + 'spellcheck' => 'true', + ) + $wgUser->getSkin()->tooltipAndAccessKeyAttribs( 'summary' ); + + $spanLabelAttrs = ( is_array($spanLabelAttrs) ? $spanLabelAttrs : array() ) + array( + 'class' => $this->missingSummary ? 'mw-summarymissed' : 'mw-summary', + 'id' => "wpSummaryLabel" ); - $separator = wfMsgExt( 'pipe-separator' , 'escapenoentities' ); - $edithelpurl = Skin::makeInternalOrExternalUrl( wfMsgForContent( 'edithelppage' )); - $edithelp = ''. - htmlspecialchars( wfMsg( 'edithelp' ) ).' '. - htmlspecialchars( wfMsg( 'newwindow' ) ); - - global $wgRightsText; - if ( $wgRightsText ) { - $copywarnMsg = array( 'copyrightwarning', - '[[' . wfMsgForContent( 'copyrightpage' ) . ']]', - $wgRightsText ); - } else { - $copywarnMsg = array( 'copyrightwarning2', - '[[' . wfMsgForContent( 'copyrightpage' ) . ']]' ); - } - // Allow for site and per-namespace customization of contribution/copyright notice. - wfRunHooks( 'EditPageCopyrightWarning', array( $this->mTitle, &$copywarnMsg ) ); - if ( $wgUser->getOption('showtoolbar') and !$this->isCssJsSubpage ) { - # prepare toolbar for edit buttons - $toolbar = EditPage::getEditToolbar(); - } else { - $toolbar = ''; + $label = null; + if ( $labelText ) { + $label = Xml::tags( 'label', $inputAttrs['id'] ? array( 'for' => $inputAttrs['id'] ) : null, $labelText ); + $label = Xml::tags( 'span', $spanLabelAttrs, $label ); } + $input = Html::input( 'wpSummary', $summary, 'text', $inputAttrs ); - // activate checkboxes if user wants them to be always active - if ( !$this->preview && !$this->diff ) { - # Sort out the "watch" checkbox - if ( $wgUser->getOption( 'watchdefault' ) ) { - # Watch all edits - $this->watchthis = true; - } elseif ( $wgUser->getOption( 'watchcreations' ) && !$this->mTitle->exists() ) { - # Watch creations - $this->watchthis = true; - } elseif ( $this->mTitle->userIsWatching() ) { - # Already watched - $this->watchthis = true; - } - - # May be overriden by request parameters - if( $wgRequest->getBool( 'watchthis' ) ) { - $this->watchthis = true; - } - - if ( $wgUser->getOption( 'minordefault' ) ) $this->minoredit = true; - } - - $wgOut->addHTML( $this->editFormPageTop ); - - if ( $wgUser->getOption( 'previewontop' ) ) { - $this->displayPreviewArea( $previewOutput, true ); - } - - - $wgOut->addHTML( $this->editFormTextTop ); - - # if this is a comment, show a subject line at the top, which is also the edit summary. - # Otherwise, show a summary field at the bottom - $summarytext = $wgContLang->recodeForEdit( $this->summary ); + return array( $label, $input ); + } - # If a blank edit summary was previously provided, and the appropriate - # user preference is active, pass a hidden tag as wpIgnoreBlankSummary. This will stop the - # user being bounced back more than once in the event that a summary - # is not required. - ##### - # For a bit more sophisticated detection of blank summaries, hash the - # automatic one and pass that in the hidden field wpAutoSummary. - $summaryhiddens = ''; - if ( $this->missingSummary ) $summaryhiddens .= Xml::hidden( 'wpIgnoreBlankSummary', true ); - $autosumm = $this->autoSumm ? $this->autoSumm : md5( $this->summary ); - $summaryhiddens .= Xml::hidden( 'wpAutoSummary', $autosumm ); - if ( $this->section == 'new' ) { - $commentsubject = ''; - if ( !$wgRequest->getBool( 'nosummary' ) ) { - # Add a class if 'missingsummary' is triggered to allow styling of the summary line - $summaryClass = $this->missingSummary ? 'mw-summarymissed' : 'mw-summary'; - - $commentsubject = - Xml::tags( 'label', array( 'for' => 'wpSummary' ), $subject ); - $commentsubject = - Xml::tags( 'span', array( 'class' => $summaryClass, 'id' => "wpSummaryLabel" ), - $commentsubject ); - $commentsubject .= ' '; - $commentsubject .= Xml::input( 'wpSummary', - 60, - $summarytext, - array( - 'id' => 'wpSummary', - 'maxlength' => '200', - 'tabindex' => '1' - ) ); - } else { - $summaryhiddens .= Xml::hidden( 'wpIgnoreBlankSummary', true ); # bug 18699 - } - $editsummary = "
\n"; - global $wgParser; - $formattedSummary = wfMsgForContent( 'newsectionsummary', $wgParser->stripSectionName( $this->summary ) ); - $subjectpreview = $summarytext && ( $this->preview || $this->diff ) ? - "
". wfMsgExt('subject-preview', 'parseinline') . $sk->commentBlock( $formattedSummary, $this->mTitle, true )."
\n" : ''; - $summarypreview = ''; + /** + * @param $isSubjectPreview Boolean: true if this is the section subject/title + * up top, or false if this is the comment summary + * down below the textarea + * @param $summary String: The text of the summary to display + * @return String + */ + protected function showSummaryInput( $isSubjectPreview, $summary = "" ) { + global $wgOut, $wgContLang; + # Add a class if 'missingsummary' is triggered to allow styling of the summary line + $summaryClass = $this->missingSummary ? 'mw-summarymissed' : 'mw-summary'; + if ( $isSubjectPreview ) { + if ( $this->nosummary ) + return; } else { - $commentsubject = ''; - - # Add a class if 'missingsummary' is triggered to allow styling of the summary line - $summaryClass = $this->missingSummary ? 'mw-summarymissed' : 'mw-summary'; - - $editsummary = Xml::tags( 'label', array( 'for' => 'wpSummary' ), $summary ); - $editsummary = Xml::tags( 'span', array( 'class' => $summaryClass, 'id' => "wpSummaryLabel" ), - $editsummary ) . ' '; - - $editsummary .= Xml::input( 'wpSummary', - 60, - $summarytext, - array( - 'id' => 'wpSummary', - 'maxlength' => '200', - 'tabindex' => '1' - ) ); - - // No idea where this is closed. - $editsummary = Xml::openElement( 'div', array( 'class' => 'editOptions' ) ) - . $editsummary . '
'; - - $summarypreview = ''; - if ( $summarytext && ( $this->preview || $this->diff ) ) { - $summarypreview = - Xml::tags( 'div', - array( 'class' => 'mw-summary-preview' ), - wfMsgExt( 'summary-preview', 'parseinline' ) . - $sk->commentBlock( $this->summary, $this->mTitle ) - ); - } - $subjectpreview = ''; - } - $commentsubject .= $summaryhiddens; - - # Set focus to the edit box on load, except on preview or diff, where it would interfere with the display - if ( !$this->preview && !$this->diff ) { - $wgOut->setOnloadHandler( 'document.editform.wpTextbox1.focus()' ); - } - $templates = $this->getTemplates(); - $formattedtemplates = $sk->formatTemplates( $templates, $this->preview, $this->section != ''); - - $hiddencats = $this->mArticle->getHiddenCategories(); - $formattedhiddencats = $sk->formatHiddenCategories( $hiddencats ); - - global $wgUseMetadataEdit ; - if ( $wgUseMetadataEdit ) { - $metadata = $this->mMetaData ; - $metadata = htmlspecialchars( $wgContLang->recodeForEdit( $metadata ) ) ; - $top = wfMsgWikiHtml( 'metadata_help' ); - /* ToDo: Replace with clean code */ - $ew = $wgUser->getOption( 'editwidth' ); - if ( $ew ) $ew = " style=\"width:100%\""; - else $ew = ''; - $cols = $wgUser->getIntOption( 'cols' ); - /* /ToDo */ - $metadata = $top . "" ; - } - else $metadata = "" ; - - $recreate = ''; - if ( $this->wasDeletedSinceLastEdit() ) { - if ( 'save' != $this->formtype ) { - $wgOut->wrapWikiMsg( - "
\n$1
", - 'deletedwhileediting' ); - } else { - // Hide the toolbar and edit area, user can click preview to get it back - // Add an confirmation checkbox and explanation. - $toolbar = ''; - $recreate = '
' . - $wgOut->parse( wfMsg( 'confirmrecreate', $this->lastDelete->user_name , $this->lastDelete->log_comment ) ) . - Xml::checkLabel( wfMsg( 'recreate' ), 'wpRecreate', 'wpRecreate', false, - array( 'title' => $sk->titleAttrib( 'recreate' ), 'tabindex' => 1, 'id' => 'wpRecreate' ) - ) . '
'; - } + if ( !$this->mShowSummaryField ) + return; } + $summary = $wgContLang->recodeForEdit( $summary ); + $labelText = wfMsgExt( $isSubjectPreview ? 'subject' : 'summary', 'parseinline' ); + list($label, $input) = $this->getSummaryInput($summary, $labelText, array( 'class' => $summaryClass ), array()); + $wgOut->addHTML("{$label} {$input}"); + } - $tabindex = 2; - - $checkboxes = $this->getCheckboxes( $tabindex, $sk, - array( 'minor' => $this->minoredit, 'watch' => $this->watchthis ) ); - - $checkboxhtml = implode( $checkboxes, "\n" ); - - $buttons = $this->getEditButtons( $tabindex ); - $buttonshtml = implode( $buttons, "\n" ); - - $safemodehtml = $this->checkUnicodeCompliantBrowser() - ? '' : Xml::hidden( 'safemode', '1' ); - - $wgOut->addHTML( << -END -); + /** + * @param $isSubjectPreview Boolean: true if this is the section subject/title + * up top, or false if this is the comment summary + * down below the textarea + * @param $summary String: the text of the summary to display + * @return String + */ + protected function getSummaryPreview( $isSubjectPreview, $summary = "" ) { + if ( !$summary || ( !$this->preview && !$this->diff ) ) + return ""; - if ( is_callable( $formCallback ) ) { - call_user_func_array( $formCallback, array( &$wgOut ) ); - } + global $wgParser, $wgUser; + $sk = $wgUser->getSkin(); - wfRunHooks( 'EditPage::showEditForm:fields', array( &$this, &$wgOut ) ); + if ( $isSubjectPreview ) + $summary = wfMsgForContent( 'newsectionsummary', $wgParser->stripSectionName( $summary ) ); - // Put these up at the top to ensure they aren't lost on early form submission - $this->showFormBeforeText(); + $message = $isSubjectPreview ? 'subject-preview' : 'summary-preview'; - $wgOut->addHTML( <<editFormTextBeforeContent} -END -); - $this->showTextbox1( $classes ); - - $wgOut->wrapWikiMsg( "
\n$1\n
", $copywarnMsg ); - $wgOut->addHTML( <<editFormTextAfterWarn} -{$metadata} -{$editsummary} -{$summarypreview} -{$checkboxhtml} -{$safemodehtml} -END -); + $summary = wfMsgExt( $message, 'parseinline' ) . $sk->commentBlock( $summary, $this->mTitle, $isSubjectPreview ); + return Xml::tags( 'div', array( 'class' => 'mw-summary-preview' ), $summary ); + } - $wgOut->addHTML( -"
-{$buttonshtml} - {$cancel}{$separator}{$edithelp} -
-
"); + protected function showFormBeforeText() { + global $wgOut; + $section = htmlspecialchars( $this->section ); + $wgOut->addHTML( << + + + + +INPUTS + ); + if ( !$this->checkUnicodeCompliantBrowser() ) + $wgOut->addHTML(Xml::hidden( 'safemode', '1' )); + } + protected function showFormAfterText() { + global $wgOut, $wgUser; /** * To make it harder for someone to slip a user a page * which submits an edit form to the wiki without their @@ -1616,69 +1585,64 @@ END * include the constant suffix to prevent editing from * broken text-mangling proxies. */ - $token = htmlspecialchars( $wgUser->editToken() ); - $wgOut->addHTML( "\n\n" ); - - $this->showTosSummary(); - $this->showEditTools(); - - $wgOut->addHTML( <<editFormTextAfterTools} -
-{$formattedtemplates} -
-
-{$formattedhiddencats} -
-END -); - - if ( $this->isConflict && wfRunHooks( 'EditPageBeforeConflictDiff', array( &$this, &$wgOut ) ) ) { - $wgOut->wrapWikiMsg( '==$1==', "yourdiff" ); - - $de = new DifferenceEngine( $this->mTitle ); - $de->setText( $this->textbox2, $this->textbox1 ); - $de->showDiff( wfMsg( "yourtext" ), wfMsg( "storedversion" ) ); - - $wgOut->wrapWikiMsg( '==$1==', "yourtext" ); - $this->showTextbox2(); - } - $wgOut->addHTML( $this->editFormTextBottom ); - $wgOut->addHTML( "\n" ); - if ( !$wgUser->getOption( 'previewontop' ) ) { - $this->displayPreviewArea( $previewOutput, false ); - } + $wgOut->addHTML( "\n" . Xml::hidden( "wpEditToken", $wgUser->editToken() ) . "\n" ); + } - wfProfileOut( $fname ); + /** + * Subpage overridable method for printing the form for page content editing + * By default this simply outputs wpTextbox1 + * Subclasses can override this to provide a custom UI for editing; + * be it a form, or simply wpTextbox1 with a modified content that will be + * reverse modified when extracted from the post data. + * Note that this is basically the inverse for importContentFormData + * + * @param $request WebRequest + */ + protected function showContentForm() { + $this->showTextbox1(); } - protected function showFormBeforeText() { - global $wgOut; - $wgOut->addHTML( " -section ) . "\" name=\"wpSection\" /> -starttime}\" name=\"wpStarttime\" />\n -edittime}\" name=\"wpEdittime\" />\n -scrolltop}\" name=\"wpScrolltop\" id=\"wpScrolltop\" />\n" ); - } - - protected function showTextbox1( $classes ) { + /** + * Method to output wpTextbox1 + * The $textoverride method can be used by subclasses overriding showContentForm + * to pass back to this method. + * + * @param $customAttribs An array of html attributes to use in the textarea + * @param $textoverride String: optional text to override $this->textarea1 with + */ + protected function showTextbox1($customAttribs = null, $textoverride = null) { + $classes = array(); // Textarea CSS + if ( $this->mTitle->getNamespace() != NS_MEDIAWIKI && $this->mTitle->isProtected( 'edit' ) ) { + # Is the title semi-protected? + if ( $this->mTitle->isSemiProtected() ) { + $classes[] = 'mw-textarea-sprotected'; + } else { + # Then it must be protected based on static groups (regular) + $classes[] = 'mw-textarea-protected'; + } + } $attribs = array( 'tabindex' => 1 ); - + if ( is_array($customAttribs) ) + $attribs += $customAttribs; + if ( $this->wasDeletedSinceLastEdit() ) $attribs['type'] = 'hidden'; - if ( !empty($classes) ) - $attribs['class'] = implode(' ',$classes); - - $this->showTextbox( $this->textbox1, 'wpTextbox1', $attribs ); + if ( !empty( $classes ) ) { + if ( isset($attribs['class']) ) + $classes[] = $attribs['class']; + $attribs['class'] = implode( ' ', $classes ); + } + + $this->showTextbox( isset($textoverride) ? $textoverride : $this->textbox1, 'wpTextbox1', $attribs ); } - + protected function showTextbox2() { $this->showTextbox( $this->textbox2, 'wpTextbox2', array( 'tabindex' => 6 ) ); } - - protected function showTextbox( $content, $name, $attribs = array() ) { + + protected function showTextbox( $content, $name, $customAttribs = array() ) { global $wgOut, $wgUser; - + $wikitext = $this->safeUnicodeOutput( $content ); if ( $wikitext !== '' ) { // Ensure there's a newline at the end, otherwise adding lines @@ -1687,18 +1651,16 @@ END // mode will show an extra newline. A bit annoying. $wikitext .= "\n"; } - - $attribs['accesskey'] = ','; - $attribs['id'] = $name; - - if ( $wgUser->getOption( 'editwidth' ) ) - $attribs['style'] = 'width: 100%'; - - $wgOut->addHTML( Xml::textarea( - $name, - $wikitext, - $wgUser->getIntOption( 'cols' ), $wgUser->getIntOption( 'rows' ), - $attribs ) ); + + $attribs = $customAttribs + array( + 'accesskey' => ',', + 'id' => $name, + 'cols' => $wgUser->getIntOption( 'cols' ), + 'rows' => $wgUser->getIntOption( 'rows' ), + 'style' => '' // avoid php notices when appending preferences (appending allows customAttribs['style'] to still work + ); + + $wgOut->addHTML( Html::textarea( $name, $wikitext, $attribs ) ); } protected function displayPreviewArea( $previewOutput, $isOnTop = false ) { @@ -1729,7 +1691,7 @@ END * Append preview output to $wgOut. * Includes category rendering if this is a category page. * - * @param string $text The HTML to be output for the preview. + * @param $text String: the HTML to be output for the preview. */ protected function showPreview( $text ) { global $wgOut; @@ -1746,29 +1708,15 @@ END } /** - * Live Preview lets us fetch rendered preview page content and - * add it to the page without refreshing the whole page. - * If not supported by the browser it will fall through to the normal form - * submission method. + * Give a chance for site and per-namespace customizations of + * terms of service summary link that might exist separately + * from the copyright notice. * - * This function outputs a script tag to support live preview, and - * returns an onclick handler which should be added to the attributes - * of the preview button + * This will display between the save button and the edit tools, + * so should remain short! */ - function doLivePreviewScript() { - global $wgOut, $wgTitle; - $wgOut->addScriptFile( 'preview.js' ); - return ""; - } - protected function showTosSummary() { $msg = 'editpage-tos-summary'; - // Give a chance for site and per-namespace customizations of - // terms of service summary link that might exist separately - // from the copyright notice. - // - // This will display between the save button and the edit tools, - // so should remain short! wfRunHooks( 'EditPageTosSummary', array( $this->mTitle, &$msg ) ); $text = wfMsg( $msg ); if( !wfEmptyMsg( $msg, $text ) && $text !== '-' ) { @@ -1786,6 +1734,67 @@ END $wgOut->addHTML( '
' ); } + protected function getCopywarn() { + global $wgRightsText; + if ( $wgRightsText ) { + $copywarnMsg = array( 'copyrightwarning', + '[[' . wfMsgForContent( 'copyrightpage' ) . ']]', + $wgRightsText ); + } else { + $copywarnMsg = array( 'copyrightwarning2', + '[[' . wfMsgForContent( 'copyrightpage' ) . ']]' ); + } + // Allow for site and per-namespace customization of contribution/copyright notice. + wfRunHooks( 'EditPageCopyrightWarning', array( $this->mTitle, &$copywarnMsg ) ); + + return "
\n" . call_user_func_array("wfMsgNoTrans", $copywarnMsg) . "\n
"; + } + + protected function showStandardInputs( &$tabindex = 2 ) { + global $wgOut, $wgUser; + $wgOut->addHTML( "
\n" ); + + if ( $this->section != 'new' ) { + $this->showSummaryInput( false, $this->summary ); + $wgOut->addHTML( $this->getSummaryPreview( false, $this->summary ) ); + } + + $checkboxes = $this->getCheckboxes( $tabindex, $wgUser->getSkin(), + array( 'minor' => $this->minoredit, 'watch' => $this->watchthis ) ); + $wgOut->addHTML( "
" . implode( $checkboxes, "\n" ) . "
\n" ); + $wgOut->addHTML( "
\n" ); + $wgOut->addHTML( implode( $this->getEditButtons( $tabindex ), "\n" ) . "\n" ); + + $cancel = $this->getCancelLink(); + $separator = wfMsgExt( 'pipe-separator' , 'escapenoentities' ); + $edithelpurl = Skin::makeInternalOrExternalUrl( wfMsgForContent( 'edithelppage' ) ); + $edithelp = ''. + htmlspecialchars( wfMsg( 'edithelp' ) ).' '. + htmlspecialchars( wfMsg( 'newwindow' ) ); + $wgOut->addHTML( " {$cancel}{$separator}{$edithelp}\n" ); + $wgOut->addHTML( "
\n
\n" ); + } + + /* + * Show an edit conflict. textbox1 is already shown in showEditForm(). + * If you want to use another entry point to this function, be careful. + */ + protected function showConflict() { + global $wgOut; + $this->textbox2 = $this->textbox1; + $this->textbox1 = $this->getContent(); + if ( wfRunHooks( 'EditPageBeforeConflictDiff', array( &$this, &$wgOut ) ) ) { + $wgOut->wrapWikiMsg( '

$1

', "yourdiff" ); + + $de = new DifferenceEngine( $this->mTitle ); + $de->setText( $this->textbox2, $this->textbox1 ); + $de->showDiff( wfMsg( "yourtext" ), wfMsg( "storedversion" ) ); + + $wgOut->wrapWikiMsg( '

$1

', "yourtext" ); + $this->showTextbox2(); + } + } + protected function getLastDelete() { $dbr = wfGetDB( DB_SLAVE ); $data = $dbr->selectRow( @@ -1798,7 +1807,7 @@ END 'log_title', 'log_comment', 'log_params', - 'log_deleted', + 'log_deleted', 'user_name' ), array( 'log_namespace' => $this->mTitle->getNamespace(), 'log_title' => $this->mTitle->getDBkey(), @@ -1809,11 +1818,11 @@ END array( 'LIMIT' => 1, 'ORDER BY' => 'log_timestamp DESC' ) ); // Quick paranoid permission checks... - if( is_object($data) ) { + if( is_object( $data ) ) { if( $data->log_deleted & LogPage::DELETED_USER ) - $data->user_name = wfMsgHtml('rev-deleted-user'); + $data->user_name = wfMsgHtml( 'rev-deleted-user' ); if( $data->log_deleted & LogPage::DELETED_COMMENT ) - $data->log_comment = wfMsgHtml('rev-deleted-comment'); + $data->log_comment = wfMsgHtml( 'rev-deleted-comment' ); } return $data; } @@ -1823,7 +1832,7 @@ END * @return string */ function getPreviewText() { - global $wgOut, $wgUser, $wgTitle, $wgParser, $wgLang, $wgContLang, $wgMessageCache; + global $wgOut, $wgUser, $wgParser, $wgMessageCache; wfProfileIn( __METHOD__ ); @@ -1854,12 +1863,12 @@ END # XXX: stupid php bug won't let us use $wgTitle->isCssJsSubpage() here if ( $this->isCssJsSubpage ) { - if (preg_match("/\\.css$/", $this->mTitle->getText() ) ) { - $previewtext = wfMsg('usercsspreview'); - } else if (preg_match("/\\.js$/", $this->mTitle->getText() ) ) { - $previewtext = wfMsg('userjspreview'); + if (preg_match( "/\\.css$/", $this->mTitle->getText() ) ) { + $previewtext = "
\n" . wfMsg( 'usercsspreview' ) . "\n
"; + } else if (preg_match( "/\\.js$/", $this->mTitle->getText() ) ) { + $previewtext = "
\n" . wfMsg( 'userjspreview' ) . "\n
"; } - $parserOptions->setTidy(true); + $parserOptions->setTidy( true ); $parserOutput = $wgParser->parse( $previewtext, $this->mTitle, $parserOptions ); $previewHTML = $parserOutput->mText; } elseif ( $rt = Title::newFromRedirectArray( $this->textbox1 ) ) { @@ -1869,11 +1878,11 @@ END # If we're adding a comment, we need to show the # summary as the headline - if ( $this->section=="new" && $this->summary!="" ) { - $toparse="== {$this->summary} ==\n\n".$toparse; + if ( $this->section == "new" && $this->summary != "" ) { + $toparse = "== {$this->summary} ==\n\n" . $toparse; } - if ( $this->mMetaData != "" ) $toparse .= "\n" . $this->mMetaData; + wfRunHooks( 'EditPageGetPreviewText', array( $this, &$toparse ) ); // Parse mediawiki messages with correct target language if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) { @@ -1883,7 +1892,7 @@ END } - $parserOptions->setTidy(true); + $parserOptions->setTidy( true ); $parserOptions->enableLimitReport(); $parserOutput = $wgParser->parse( $this->mArticle->preSaveTransform( $toparse ), $this->mTitle, $parserOptions ); @@ -1908,13 +1917,13 @@ END $wgOut->parse( $note ) . $conflict . "
\n"; wfProfileOut( __METHOD__ ); - return $previewhead . $previewHTML; + return $previewhead . $previewHTML . $this->previewTextAfterContent; } - + function getTemplates() { if ( $this->preview || $this->section != '' ) { $templates = array(); - if ( !isset($this->mParserOutput) ) return $templates; + if ( !isset( $this->mParserOutput ) ) return $templates; foreach( $this->mParserOutput->getTemplates() as $ns => $template) { foreach( array_keys( $template ) as $dbk ) { $templates[] = Title::makeTitle($ns, $dbk); @@ -1930,7 +1939,7 @@ END * Call the stock "user is blocked" page */ function blockedPage() { - global $wgOut, $wgUser; + global $wgOut; $wgOut->blockedPage( false ); # Standard block notice on the top, don't 'return' # If the user made changes, preserve them when showing the markup @@ -1944,14 +1953,9 @@ END # Spit out the source or the user's modified version if ( $source !== false ) { - $rows = $wgUser->getIntOption( 'rows' ); - $cols = $wgUser->getIntOption( 'cols' ); - $attribs = array( 'id' => 'wpTextbox1', 'name' => 'wpTextbox1', 'cols' => $cols, 'rows' => $rows, 'readonly' => 'readonly' ); $wgOut->addHTML( '
' ); $wgOut->addWikiMsg( $first ? 'blockedoriginalsource' : 'blockededitsource', $this->mTitle->getPrefixedText() ); - # Why we don't use Xml::element here? - # Is it because if $source is '', it returns