00001 <?php
00019 class EditPage {
00020 const AS_SUCCESS_UPDATE = 200;
00021 const AS_SUCCESS_NEW_ARTICLE = 201;
00022 const AS_HOOK_ERROR = 210;
00023 const AS_FILTERING = 211;
00024 const AS_HOOK_ERROR_EXPECTED = 212;
00025 const AS_BLOCKED_PAGE_FOR_USER = 215;
00026 const AS_CONTENT_TOO_BIG = 216;
00027 const AS_USER_CANNOT_EDIT = 217;
00028 const AS_READ_ONLY_PAGE_ANON = 218;
00029 const AS_READ_ONLY_PAGE_LOGGED = 219;
00030 const AS_READ_ONLY_PAGE = 220;
00031 const AS_RATE_LIMITED = 221;
00032 const AS_ARTICLE_WAS_DELETED = 222;
00033 const AS_NO_CREATE_PERMISSION = 223;
00034 const AS_BLANK_ARTICLE = 224;
00035 const AS_CONFLICT_DETECTED = 225;
00036 const AS_SUMMARY_NEEDED = 226;
00037 const AS_TEXTBOX_EMPTY = 228;
00038 const AS_MAX_ARTICLE_SIZE_EXCEEDED = 229;
00039 const AS_OK = 230;
00040 const AS_END = 231;
00041 const AS_SPAM_ERROR = 232;
00042 const AS_IMAGE_REDIRECT_ANON = 233;
00043 const AS_IMAGE_REDIRECT_LOGGED = 234;
00044
00045 var $mArticle;
00046 var $mTitle;
00047 var $action;
00048 var $isConflict = false;
00049 var $isCssJsSubpage = false;
00050 var $isCssSubpage = false;
00051 var $isJsSubpage = false;
00052 var $deletedSinceEdit = false;
00053 var $formtype;
00054 var $firsttime;
00055 var $lastDelete;
00056 var $mTokenOk = false;
00057 var $mTokenOkExceptSuffix = false;
00058 var $mTriedSave = false;
00059 var $tooBig = false;
00060 var $kblength = false;
00061 var $missingComment = false;
00062 var $missingSummary = false;
00063 var $allowBlankSummary = false;
00064 var $autoSumm = '';
00065 var $hookError = '';
00066 #var $mPreviewTemplates;
00067 var $mParserOutput;
00068 var $mBaseRevision = false;
00069 var $mShowSummaryField = true;
00070
00071 # Form values
00072 var $save = false, $preview = false, $diff = false;
00073 var $minoredit = false, $watchthis = false, $recreate = false;
00074 var $textbox1 = '', $textbox2 = '', $summary = '', $nosummary = false;
00075 var $edittime = '', $section = '', $starttime = '';
00076 var $oldid = 0, $editintro = '', $scrolltop = null, $bot = true;
00077
00078 # Placeholders for text injection by hooks (must be HTML)
00079 # extensions should take care to _append_ to the present value
00080 public $editFormPageTop;
00081 public $editFormTextTop;
00082 public $editFormTextBeforeContent;
00083 public $editFormTextAfterWarn;
00084 public $editFormTextAfterTools;
00085 public $editFormTextBottom;
00086 public $editFormTextAfterContent;
00087 public $previewTextAfterContent;
00088
00089
00090 public $didSave = false;
00091 public $undidRev = 0;
00092
00093 public $suppressIntro = false;
00094
00099 function EditPage( $article ) {
00100 $this->mArticle =& $article;
00101 $this->mTitle = $article->getTitle();
00102 $this->action = 'submit';
00103
00104 # Placeholders for text injection by hooks (empty per default)
00105 $this->editFormPageTop =
00106 $this->editFormTextTop =
00107 $this->editFormTextBeforeContent =
00108 $this->editFormTextAfterWarn =
00109 $this->editFormTextAfterTools =
00110 $this->editFormTextBottom =
00111 $this->editFormTextAfterContent =
00112 $this->previewTextAfterContent =
00113 $this->mPreloadText = "";
00114 }
00115
00116 function getArticle() {
00117 return $this->mArticle;
00118 }
00119
00120
00126 function getContent( $def_text = '' ) {
00127 global $wgOut, $wgRequest, $wgParser, $wgContLang, $wgMessageCache;
00128
00129 wfProfileIn( __METHOD__ );
00130 # Get variables from query string :P
00131 $section = $wgRequest->getVal( 'section' );
00132
00133 $preload = $wgRequest->getVal( 'preload',
00134
00135 $section === 'new' ? 'MediaWiki:addsection-preload' : '' );
00136 $undoafter = $wgRequest->getVal( 'undoafter' );
00137 $undo = $wgRequest->getVal( 'undo' );
00138
00139 $text = '';
00140
00141
00142 if ( !$this->mTitle->exists() ) {
00143 if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
00144 # If this is a system message, get the default text.
00145 list( $message, $lang ) = $wgMessageCache->figureMessage( $wgContLang->lcfirst( $this->mTitle->getText() ) );
00146 $wgMessageCache->loadAllMessages( $lang );
00147 $text = wfMsgGetKey( $message, false, $lang, false );
00148 if( wfEmptyMsg( $message, $text ) )
00149 $text = $this->getPreloadedText( $preload );
00150 } else {
00151 # If requested, preload some text.
00152 $text = $this->getPreloadedText( $preload );
00153 }
00154
00155 } else {
00156 $text = $this->mArticle->getContent();
00157 if ( $undo > 0 && $undoafter > 0 && $undo < $undoafter ) {
00158 # If they got undoafter and undo round the wrong way, switch them
00159 list( $undo, $undoafter ) = array( $undoafter, $undo );
00160 }
00161 if ( $undo > 0 && $undo > $undoafter ) {
00162 # Undoing a specific edit overrides section editing; section-editing
00163 # doesn't work with undoing.
00164 if ( $undoafter ) {
00165 $undorev = Revision::newFromId( $undo );
00166 $oldrev = Revision::newFromId( $undoafter );
00167 } else {
00168 $undorev = Revision::newFromId( $undo );
00169 $oldrev = $undorev ? $undorev->getPrevious() : null;
00170 }
00171
00172 # Sanity check, make sure it's the right page,
00173 # the revisions exist and they were not deleted.
00174 # Otherwise, $text will be left as-is.
00175 if ( !is_null( $undorev ) && !is_null( $oldrev ) &&
00176 $undorev->getPage() == $oldrev->getPage() &&
00177 $undorev->getPage() == $this->mArticle->getID() &&
00178 !$undorev->isDeleted( Revision::DELETED_TEXT ) &&
00179 !$oldrev->isDeleted( Revision::DELETED_TEXT ) ) {
00180
00181 $undotext = $this->mArticle->getUndoText( $undorev, $oldrev );
00182 if ( $undotext === false ) {
00183 # Warn the user that something went wrong
00184 $this->editFormPageTop .= $wgOut->parse( '<div class="error mw-undo-failure">' . wfMsgNoTrans( 'undo-failure' ) . '</div>' );
00185 } else {
00186 $text = $undotext;
00187 # Inform the user of our success and set an automatic edit summary
00188 $this->editFormPageTop .= $wgOut->parse( '<div class="mw-undo-success">' . wfMsgNoTrans( 'undo-success' ) . '</div>' );
00189 $firstrev = $oldrev->getNext();
00190 # If we just undid one rev, use an autosummary
00191 if ( $firstrev->mId == $undo ) {
00192 $this->summary = wfMsgForContent( 'undo-summary', $undo, $undorev->getUserText() );
00193 $this->undidRev = $undo;
00194 }
00195 $this->formtype = 'diff';
00196 }
00197 } else {
00198
00199
00200
00201 $this->editFormPageTop .= $wgOut->parse( '<div class="error mw-undo-norev">' . wfMsgNoTrans( 'undo-norev' ) . '</div>' );
00202 }
00203 } else if ( $section != '' ) {
00204 if ( $section == 'new' ) {
00205 $text = $this->getPreloadedText( $preload );
00206 } else {
00207
00208 $text = $wgParser->getSection( $text, $section, $def_text );
00209 }
00210 }
00211 }
00212
00213 wfProfileOut( __METHOD__ );
00214 return $text;
00215 }
00216
00218 public function setPreloadedText( $text ) {
00219 $this->mPreloadText = $text;
00220 }
00221
00228 protected function getPreloadedText( $preload ) {
00229 if ( !empty( $this->mPreloadText ) ) {
00230 return $this->mPreloadText;
00231 } elseif ( $preload === '' ) {
00232 return '';
00233 } else {
00234 $preloadTitle = Title::newFromText( $preload );
00235 if ( isset( $preloadTitle ) && $preloadTitle->userCanRead() ) {
00236 $rev = Revision::newFromTitle( $preloadTitle );
00237 if ( is_object( $rev ) ) {
00238 $text = $rev->getText();
00239
00240
00241 $text = preg_replace( '~</?includeonly>~', '', $text );
00242 return $text;
00243 } else
00244 return '';
00245 }
00246 }
00247 }
00248
00249
00250
00251
00252
00253
00254
00255 protected function wasDeletedSinceLastEdit() {
00256 if ( $this->deletedSinceEdit )
00257 return true;
00258 if ( $this->mTitle->isDeletedQuick() ) {
00259 $this->lastDelete = $this->getLastDelete();
00260 if ( $this->lastDelete ) {
00261 $deleteTime = wfTimestamp( TS_MW, $this->lastDelete->log_timestamp );
00262 if ( $deleteTime > $this->starttime ) {
00263 $this->deletedSinceEdit = true;
00264 }
00265 }
00266 }
00267 return $this->deletedSinceEdit;
00268 }
00269
00270 function submit() {
00271 $this->edit();
00272 }
00273
00285 function edit() {
00286 global $wgOut, $wgRequest, $wgUser;
00287
00288 if ( !wfRunHooks( 'AlternateEdit', array( $this ) ) ) {
00289 return;
00290 }
00291
00292 wfProfileIn( __METHOD__ );
00293 wfDebug( __METHOD__.": enter\n" );
00294
00295
00296 $wgOut->setArticleFlag( false );
00297
00298 $this->importFormData( $wgRequest );
00299 $this->firsttime = false;
00300
00301 if ( $this->live ) {
00302 $this->livePreview();
00303 wfProfileOut( __METHOD__ );
00304 return;
00305 }
00306
00307 if ( wfReadOnly() && $this->save ) {
00308
00309 $this->save = false;
00310 $this->preview = true;
00311 }
00312
00313 $wgOut->addScriptFile( 'edit.js' );
00314
00315 if ( $wgUser->getOption( 'uselivepreview', false ) ) {
00316 $wgOut->includeJQuery();
00317 $wgOut->addScriptFile( 'preview.js' );
00318 }
00319
00320 $wgOut->addStyle( 'common/IE80Fixes.css', 'screen', 'IE 8' );
00321
00322 $permErrors = $this->getEditPermissionErrors();
00323 if ( $permErrors ) {
00324 wfDebug( __METHOD__ . ": User can't edit\n" );
00325 $this->readOnlyPage( $this->getContent( false ), true, $permErrors, 'edit' );
00326 wfProfileOut( __METHOD__ );
00327 return;
00328 } else {
00329 if ( $this->save ) {
00330 $this->formtype = 'save';
00331 } else if ( $this->preview ) {
00332 $this->formtype = 'preview';
00333 } else if ( $this->diff ) {
00334 $this->formtype = 'diff';
00335 } else { # First time through
00336 $this->firsttime = true;
00337 if ( $this->previewOnOpen() ) {
00338 $this->formtype = 'preview';
00339 } else {
00340 $this->formtype = 'initial';
00341 }
00342 }
00343 }
00344
00345
00346 if ( $wgRequest->getBool( 'redlink' ) && $this->mTitle->exists() ) {
00347 $wgOut->redirect( $this->mTitle->getFullURL() );
00348 }
00349
00350 wfProfileIn( __METHOD__."-business-end" );
00351
00352 $this->isConflict = false;
00353
00354 $this->isCssJsSubpage = $this->mTitle->isCssJsSubpage();
00355 $this->isCssSubpage = $this->mTitle->isCssSubpage();
00356 $this->isJsSubpage = $this->mTitle->isJsSubpage();
00357 $this->isValidCssJsSubpage = $this->mTitle->isValidCssJsSubpage();
00358
00359 # Show applicable editing introductions
00360 if ( $this->formtype == 'initial' || $this->firsttime )
00361 $this->showIntro();
00362
00363 if ( $this->mTitle->isTalkPage() ) {
00364 $wgOut->addWikiMsg( 'talkpagetext' );
00365 }
00366
00367 # Optional notices on a per-namespace and per-page basis
00368 $editnotice_ns = 'editnotice-'.$this->mTitle->getNamespace();
00369 if ( !wfEmptyMsg( $editnotice_ns, wfMsgForContent( $editnotice_ns ) ) ) {
00370 $wgOut->addWikiText( wfMsgForContent( $editnotice_ns ) );
00371 }
00372 if ( MWNamespace::hasSubpages( $this->mTitle->getNamespace() ) ) {
00373 $parts = explode( '/', $this->mTitle->getDBkey() );
00374 $editnotice_base = $editnotice_ns;
00375 while ( count( $parts ) > 0 ) {
00376 $editnotice_base .= '-'.array_shift( $parts );
00377 if ( !wfEmptyMsg( $editnotice_base, wfMsgForContent( $editnotice_base ) ) ) {
00378 $wgOut->addWikiText( wfMsgForContent( $editnotice_base ) );
00379 }
00380 }
00381 }
00382
00383 # Attempt submission here. This will check for edit conflicts,
00384 # and redundantly check for locked database, blocked IPs, etc.
00385 # that edit() already checked just in case someone tries to sneak
00386 # in the back door with a hand-edited submission URL.
00387
00388 if ( 'save' == $this->formtype ) {
00389 if ( !$this->attemptSave() ) {
00390 wfProfileOut( __METHOD__."-business-end" );
00391 wfProfileOut( __METHOD__ );
00392 return;
00393 }
00394 }
00395
00396 # First time through: get contents, set time for conflict
00397 # checking, etc.
00398 if ( 'initial' == $this->formtype || $this->firsttime ) {
00399 if ( $this->initialiseForm() === false ) {
00400 $this->noSuchSectionPage();
00401 wfProfileOut( __METHOD__."-business-end" );
00402 wfProfileOut( __METHOD__ );
00403 return;
00404 }
00405 if ( !$this->mTitle->getArticleId() )
00406 wfRunHooks( 'EditFormPreloadText', array( &$this->textbox1, &$this->mTitle ) );
00407 else
00408 wfRunHooks( 'EditFormInitialText', array( $this ) );
00409 }
00410
00411 $this->showEditForm();
00412 wfProfileOut( __METHOD__."-business-end" );
00413 wfProfileOut( __METHOD__ );
00414 }
00415
00416 protected function getEditPermissionErrors() {
00417 global $wgUser;
00418 $permErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser );
00419 # Can this title be created?
00420 if ( !$this->mTitle->exists() ) {
00421 $permErrors = array_merge( $permErrors,
00422 wfArrayDiff2( $this->mTitle->getUserPermissionsErrors( 'create', $wgUser ), $permErrors ) );
00423 }
00424 # Ignore some permissions errors when a user is just previewing/viewing diffs
00425 $remove = array();
00426 foreach( $permErrors as $error ) {
00427 if ( ( $this->preview || $this->diff ) &&
00428 ( $error[0] == 'blockedtext' || $error[0] == 'autoblockedtext' ) )
00429 {
00430 $remove[] = $error;
00431 }
00432 }
00433 $permErrors = wfArrayDiff2( $permErrors, $remove );
00434 return $permErrors;
00435 }
00436
00442 function readOnlyPage( $source = null, $protected = false, $reasons = array(), $action = null ) {
00443 global $wgRequest, $wgOut;
00444 if ( $wgRequest->getBool( 'redlink' ) ) {
00445
00446
00447
00448 $wgOut->redirect( $this->mTitle->getFullUrl() );
00449 } else {
00450 $wgOut->readOnlyPage( $source, $protected, $reasons, $action );
00451 }
00452 }
00453
00459 protected function previewOnOpen() {
00460 global $wgRequest, $wgUser, $wgPreviewOnOpenNamespaces;
00461 if ( $wgRequest->getVal( 'preview' ) == 'yes' ) {
00462
00463 return true;
00464 } elseif ( $wgRequest->getVal( 'preview' ) == 'no' ) {
00465
00466 return false;
00467 } elseif ( $this->section == 'new' ) {
00468
00469 return false;
00470 } elseif ( ( $wgRequest->getVal( 'preload' ) !== null || $this->mTitle->exists() ) && $wgUser->getOption( 'previewonfirst' ) ) {
00471
00472 return true;
00473 } elseif ( !$this->mTitle->exists() &&
00474 isset($wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()]) &&
00475 $wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()] )
00476 {
00477
00478 return true;
00479 } else {
00480 return false;
00481 }
00482 }
00483
00490 protected function isSectionEditSupported() {
00491 return true;
00492 }
00493
00503 protected function getActionURL( Title $title ) {
00504 return $title->getLocalURL( array( 'action' => $this->action ) );
00505 }
00506
00511 function importFormData( &$request ) {
00512 global $wgLang, $wgUser;
00513
00514 wfProfileIn( __METHOD__ );
00515
00516 # Section edit can come from either the form or a link
00517 $this->section = $request->getVal( 'wpSection', $request->getVal( 'section' ) );
00518
00519 if ( $request->wasPosted() ) {
00520 # These fields need to be checked for encoding.
00521 # Also remove trailing whitespace, but don't remove _initial_
00522 # whitespace from the text boxes. This may be significant formatting.
00523 $this->textbox1 = $this->safeUnicodeInput( $request, 'wpTextbox1' );
00524 if ( !$request->getCheck('wpTextbox2') ) {
00525
00526
00527
00528 wfProfileIn( get_class($this)."::importContentFormData" );
00529 $textbox1 = $this->importContentFormData( $request );
00530 if ( isset($textbox1) )
00531 $this->textbox1 = $textbox1;
00532 wfProfileOut( get_class($this)."::importContentFormData" );
00533 }
00534
00535 # Truncate for whole multibyte characters. +5 bytes for ellipsis
00536 $this->summary = $wgLang->truncate( $request->getText( 'wpSummary' ), 250, '' );
00537
00538 # Remove extra headings from summaries and new sections.
00539 $this->summary = preg_replace('/^\s*=+\s*(.*?)\s*=+\s*$/', '$1', $this->summary);
00540
00541 $this->edittime = $request->getVal( 'wpEdittime' );
00542 $this->starttime = $request->getVal( 'wpStarttime' );
00543
00544 $this->scrolltop = $request->getIntOrNull( 'wpScrolltop' );
00545
00546 if ( is_null( $this->edittime ) ) {
00547 # If the form is incomplete, force to preview.
00548 wfDebug( __METHOD__ . ": Form data appears to be incomplete\n" );
00549 wfDebug( "POST DATA: " . var_export( $_POST, true ) . "\n" );
00550 $this->preview = true;
00551 } else {
00552
00553 $this->preview = $request->getCheck( 'wpPreview' ) || $request->getCheck( 'wpLivePreview' );
00554 $this->diff = $request->getCheck( 'wpDiff' );
00555
00556
00557
00558 $this->mTriedSave = !$this->preview;
00559
00560 if ( $this->tokenOk( $request ) ) {
00561 # Some browsers will not report any submit button
00562 # if the user hits enter in the comment box.
00563 # The unmarked state will be assumed to be a save,
00564 # if the form seems otherwise complete.
00565 wfDebug( __METHOD__ . ": Passed token check.\n" );
00566 } else if ( $this->diff ) {
00567 # Failed token check, but only requested "Show Changes".
00568 wfDebug( __METHOD__ . ": Failed token check; Show Changes requested.\n" );
00569 } else {
00570 # Page might be a hack attempt posted from
00571 # an external site. Preview instead of saving.
00572 wfDebug( __METHOD__ . ": Failed token check; forcing preview\n" );
00573 $this->preview = true;
00574 }
00575 }
00576 $this->save = !$this->preview && !$this->diff;
00577 if ( !preg_match( '/^\d{14}$/', $this->edittime ) ) {
00578 $this->edittime = null;
00579 }
00580
00581 if ( !preg_match( '/^\d{14}$/', $this->starttime ) ) {
00582 $this->starttime = null;
00583 }
00584
00585 $this->recreate = $request->getCheck( 'wpRecreate' );
00586
00587 $this->minoredit = $request->getCheck( 'wpMinoredit' );
00588 $this->watchthis = $request->getCheck( 'wpWatchthis' );
00589
00590 # Don't force edit summaries when a user is editing their own user or talk page
00591 if ( ( $this->mTitle->mNamespace == NS_USER || $this->mTitle->mNamespace == NS_USER_TALK ) &&
00592 $this->mTitle->getText() == $wgUser->getName() )
00593 {
00594 $this->allowBlankSummary = true;
00595 } else {
00596 $this->allowBlankSummary = $request->getBool( 'wpIgnoreBlankSummary' ) || !$wgUser->getOption( 'forceeditsummary');
00597 }
00598
00599 $this->autoSumm = $request->getText( 'wpAutoSummary' );
00600 } else {
00601 # Not a posted form? Start with nothing.
00602 wfDebug( __METHOD__ . ": Not a posted form.\n" );
00603 $this->textbox1 = '';
00604 $this->summary = '';
00605 $this->edittime = '';
00606 $this->starttime = wfTimestampNow();
00607 $this->edit = false;
00608 $this->preview = false;
00609 $this->save = false;
00610 $this->diff = false;
00611 $this->minoredit = false;
00612 $this->watchthis = $request->getBool( 'watchthis', false );
00613 $this->recreate = false;
00614
00615 if ( $this->section == 'new' && $request->getVal( 'preloadtitle' ) ) {
00616 $this->summary = $request->getVal( 'preloadtitle' );
00617 }
00618 elseif ( $this->section != 'new' && $request->getVal( 'summary' ) ) {
00619 $this->summary = $request->getText( 'summary' );
00620 }
00621
00622 if ( $request->getVal( 'minor' ) ) {
00623 $this->minoredit = true;
00624 }
00625 }
00626
00627 $this->bot = $request->getBool( 'bot', true );
00628 $this->nosummary = $request->getBool( 'nosummary' );
00629
00630
00631 $this->oldid = $request->getInt( 'oldid' );
00632
00633 $this->live = $request->getCheck( 'live' );
00634 $this->editintro = $request->getText( 'editintro',
00635
00636 $this->section === 'new' ? 'MediaWiki:addsection-editintro' : '' );
00637
00638 wfProfileOut( __METHOD__ );
00639
00640
00641 wfRunHooks( 'EditPage::importFormData', array( $this, $request ) );
00642 }
00643
00652 protected function importContentFormData( &$request ) {
00653 return;
00654 }
00655
00663 function tokenOk( &$request ) {
00664 global $wgUser;
00665 $token = $request->getVal( 'wpEditToken' );
00666 $this->mTokenOk = $wgUser->matchEditToken( $token );
00667 $this->mTokenOkExceptSuffix = $wgUser->matchEditTokenNoSuffix( $token );
00668 return $this->mTokenOk;
00669 }
00670
00674 protected function showIntro() {
00675 global $wgOut, $wgUser;
00676 if ( $this->suppressIntro ) {
00677 return;
00678 }
00679
00680 $namespace = $this->mTitle->getNamespace();
00681
00682 if ( $namespace == NS_MEDIAWIKI ) {
00683 # Show a warning if editing an interface message
00684 $wgOut->wrapWikiMsg( "<div class='mw-editinginterface'>\n$1</div>", 'editinginterface' );
00685 }
00686
00687 # Show a warning message when someone creates/edits a user (talk) page but the user does not exist
00688 # Show log extract when the user is currently blocked
00689 if ( $namespace == NS_USER || $namespace == NS_USER_TALK ) {
00690 $parts = explode( '/', $this->mTitle->getText(), 2 );
00691 $username = $parts[0];
00692 $user = User::newFromName( $username, false );
00693 $ip = User::isIP( $username );
00694 if ( !$user->isLoggedIn() && !$ip ) { # User does not exist
00695 $wgOut->wrapWikiMsg( "<div class=\"mw-userpage-userdoesnotexist error\">\n$1</div>",
00696 array( 'userpage-userdoesnotexist', $username ) );
00697 } else if ( $user->isBlocked() ) { # Show log extract if the user is currently blocked
00698 LogEventsList::showLogExtract(
00699 $wgOut,
00700 'block',
00701 $user->getUserPage()->getPrefixedText(),
00702 '',
00703 array(
00704 'lim' => 1,
00705 'showIfEmpty' => false,
00706 'msgKey' => array(
00707 'blocked-notice-logextract',
00708 $user->getName() # Support GENDER in notice
00709 )
00710 )
00711 );
00712 }
00713 }
00714 # Try to add a custom edit intro, or use the standard one if this is not possible.
00715 if ( !$this->showCustomIntro() && !$this->mTitle->exists() ) {
00716 if ( $wgUser->isLoggedIn() ) {
00717 $wgOut->wrapWikiMsg( "<div class=\"mw-newarticletext\">\n$1</div>", 'newarticletext' );
00718 } else {
00719 $wgOut->wrapWikiMsg( "<div class=\"mw-newarticletextanon\">\n$1</div>", 'newarticletextanon' );
00720 }
00721 }
00722 # Give a notice if the user is editing a deleted/moved page...
00723 if ( !$this->mTitle->exists() ) {
00724 LogEventsList::showLogExtract( $wgOut, array( 'delete', 'move' ), $this->mTitle->getPrefixedText(),
00725 '', array( 'lim' => 10,
00726 'conds' => array( "log_action != 'revision'" ),
00727 'showIfEmpty' => false,
00728 'msgKey' => array( 'recreate-moveddeleted-warn') )
00729 );
00730 }
00731 }
00732
00738 protected function showCustomIntro() {
00739 if ( $this->editintro ) {
00740 $title = Title::newFromText( $this->editintro );
00741 if ( $title instanceof Title && $title->exists() && $title->userCanRead() ) {
00742 global $wgOut;
00743 $revision = Revision::newFromTitle( $title );
00744 $wgOut->addWikiTextTitleTidy( $revision->getText(), $this->mTitle );
00745 return true;
00746 } else {
00747 return false;
00748 }
00749 } else {
00750 return false;
00751 }
00752 }
00753
00758 function internalAttemptSave( &$result, $bot = false ) {
00759 global $wgFilterCallback, $wgUser, $wgOut, $wgParser;
00760 global $wgMaxArticleSize;
00761
00762 wfProfileIn( __METHOD__ );
00763 wfProfileIn( __METHOD__ . '-checks' );
00764
00765 if ( !wfRunHooks( 'EditPage::attemptSave', array( $this ) ) ) {
00766 wfDebug( "Hook 'EditPage::attemptSave' aborted article saving\n" );
00767 return self::AS_HOOK_ERROR;
00768 }
00769
00770 # Check image redirect
00771 if ( $this->mTitle->getNamespace() == NS_FILE &&
00772 Title::newFromRedirect( $this->textbox1 ) instanceof Title &&
00773 !$wgUser->isAllowed( 'upload' ) ) {
00774 if ( $wgUser->isAnon() ) {
00775 return self::AS_IMAGE_REDIRECT_ANON;
00776 } else {
00777 return self::AS_IMAGE_REDIRECT_LOGGED;
00778 }
00779 }
00780
00781 # Check for spam
00782 $match = self::matchSummarySpamRegex( $this->summary );
00783 if ( $match === false ) {
00784 $match = self::matchSpamRegex( $this->textbox1 );
00785 }
00786 if ( $match !== false ) {
00787 $result['spam'] = $match;
00788 $ip = wfGetIP();
00789 $pdbk = $this->mTitle->getPrefixedDBkey();
00790 $match = str_replace( "\n", '', $match );
00791 wfDebugLog( 'SpamRegex', "$ip spam regex hit [[$pdbk]]: \"$match\"" );
00792 wfProfileOut( __METHOD__ . '-checks' );
00793 wfProfileOut( __METHOD__ );
00794 return self::AS_SPAM_ERROR;
00795 }
00796 if ( $wgFilterCallback && $wgFilterCallback( $this->mTitle, $this->textbox1, $this->section, $this->hookError, $this->summary ) ) {
00797 # Error messages or other handling should be performed by the filter function
00798 wfProfileOut( __METHOD__ . '-checks' );
00799 wfProfileOut( __METHOD__ );
00800 return self::AS_FILTERING;
00801 }
00802 if ( !wfRunHooks( 'EditFilter', array( $this, $this->textbox1, $this->section, &$this->hookError, $this->summary ) ) ) {
00803 # Error messages etc. could be handled within the hook...
00804 wfProfileOut( __METHOD__ . '-checks' );
00805 wfProfileOut( __METHOD__ );
00806 return self::AS_HOOK_ERROR;
00807 } elseif ( $this->hookError != '' ) {
00808 # ...or the hook could be expecting us to produce an error
00809 wfProfileOut( __METHOD__ . '-checks' );
00810 wfProfileOut( __METHOD__ );
00811 return self::AS_HOOK_ERROR_EXPECTED;
00812 }
00813 if ( $wgUser->isBlockedFrom( $this->mTitle, false ) ) {
00814 # Check block state against master, thus 'false'.
00815 wfProfileOut( __METHOD__ . '-checks' );
00816 wfProfileOut( __METHOD__ );
00817 return self::AS_BLOCKED_PAGE_FOR_USER;
00818 }
00819 $this->kblength = (int)( strlen( $this->textbox1 ) / 1024 );
00820 if ( $this->kblength > $wgMaxArticleSize ) {
00821
00822 $this->tooBig = true;
00823 wfProfileOut( __METHOD__ . '-checks' );
00824 wfProfileOut( __METHOD__ );
00825 return self::AS_CONTENT_TOO_BIG;
00826 }
00827
00828 if ( !$wgUser->isAllowed( 'edit' ) ) {
00829 if ( $wgUser->isAnon() ) {
00830 wfProfileOut( __METHOD__ . '-checks' );
00831 wfProfileOut( __METHOD__ );
00832 return self::AS_READ_ONLY_PAGE_ANON;
00833 } else {
00834 wfProfileOut( __METHOD__ . '-checks' );
00835 wfProfileOut( __METHOD__ );
00836 return self::AS_READ_ONLY_PAGE_LOGGED;
00837 }
00838 }
00839
00840 if ( wfReadOnly() ) {
00841 wfProfileOut( __METHOD__ . '-checks' );
00842 wfProfileOut( __METHOD__ );
00843 return self::AS_READ_ONLY_PAGE;
00844 }
00845 if ( $wgUser->pingLimiter() ) {
00846 wfProfileOut( __METHOD__ . '-checks' );
00847 wfProfileOut( __METHOD__ );
00848 return self::AS_RATE_LIMITED;
00849 }
00850
00851 # If the article has been deleted while editing, don't save it without
00852 # confirmation
00853 if ( $this->wasDeletedSinceLastEdit() && !$this->recreate ) {
00854 wfProfileOut( __METHOD__ . '-checks' );
00855 wfProfileOut( __METHOD__ );
00856 return self::AS_ARTICLE_WAS_DELETED;
00857 }
00858
00859 wfProfileOut( __METHOD__ . '-checks' );
00860
00861 # If article is new, insert it.
00862 $aid = $this->mTitle->getArticleID( GAID_FOR_UPDATE );
00863 if ( 0 == $aid ) {
00864
00865 if ( !$this->mTitle->userCan( 'create' ) ) {
00866 wfDebug( __METHOD__ . ": no create permission\n" );
00867 wfProfileOut( __METHOD__ );
00868 return self::AS_NO_CREATE_PERMISSION;
00869 }
00870
00871 # Don't save a new article if it's blank.
00872 if ( $this->textbox1 == '' ) {
00873 wfProfileOut( __METHOD__ );
00874 return self::AS_BLANK_ARTICLE;
00875 }
00876
00877
00878 if ( !wfRunHooks( 'EditFilterMerged', array( $this, $this->textbox1, &$this->hookError, $this->summary ) ) ) {
00879 # Error messages etc. could be handled within the hook...
00880 wfProfileOut( __METHOD__ );
00881 return self::AS_HOOK_ERROR;
00882 } elseif ( $this->hookError != '' ) {
00883 # ...or the hook could be expecting us to produce an error
00884 wfProfileOut( __METHOD__ );
00885 return self::AS_HOOK_ERROR_EXPECTED;
00886 }
00887
00888 # Handle the user preference to force summaries here. Check if it's not a redirect.
00889 if ( !$this->allowBlankSummary && !Title::newFromRedirect( $this->textbox1 ) ) {
00890 if ( md5( $this->summary ) == $this->autoSumm ) {
00891 $this->missingSummary = true;
00892 wfProfileOut( __METHOD__ );
00893 return self::AS_SUMMARY_NEEDED;
00894 }
00895 }
00896
00897 $isComment = ( $this->section == 'new' );
00898
00899 $this->mArticle->insertNewArticle( $this->textbox1, $this->summary,
00900 $this->minoredit, $this->watchthis, false, $isComment, $bot );
00901
00902 wfProfileOut( __METHOD__ );
00903 return self::AS_SUCCESS_NEW_ARTICLE;
00904 }
00905
00906 # Article exists. Check for edit conflict.
00907
00908 $this->mArticle->clear(); # Force reload of dates, etc.
00909 $this->mArticle->forUpdate( true ); # Lock the article
00910
00911 wfDebug( "timestamp: {$this->mArticle->getTimestamp()}, edittime: {$this->edittime}\n" );
00912
00913 if ( $this->mArticle->getTimestamp() != $this->edittime ) {
00914 $this->isConflict = true;
00915 if ( $this->section == 'new' ) {
00916 if ( $this->mArticle->getUserText() == $wgUser->getName() &&
00917 $this->mArticle->getComment() == $this->summary ) {
00918
00919
00920
00921 wfDebug( __METHOD__ . ": duplicate new section submission; trigger edit conflict!\n" );
00922 } else {
00923
00924 $this->isConflict = false;
00925 wfDebug( __METHOD__ .": conflict suppressed; new section\n" );
00926 }
00927 }
00928 }
00929 $userid = $wgUser->getId();
00930
00931 # Suppress edit conflict with self, except for section edits where merging is required.
00932 if ( $this->isConflict && $this->section == '' && $this->userWasLastToEdit( $userid, $this->edittime ) ) {
00933 wfDebug( __METHOD__ . ": Suppressing edit conflict, same user.\n" );
00934 $this->isConflict = false;
00935 }
00936
00937 if ( $this->isConflict ) {
00938 wfDebug( __METHOD__ . ": conflict! getting section '$this->section' for time '$this->edittime' (article time '" .
00939 $this->mArticle->getTimestamp() . "')\n" );
00940 $text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $this->summary, $this->edittime );
00941 } else {
00942 wfDebug( __METHOD__ . ": getting section '$this->section'\n" );
00943 $text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $this->summary );
00944 }
00945 if ( is_null( $text ) ) {
00946 wfDebug( __METHOD__ . ": activating conflict; section replace failed.\n" );
00947 $this->isConflict = true;
00948 $text = $this->textbox1;
00949 } else if ( $this->isConflict ) {
00950 # Attempt merge
00951 if ( $this->mergeChangesInto( $text ) ) {
00952
00953 $this->isConflict = false;
00954 wfDebug( __METHOD__ . ": Suppressing edit conflict, successful merge.\n" );
00955 } else {
00956 $this->section = '';
00957 $this->textbox1 = $text;
00958 wfDebug( __METHOD__ . ": Keeping edit conflict, failed merge.\n" );
00959 }
00960 }
00961
00962 if ( $this->isConflict ) {
00963 wfProfileOut( __METHOD__ );
00964 return self::AS_CONFLICT_DETECTED;
00965 }
00966
00967 $oldtext = $this->mArticle->getContent();
00968
00969
00970 if ( !wfRunHooks( 'EditFilterMerged', array( $this, $text, &$this->hookError, $this->summary ) ) ) {
00971 # Error messages etc. could be handled within the hook...
00972 wfProfileOut( __METHOD__ );
00973 return self::AS_HOOK_ERROR;
00974 } elseif ( $this->hookError != '' ) {
00975 # ...or the hook could be expecting us to produce an error
00976 wfProfileOut( __METHOD__ );
00977 return self::AS_HOOK_ERROR_EXPECTED;
00978 }
00979
00980 # Handle the user preference to force summaries here, but not for null edits
00981 if ( $this->section != 'new' && !$this->allowBlankSummary && 0 != strcmp( $oldtext, $text )
00982 && !Title::newFromRedirect( $text ) ) # check if it's not a redirect
00983 {
00984 if ( md5( $this->summary ) == $this->autoSumm ) {
00985 $this->missingSummary = true;
00986 wfProfileOut( __METHOD__ );
00987 return self::AS_SUMMARY_NEEDED;
00988 }
00989 }
00990
00991 # And a similar thing for new sections
00992 if ( $this->section == 'new' && !$this->allowBlankSummary ) {
00993 if ( trim( $this->summary ) == '' ) {
00994 $this->missingSummary = true;
00995 wfProfileOut( __METHOD__ );
00996 return self::AS_SUMMARY_NEEDED;
00997 }
00998 }
00999
01000 # All's well
01001 wfProfileIn( __METHOD__ . '-sectionanchor' );
01002 $sectionanchor = '';
01003 if ( $this->section == 'new' ) {
01004 if ( $this->textbox1 == '' ) {
01005 $this->missingComment = true;
01006 wfProfileOut( __METHOD__ . '-sectionanchor' );
01007 wfProfileOut( __METHOD__ );
01008 return self::AS_TEXTBOX_EMPTY;
01009 }
01010 if ( $this->summary != '' ) {
01011 $sectionanchor = $wgParser->guessSectionNameFromWikiText( $this->summary );
01012 # This is a new section, so create a link to the new section
01013 # in the revision summary.
01014 $cleanSummary = $wgParser->stripSectionName( $this->summary );
01015 $this->summary = wfMsgForContent( 'newsectionsummary', $cleanSummary );
01016 }
01017 } elseif ( $this->section != '' ) {
01018 # Try to get a section anchor from the section source, redirect to edited section if header found
01019 # XXX: might be better to integrate this into Article::replaceSection
01020 # for duplicate heading checking and maybe parsing
01021 $hasmatch = preg_match( "/^ *([=]{1,6})(.*?)(\\1) *\\n/i", $this->textbox1, $matches );
01022 # we can't deal with anchors, includes, html etc in the header for now,
01023 # headline would need to be parsed to improve this
01024 if ( $hasmatch and strlen( $matches[2] ) > 0 ) {
01025 $sectionanchor = $wgParser->guessSectionNameFromWikiText( $matches[2] );
01026 }
01027 }
01028 wfProfileOut( __METHOD__ . '-sectionanchor' );
01029
01030
01031
01032
01033
01034 $this->textbox1 = $text;
01035 $this->section = '';
01036
01037
01038 $this->kblength = (int)( strlen( $text ) / 1024 );
01039 if ( $this->kblength > $wgMaxArticleSize ) {
01040 $this->tooBig = true;
01041 wfProfileOut( __METHOD__ );
01042 return self::AS_MAX_ARTICLE_SIZE_EXCEEDED;
01043 }
01044
01045 # update the article here
01046 if ( $this->mArticle->updateArticle( $text, $this->summary, $this->minoredit,
01047 $this->watchthis, $bot, $sectionanchor ) )
01048 {
01049 wfProfileOut( __METHOD__ );
01050 return self::AS_SUCCESS_UPDATE;
01051 } else {
01052 $this->isConflict = true;
01053 }
01054 wfProfileOut( __METHOD__ );
01055 return self::AS_END;
01056 }
01057
01063 protected function userWasLastToEdit( $id, $edittime ) {
01064 if( !$id ) return false;
01065 $dbw = wfGetDB( DB_MASTER );
01066 $res = $dbw->select( 'revision',
01067 'rev_user',
01068 array(
01069 'rev_page' => $this->mArticle->getId(),
01070 'rev_timestamp > '.$dbw->addQuotes( $dbw->timestamp($edittime) )
01071 ),
01072 __METHOD__,
01073 array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 50 ) );
01074 while( $row = $res->fetchObject() ) {
01075 if( $row->rev_user != $id ) {
01076 return false;
01077 }
01078 }
01079 return true;
01080 }
01081
01086 public static function matchSpamRegex( $text ) {
01087 global $wgSpamRegex;
01088
01089 $regexes = (array)$wgSpamRegex;
01090 return self::matchSpamRegexInternal( $text, $regexes );
01091 }
01092
01097 public static function matchSummarySpamRegex( $text ) {
01098 global $wgSummarySpamRegex;
01099 $regexes = (array)$wgSummarySpamRegex;
01100 return self::matchSpamRegexInternal( $text, $regexes );
01101 }
01102
01103 protected static function matchSpamRegexInternal( $text, $regexes ) {
01104 foreach( $regexes as $regex ) {
01105 $matches = array();
01106 if( preg_match( $regex, $text, $matches ) ) {
01107 return $matches[0];
01108 }
01109 }
01110 return false;
01111 }
01112
01118 function initialiseForm() {
01119 global $wgUser;
01120 $this->edittime = $this->mArticle->getTimestamp();
01121 $this->textbox1 = $this->getContent( false );
01122
01123 # Sort out the "watch" checkbox
01124 if ( $wgUser->getOption( 'watchdefault' ) ) {
01125 # Watch all edits
01126 $this->watchthis = true;
01127 } elseif ( $wgUser->getOption( 'watchcreations' ) && !$this->mTitle->exists() ) {
01128 # Watch creations
01129 $this->watchthis = true;
01130 } elseif ( $this->mTitle->userIsWatching() ) {
01131 # Already watched
01132 $this->watchthis = true;
01133 }
01134 if ( $wgUser->getOption( 'minordefault' ) ) $this->minoredit = true;
01135 if ( $this->textbox1 === false ) return false;
01136 wfProxyCheck();
01137 return true;
01138 }
01139
01140 function setHeaders() {
01141 global $wgOut, $wgTitle;
01142 $wgOut->setRobotPolicy( 'noindex,nofollow' );
01143 if ( $this->formtype == 'preview' ) {
01144 $wgOut->setPageTitleActionText( wfMsg( 'preview' ) );
01145 }
01146 if ( $this->isConflict ) {
01147 $wgOut->setPageTitle( wfMsg( 'editconflict', $wgTitle->getPrefixedText() ) );
01148 } elseif ( $this->section != '' ) {
01149 $msg = $this->section == 'new' ? 'editingcomment' : 'editingsection';
01150 $wgOut->setPageTitle( wfMsg( $msg, $wgTitle->getPrefixedText() ) );
01151 } else {
01152 # Use the title defined by DISPLAYTITLE magic word when present
01153 if ( isset( $this->mParserOutput )
01154 && ( $dt = $this->mParserOutput->getDisplayTitle() ) !== false ) {
01155 $title = $dt;
01156 } else {
01157 $title = $wgTitle->getPrefixedText();
01158 }
01159 $wgOut->setPageTitle( wfMsg( 'editing', $title ) );
01160 }
01161 }
01162
01169 function showEditForm( $formCallback=null ) {
01170 global $wgOut, $wgUser, $wgTitle;
01171
01172 # If $wgTitle is null, that means we're in API mode.
01173 # Some hook probably called this function without checking
01174 # for is_null($wgTitle) first. Bail out right here so we don't
01175 # do lots of work just to discard it right after.
01176 if ( is_null( $wgTitle ) )
01177 return;
01178
01179 wfProfileIn( __METHOD__ );
01180
01181 $sk = $wgUser->getSkin();
01182
01183 #need to parse the preview early so that we know which templates are used,
01184 #otherwise users with "show preview after edit box" will get a blank list
01185 #we parse this near the beginning so that setHeaders can do the title
01186 #setting work instead of leaving it in getPreviewText
01187 $previewOutput = '';
01188 if ( $this->formtype == 'preview' ) {
01189 $previewOutput = $this->getPreviewText();
01190 }
01191
01192 wfRunHooks( 'EditPage::showEditForm:initial', array( &$this ) );
01193
01194 $this->setHeaders();
01195
01196 # Enabled article-related sidebar, toplinks, etc.
01197 $wgOut->setArticleRelated( true );
01198
01199 if ( $this->showHeader() === false )
01200 return;
01201
01202 $action = htmlspecialchars($this->getActionURL($wgTitle));
01203
01204 if ( $wgUser->getOption( 'showtoolbar' ) and !$this->isCssJsSubpage ) {
01205 # prepare toolbar for edit buttons
01206 $toolbar = EditPage::getEditToolbar();
01207 } else {
01208 $toolbar = '';
01209 }
01210
01211
01212 $wgOut->addHTML( $this->editFormPageTop );
01213
01214 if ( $wgUser->getOption( 'previewontop' ) ) {
01215 $this->displayPreviewArea( $previewOutput, true );
01216 }
01217
01218 $wgOut->addHTML( $this->editFormTextTop );
01219
01220 $templates = $this->getTemplates();
01221 $formattedtemplates = $sk->formatTemplates( $templates, $this->preview, $this->section != '');
01222
01223 $hiddencats = $this->mArticle->getHiddenCategories();
01224 $formattedhiddencats = $sk->formatHiddenCategories( $hiddencats );
01225
01226 if ( $this->wasDeletedSinceLastEdit() && 'save' != $this->formtype ) {
01227 $wgOut->wrapWikiMsg(
01228 "<div class='error mw-deleted-while-editing'>\n$1</div>",
01229 'deletedwhileediting' );
01230 } elseif ( $this->wasDeletedSinceLastEdit() ) {
01231
01232
01233 $toolbar = '';
01234
01235 }
01236 $wgOut->addHTML( <<<HTML
01237 {$toolbar}
01238 <form id="editform" name="editform" method="post" action="$action" enctype="multipart/form-data">
01239 HTML
01240 );
01241
01242 if ( is_callable( $formCallback ) ) {
01243 call_user_func_array( $formCallback, array( &$wgOut ) );
01244 }
01245
01246 wfRunHooks( 'EditPage::showEditForm:fields', array( &$this, &$wgOut ) );
01247
01248
01249 $this->showFormBeforeText();
01250
01251 if ( $this->wasDeletedSinceLastEdit() && 'save' == $this->formtype ) {
01252 $wgOut->addHTML(
01253 '<div class="mw-confirm-recreate">' .
01254 $wgOut->parse( wfMsg( 'confirmrecreate', $this->lastDelete->user_name , $this->lastDelete->log_comment ) ) .
01255 Xml::checkLabel( wfMsg( 'recreate' ), 'wpRecreate', 'wpRecreate', false,
01256 array( 'title' => $sk->titleAttrib( 'recreate' ), 'tabindex' => 1, 'id' => 'wpRecreate' )
01257 ) .
01258 '</div>'
01259 );
01260 }
01261
01262 # If a blank edit summary was previously provided, and the appropriate
01263 # user preference is active, pass a hidden tag as wpIgnoreBlankSummary. This will stop the
01264 # user being bounced back more than once in the event that a summary
01265 # is not required.
01266 #####
01267 # For a bit more sophisticated detection of blank summaries, hash the
01268 # automatic one and pass that in the hidden field wpAutoSummary.
01269 if ( $this->missingSummary ||
01270 ( $this->section == 'new' && $this->nosummary ) )
01271 $wgOut->addHTML( Xml::hidden( 'wpIgnoreBlankSummary', true ) );
01272 $autosumm = $this->autoSumm ? $this->autoSumm : md5( $this->summary );
01273 $wgOut->addHTML( Xml::hidden( 'wpAutoSummary', $autosumm ) );
01274
01275 $wgOut->addHTML( Xml::hidden( 'oldid', $this->mArticle->getOldID() ) );
01276
01277 if ( $this->section == 'new' ) {
01278 $this->showSummaryInput( true, $this->summary );
01279 $wgOut->addHTML( $this->getSummaryPreview( true, $this->summary ) );
01280 }
01281
01282 $wgOut->addHTML( $this->editFormTextBeforeContent );
01283
01284 if ( $this->isConflict ) {
01285
01286
01287
01288
01289 $this->showTextbox1( null, $this->getContent() );
01290 } else {
01291 $this->showContentForm();
01292 }
01293
01294 $wgOut->addHTML( $this->editFormTextAfterContent );
01295
01296 $wgOut->addWikiText( $this->getCopywarn() );
01297 if ( isset($this->editFormTextAfterWarn) && $this->editFormTextAfterWarn !== '' )
01298 $wgOut->addHTML( $this->editFormTextAfterWarn );
01299
01300 $this->showStandardInputs();
01301
01302 $this->showFormAfterText();
01303
01304 $this->showTosSummary();
01305 $this->showEditTools();
01306
01307 $wgOut->addHTML( <<<HTML
01308 {$this->editFormTextAfterTools}
01309 <div class='templatesUsed'>
01310 {$formattedtemplates}
01311 </div>
01312 <div class='hiddencats'>
01313 {$formattedhiddencats}
01314 </div>
01315 HTML
01316 );
01317
01318 if ( $this->isConflict )
01319 $this->showConflict();
01320
01321 $wgOut->addHTML( $this->editFormTextBottom );
01322 $wgOut->addHTML( "</form>\n" );
01323 if ( !$wgUser->getOption( 'previewontop' ) ) {
01324 $this->displayPreviewArea( $previewOutput, false );
01325 }
01326
01327 wfProfileOut( __METHOD__ );
01328 }
01329
01330 protected function showHeader() {
01331 global $wgOut, $wgUser, $wgTitle, $wgMaxArticleSize, $wgLang;
01332 if ( $this->isConflict ) {
01333 $wgOut->wrapWikiMsg( "<div class='mw-explainconflict'>\n$1</div>", 'explainconflict' );
01334 $this->edittime = $this->mArticle->getTimestamp();
01335 } else {
01336 if ( $this->section != '' && !$this->isSectionEditSupported() ) {
01337
01338
01339
01340 $wgOut->showErrorPage( 'sectioneditnotsupported-title', 'sectioneditnotsupported-text' );
01341 return false;
01342 }
01343
01344 if ( $this->section != '' && $this->section != 'new' ) {
01345 $matches = array();
01346 if ( !$this->summary && !$this->preview && !$this->diff ) {
01347 preg_match( "/^(=+)(.+)\\1/mi", $this->textbox1, $matches );
01348 if ( !empty( $matches[2] ) ) {
01349 global $wgParser;
01350 $this->summary = "/* " .
01351 $wgParser->stripSectionName(trim($matches[2])) .
01352 " */ ";
01353 }
01354 }
01355 }
01356
01357 if ( $this->missingComment ) {
01358 $wgOut->wrapWikiMsg( "<div id='mw-missingcommenttext'>\n$1</div>", 'missingcommenttext' );
01359 }
01360
01361 if ( $this->missingSummary && $this->section != 'new' ) {
01362 $wgOut->wrapWikiMsg( "<div id='mw-missingsummary'>\n$1</div>", 'missingsummary' );
01363 }
01364
01365 if ( $this->missingSummary && $this->section == 'new' ) {
01366 $wgOut->wrapWikiMsg( "<div id='mw-missingcommentheader'>\n$1</div>", 'missingcommentheader' );
01367 }
01368
01369 if ( $this->hookError !== '' ) {
01370 $wgOut->addWikiText( $this->hookError );
01371 }
01372
01373 if ( !$this->checkUnicodeCompliantBrowser() ) {
01374 $wgOut->addWikiMsg( 'nonunicodebrowser' );
01375 }
01376
01377 if ( isset( $this->mArticle ) && isset( $this->mArticle->mRevision ) ) {
01378
01379
01380 if ( !$this->mArticle->mRevision->userCan( Revision::DELETED_TEXT ) ) {
01381 $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-permission' );
01382 } else if ( $this->mArticle->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
01383 $wgOut->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1</div>\n", 'rev-deleted-text-view' );
01384 }
01385
01386 if ( !$this->mArticle->mRevision->isCurrent() ) {
01387 $this->mArticle->setOldSubtitle( $this->mArticle->mRevision->getId() );
01388 $wgOut->addWikiMsg( 'editingold' );
01389 }
01390 }
01391 }
01392
01393 if ( wfReadOnly() ) {
01394 $wgOut->wrapWikiMsg( "<div id=\"mw-read-only-warning\">\n$1\n</div>", array( 'readonlywarning', wfReadOnlyReason() ) );
01395 } elseif ( $wgUser->isAnon() && $this->formtype != 'preview' ) {
01396 $wgOut->wrapWikiMsg( "<div id=\"mw-anon-edit-warning\">\n$1</div>", 'anoneditwarning' );
01397 } else {
01398 if ( $this->isCssJsSubpage ) {
01399 # Check the skin exists
01400 if ( !$this->isValidCssJsSubpage ) {
01401 $wgOut->addWikiMsg( 'userinvalidcssjstitle', $wgTitle->getSkinFromCssJsSubpage() );
01402 }
01403 if ( $this->formtype !== 'preview' ) {
01404 if ( $this->isCssSubpage )
01405 $wgOut->addWikiMsg( 'usercssyoucanpreview' );
01406 if ( $this->isJsSubpage )
01407 $wgOut->addWikiMsg( 'userjsyoucanpreview' );
01408 }
01409 }
01410 }
01411
01412 if ( $this->mTitle->getNamespace() != NS_MEDIAWIKI && $this->mTitle->isProtected( 'edit' ) ) {
01413 # Is the title semi-protected?
01414 if ( $this->mTitle->isSemiProtected() ) {
01415 $noticeMsg = 'semiprotectedpagewarning';
01416 } else {
01417 # Then it must be protected based on static groups (regular)
01418 $noticeMsg = 'protectedpagewarning';
01419 }
01420 LogEventsList::showLogExtract( $wgOut, 'protect', $this->mTitle->getPrefixedText(), '',
01421 array( 'lim' => 1, 'msgKey' => array( $noticeMsg ) ) );
01422 }
01423 if ( $this->mTitle->isCascadeProtected() ) {
01424 # Is this page under cascading protection from some source pages?
01425 list($cascadeSources, ) = $this->mTitle->getCascadeProtectionSources();
01426 $notice = "<div class='mw-cascadeprotectedwarning'>\n$1\n";
01427 $cascadeSourcesCount = count( $cascadeSources );
01428 if ( $cascadeSourcesCount > 0 ) {
01429 # Explain, and list the titles responsible
01430 foreach( $cascadeSources as $page ) {
01431 $notice .= '* [[:' . $page->getPrefixedText() . "]]\n";
01432 }
01433 }
01434 $notice .= '</div>';
01435 $wgOut->wrapWikiMsg( $notice, array( 'cascadeprotectedwarning', $cascadeSourcesCount ) );
01436 }
01437 if ( !$this->mTitle->exists() && $this->mTitle->getRestrictions( 'create' ) ) {
01438 LogEventsList::showLogExtract( $wgOut, 'protect', $this->mTitle->getPrefixedText(), '',
01439 array( 'lim' => 1,
01440 'showIfEmpty' => false,
01441 'msgKey' => array( 'titleprotectedwarning' ),
01442 'wrap' => "<div class=\"mw-titleprotectedwarning\">\n$1</div>" ) );
01443 }
01444
01445 if ( $this->kblength === false ) {
01446 $this->kblength = (int)( strlen( $this->textbox1 ) / 1024 );
01447 }
01448
01449 if ( $this->tooBig || $this->kblength > $wgMaxArticleSize ) {
01450 $wgOut->addHTML( "<div class='error' id='mw-edit-longpageerror'>\n" );
01451 $wgOut->addWikiMsg( 'longpageerror', $wgLang->formatNum( $this->kblength ), $wgLang->formatNum( $wgMaxArticleSize ) );
01452 $wgOut->addHTML( "</div>\n" );
01453 } elseif ( $this->kblength > 29 ) {
01454 $wgOut->addHTML( "<div id='mw-edit-longpagewarning'>\n" );
01455 $wgOut->addWikiMsg( 'longpagewarning', $wgLang->formatNum( $this->kblength ) );
01456 $wgOut->addHTML( "</div>\n" );
01457 }
01458 }
01459
01474 function getSummaryInput($summary = "", $labelText = null, $inputAttrs = null, $spanLabelAttrs = null) {
01475 $inputAttrs = ( is_array($inputAttrs) ? $inputAttrs : array() ) + array(
01476 'id' => 'wpSummary',
01477 'maxlength' => '200',
01478 'tabindex' => '1',
01479 'size' => 60,
01480 'spellcheck' => 'true',
01481 );
01482
01483 $spanLabelAttrs = ( is_array($spanLabelAttrs) ? $spanLabelAttrs : array() ) + array(
01484 'class' => $this->missingSummary ? 'mw-summarymissed' : 'mw-summary',
01485 'id' => "wpSummaryLabel"
01486 );
01487
01488 $label = null;
01489 if ( $labelText ) {
01490 $label = Xml::tags( 'label', $inputAttrs['id'] ? array( 'for' => $inputAttrs['id'] ) : null, $labelText );
01491 $label = Xml::tags( 'span', $spanLabelAttrs, $label );
01492 }
01493
01494 $input = Html::input( 'wpSummary', $summary, 'text', $inputAttrs );
01495
01496 return array( $label, $input );
01497 }
01498
01506 protected function showSummaryInput( $isSubjectPreview, $summary = "" ) {
01507 global $wgOut, $wgContLang;
01508 # Add a class if 'missingsummary' is triggered to allow styling of the summary line
01509 $summaryClass = $this->missingSummary ? 'mw-summarymissed' : 'mw-summary';
01510 if ( $isSubjectPreview ) {
01511 if ( $this->nosummary )
01512 return;
01513 } else {
01514 if ( !$this->mShowSummaryField )
01515 return;
01516 }
01517 $summary = $wgContLang->recodeForEdit( $summary );
01518 $labelText = wfMsgExt( $isSubjectPreview ? 'subject' : 'summary', 'parseinline' );
01519 list($label, $input) = $this->getSummaryInput($summary, $labelText, array( 'class' => $summaryClass ), array());
01520 $wgOut->addHTML("{$label} {$input}");
01521 }
01522
01530 protected function getSummaryPreview( $isSubjectPreview, $summary = "" ) {
01531 if ( !$summary || ( !$this->preview && !$this->diff ) )
01532 return "";
01533
01534 global $wgParser, $wgUser;
01535 $sk = $wgUser->getSkin();
01536
01537 if ( $isSubjectPreview )
01538 $summary = wfMsgForContent( 'newsectionsummary', $wgParser->stripSectionName( $summary ) );
01539
01540 $summary = wfMsgExt( 'subject-preview', 'parseinline' ) . $sk->commentBlock( $summary, $this->mTitle, !!$isSubjectPreview );
01541 return Xml::tags( 'div', array( 'class' => 'mw-summary-preview' ), $summary );
01542 }
01543
01544 protected function showFormBeforeText() {
01545 global $wgOut;
01546 $section = htmlspecialchars( $this->section );
01547 $wgOut->addHTML( <<<INPUTS
01548 <input type='hidden' value="{$section}" name="wpSection" />
01549 <input type='hidden' value="{$this->starttime}" name="wpStarttime" />
01550 <input type='hidden' value="{$this->edittime}" name="wpEdittime" />
01551 <input type='hidden' value="{$this->scrolltop}" name="wpScrolltop" id="wpScrolltop" />
01552
01553 INPUTS
01554 );
01555 if ( !$this->checkUnicodeCompliantBrowser() )
01556 $wgOut->addHTML(Xml::hidden( 'safemode', '1' ));
01557 }
01558
01559 protected function showFormAfterText() {
01560 global $wgOut, $wgUser;
01573 $wgOut->addHTML( "\n" . Xml::hidden( "wpEditToken", $wgUser->editToken() ) . "\n" );
01574 }
01575
01586 protected function showContentForm() {
01587 $this->showTextbox1();
01588 }
01589
01598 protected function showTextbox1($customAttribs = null, $textoverride = null) {
01599 $classes = array();
01600 if ( $this->mTitle->getNamespace() != NS_MEDIAWIKI && $this->mTitle->isProtected( 'edit' ) ) {
01601 # Is the title semi-protected?
01602 if ( $this->mTitle->isSemiProtected() ) {
01603 $classes[] = 'mw-textarea-sprotected';
01604 } else {
01605 # Then it must be protected based on static groups (regular)
01606 $classes[] = 'mw-textarea-protected';
01607 }
01608 }
01609 $attribs = array( 'tabindex' => 1 );
01610 if ( is_array($customAttribs) )
01611 $attribs += $customAttribs;
01612
01613 if ( $this->wasDeletedSinceLastEdit() )
01614 $attribs['type'] = 'hidden';
01615 if ( !empty( $classes ) ) {
01616 if ( isset($attribs['class']) )
01617 $classes[] = $attribs['class'];
01618 $attribs['class'] = implode( ' ', $classes );
01619 }
01620
01621 $this->showTextbox( isset($textoverride) ? $textoverride : $this->textbox1, 'wpTextbox1', $attribs );
01622 }
01623
01624 protected function showTextbox2() {
01625 $this->showTextbox( $this->textbox2, 'wpTextbox2', array( 'tabindex' => 6 ) );
01626 }
01627
01628 protected function showTextbox( $content, $name, $customAttribs = array() ) {
01629 global $wgOut, $wgUser;
01630
01631 $wikitext = $this->safeUnicodeOutput( $content );
01632 if ( $wikitext !== '' ) {
01633
01634
01635
01636
01637 $wikitext .= "\n";
01638 }
01639
01640 $attribs = $customAttribs + array(
01641 'accesskey' => ',',
01642 'id' => $name,
01643 'cols' => $wgUser->getIntOption( 'cols' ),
01644 'rows' => $wgUser->getIntOption( 'rows' ),
01645 'style' => ''
01646 );
01647
01648 if ( $wgUser->getOption( 'editwidth' ) )
01649 $attribs['style'] .= 'width: 100%';
01650
01651 $wgOut->addHTML( Html::textarea( $name, $wikitext, $attribs ) );
01652 }
01653
01654 protected function displayPreviewArea( $previewOutput, $isOnTop = false ) {
01655 global $wgOut;
01656 $classes = array();
01657 if ( $isOnTop )
01658 $classes[] = 'ontop';
01659
01660 $attribs = array( 'id' => 'wikiPreview', 'class' => implode( ' ', $classes ) );
01661
01662 if ( $this->formtype != 'preview' )
01663 $attribs['style'] = 'display: none;';
01664
01665 $wgOut->addHTML( Xml::openElement( 'div', $attribs ) );
01666
01667 if ( $this->formtype == 'preview' ) {
01668 $this->showPreview( $previewOutput );
01669 }
01670
01671 $wgOut->addHTML( '</div>' );
01672
01673 if ( $this->formtype == 'diff') {
01674 $this->showDiff();
01675 }
01676 }
01677
01684 protected function showPreview( $text ) {
01685 global $wgOut;
01686 if ( $this->mTitle->getNamespace() == NS_CATEGORY) {
01687 $this->mArticle->openShowCategory();
01688 }
01689 # This hook seems slightly odd here, but makes things more
01690 # consistent for extensions.
01691 wfRunHooks( 'OutputPageBeforeHTML',array( &$wgOut, &$text ) );
01692 $wgOut->addHTML( $text );
01693 if ( $this->mTitle->getNamespace() == NS_CATEGORY ) {
01694 $this->mArticle->closeShowCategory();
01695 }
01696 }
01697
01698 protected function showTosSummary() {
01699 $msg = 'editpage-tos-summary';
01700
01701
01702
01703
01704
01705
01706 wfRunHooks( 'EditPageTosSummary', array( $this->mTitle, &$msg ) );
01707 $text = wfMsg( $msg );
01708 if( !wfEmptyMsg( $msg, $text ) && $text !== '-' ) {
01709 global $wgOut;
01710 $wgOut->addHTML( '<div class="mw-tos-summary">' );
01711 $wgOut->addWikiMsgArray( $msg, array() );
01712 $wgOut->addHTML( '</div>' );
01713 }
01714 }
01715
01716 protected function showEditTools() {
01717 global $wgOut;
01718 $wgOut->addHTML( '<div class="mw-editTools">' );
01719 $wgOut->addWikiMsgArray( 'edittools', array(), array( 'content' ) );
01720 $wgOut->addHTML( '</div>' );
01721 }
01722
01723 protected function getCopywarn() {
01724 global $wgRightsText;
01725 if ( $wgRightsText ) {
01726 $copywarnMsg = array( 'copyrightwarning',
01727 '[[' . wfMsgForContent( 'copyrightpage' ) . ']]',
01728 $wgRightsText );
01729 } else {
01730 $copywarnMsg = array( 'copyrightwarning2',
01731 '[[' . wfMsgForContent( 'copyrightpage' ) . ']]' );
01732 }
01733
01734 wfRunHooks( 'EditPageCopyrightWarning', array( $this->mTitle, &$copywarnMsg ) );
01735
01736 return "<div id=\"editpage-copywarn\">\n" . call_user_func_array("wfMsgNoTrans", $copywarnMsg) . "\n</div>";
01737 }
01738
01739 protected function showStandardInputs( &$tabindex = 2 ) {
01740 global $wgOut, $wgUser;
01741 $wgOut->addHTML( "<div class='editOptions'>\n" );
01742
01743 if ( $this->section != 'new' ) {
01744 $this->showSummaryInput( false, $this->summary );
01745 $wgOut->addHTML( $this->getSummaryPreview( false, $this->summary ) );
01746 }
01747
01748 $checkboxes = $this->getCheckboxes( $tabindex, $wgUser->getSkin(),
01749 array( 'minor' => $this->minoredit, 'watch' => $this->watchthis ) );
01750 $wgOut->addHTML( "<div class='editCheckboxes'>" . implode( $checkboxes, "\n" ) . "</div>\n" );
01751 $wgOut->addHTML( "<div class='editButtons'>\n" );
01752 $wgOut->addHTML( implode( $this->getEditButtons( $tabindex ), "\n" ) . "\n" );
01753
01754 $cancel = $this->getCancelLink();
01755 $separator = wfMsgExt( 'pipe-separator' , 'escapenoentities' );
01756 $edithelpurl = Skin::makeInternalOrExternalUrl( wfMsgForContent( 'edithelppage' ) );
01757 $edithelp = '<a target="helpwindow" href="'.$edithelpurl.'">'.
01758 htmlspecialchars( wfMsg( 'edithelp' ) ).'</a> '.
01759 htmlspecialchars( wfMsg( 'newwindow' ) );
01760 $wgOut->addHTML( " <span class='editHelp'>{$cancel}{$separator}{$edithelp}</span>\n" );
01761 $wgOut->addHTML( "</div><!-- editButtons -->\n</div><!-- editOptions -->\n" );
01762 }
01763
01764
01765
01766
01767
01768 protected function showConflict() {
01769 global $wgOut;
01770 $this->textbox2 = $this->textbox1;
01771 $this->textbox1 = $this->getContent();
01772 if ( wfRunHooks( 'EditPageBeforeConflictDiff', array( &$this, &$wgOut ) ) ) {
01773 $wgOut->wrapWikiMsg( '<h2>$1</h2>', "yourdiff" );
01774
01775 $de = new DifferenceEngine( $this->mTitle );
01776 $de->setText( $this->textbox2, $this->textbox1 );
01777 $de->showDiff( wfMsg( "yourtext" ), wfMsg( "storedversion" ) );
01778
01779 $wgOut->wrapWikiMsg( '<h2>$1</h2>', "yourtext" );
01780 $this->showTextbox2();
01781 }
01782 }
01783
01784 protected function getLastDelete() {
01785 $dbr = wfGetDB( DB_SLAVE );
01786 $data = $dbr->selectRow(
01787 array( 'logging', 'user' ),
01788 array( 'log_type',
01789 'log_action',
01790 'log_timestamp',
01791 'log_user',
01792 'log_namespace',
01793 'log_title',
01794 'log_comment',
01795 'log_params',
01796 'log_deleted',
01797 'user_name' ),
01798 array( 'log_namespace' => $this->mTitle->getNamespace(),
01799 'log_title' => $this->mTitle->getDBkey(),
01800 'log_type' => 'delete',
01801 'log_action' => 'delete',
01802 'user_id=log_user' ),
01803 __METHOD__,
01804 array( 'LIMIT' => 1, 'ORDER BY' => 'log_timestamp DESC' )
01805 );
01806
01807 if( is_object( $data ) ) {
01808 if( $data->log_deleted & LogPage::DELETED_USER )
01809 $data->user_name = wfMsgHtml( 'rev-deleted-user' );
01810 if( $data->log_deleted & LogPage::DELETED_COMMENT )
01811 $data->log_comment = wfMsgHtml( 'rev-deleted-comment' );
01812 }
01813 return $data;
01814 }
01815
01820 function getPreviewText() {
01821 global $wgOut, $wgUser, $wgTitle, $wgParser, $wgLang, $wgContLang, $wgMessageCache;
01822
01823 wfProfileIn( __METHOD__ );
01824
01825 if ( $this->mTriedSave && !$this->mTokenOk ) {
01826 if ( $this->mTokenOkExceptSuffix ) {
01827 $note = wfMsg( 'token_suffix_mismatch' );
01828 } else {
01829 $note = wfMsg( 'session_fail_preview' );
01830 }
01831 } else {
01832 $note = wfMsg( 'previewnote' );
01833 }
01834
01835 $parserOptions = ParserOptions::newFromUser( $wgUser );
01836 $parserOptions->setEditSection( false );
01837 $parserOptions->setIsPreview( true );
01838 $parserOptions->setIsSectionPreview( !is_null($this->section) && $this->section !== '' );
01839
01840 global $wgRawHtml;
01841 if ( $wgRawHtml && !$this->mTokenOk ) {
01842
01843
01844 return $wgOut->parse( "<div class='previewnote'>" .
01845 wfMsg( 'session_fail_preview_html' ) . "</div>" );
01846 }
01847
01848 # don't parse user css/js, show message about preview
01849 # XXX: stupid php bug won't let us use $wgTitle->isCssJsSubpage() here
01850
01851 if ( $this->isCssJsSubpage ) {
01852 if (preg_match( "/\\.css$/", $this->mTitle->getText() ) ) {
01853 $previewtext = wfMsg( 'usercsspreview' );
01854 } else if (preg_match( "/\\.js$/", $this->mTitle->getText() ) ) {
01855 $previewtext = wfMsg( 'userjspreview' );
01856 }
01857 $parserOptions->setTidy( true );
01858 $parserOutput = $wgParser->parse( $previewtext, $this->mTitle, $parserOptions );
01859 $previewHTML = $parserOutput->mText;
01860 } elseif ( $rt = Title::newFromRedirectArray( $this->textbox1 ) ) {
01861 $previewHTML = $this->mArticle->viewRedirect( $rt, false );
01862 } else {
01863 $toparse = $this->textbox1;
01864
01865 # If we're adding a comment, we need to show the
01866 # summary as the headline
01867 if ( $this->section == "new" && $this->summary != "" ) {
01868 $toparse = "== {$this->summary} ==\n\n" . $toparse;
01869 }
01870
01871 wfRunHooks( 'EditPageGetPreviewText', array( $this, &$toparse ) );
01872
01873
01874 if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI ) {
01875 list( , $lang ) = $wgMessageCache->figureMessage( $this->mTitle->getText() );
01876 $obj = wfGetLangObj( $lang );
01877 $parserOptions->setTargetLanguage( $obj );
01878 }
01879
01880
01881 $parserOptions->setTidy( true );
01882 $parserOptions->enableLimitReport();
01883 $parserOutput = $wgParser->parse( $this->mArticle->preSaveTransform( $toparse ),
01884 $this->mTitle, $parserOptions );
01885
01886 $previewHTML = $parserOutput->getText();
01887 $this->mParserOutput = $parserOutput;
01888 $wgOut->addParserOutputNoText( $parserOutput );
01889
01890 if ( count( $parserOutput->getWarnings() ) ) {
01891 $note .= "\n\n" . implode( "\n\n", $parserOutput->getWarnings() );
01892 }
01893 }
01894
01895 if( $this->isConflict ) {
01896 $conflict = '<h2 id="mw-previewconflict">' . htmlspecialchars( wfMsg( 'previewconflict' ) ) . "</h2>\n";
01897 } else {
01898 $conflict = '<hr />';
01899 }
01900
01901 $previewhead = "<div class='previewnote'>\n" .
01902 '<h2 id="mw-previewheader">' . htmlspecialchars( wfMsg( 'preview' ) ) . "</h2>" .
01903 $wgOut->parse( $note ) . $conflict . "</div>\n";
01904
01905 wfProfileOut( __METHOD__ );
01906 return $previewhead . $previewHTML . $this->previewTextAfterContent;
01907 }
01908
01909 function getTemplates() {
01910 if ( $this->preview || $this->section != '' ) {
01911 $templates = array();
01912 if ( !isset( $this->mParserOutput ) ) return $templates;
01913 foreach( $this->mParserOutput->getTemplates() as $ns => $template) {
01914 foreach( array_keys( $template ) as $dbk ) {
01915 $templates[] = Title::makeTitle($ns, $dbk);
01916 }
01917 }
01918 return $templates;
01919 } else {
01920 return $this->mArticle->getUsedTemplates();
01921 }
01922 }
01923
01927 function blockedPage() {
01928 global $wgOut;
01929 $wgOut->blockedPage( false ); # Standard block notice on the top, don't 'return'
01930
01931 # If the user made changes, preserve them when showing the markup
01932 # (This happens when a user is blocked during edit, for instance)
01933 $first = $this->firsttime || ( !$this->save && $this->textbox1 == '' );
01934 if ( $first ) {
01935 $source = $this->mTitle->exists() ? $this->getContent() : false;
01936 } else {
01937 $source = $this->textbox1;
01938 }
01939
01940 # Spit out the source or the user's modified version
01941 if ( $source !== false ) {
01942 $wgOut->addHTML( '<hr />' );
01943 $wgOut->addWikiMsg( $first ? 'blockedoriginalsource' : 'blockededitsource', $this->mTitle->getPrefixedText() );
01944 $this->showTextbox1( array( 'readonly' ), $source );
01945 }
01946 }
01947
01951 function userNotLoggedInPage() {
01952 global $wgUser, $wgOut, $wgTitle;
01953 $skin = $wgUser->getSkin();
01954
01955 $loginTitle = SpecialPage::getTitleFor( 'Userlogin' );
01956 $loginLink = $skin->link(
01957 $loginTitle,
01958 wfMsgHtml( 'loginreqlink' ),
01959 array(),
01960 array( 'returnto' => $wgTitle->getPrefixedText() ),
01961 array( 'known', 'noclasses' )
01962 );
01963
01964 $wgOut->setPageTitle( wfMsg( 'whitelistedittitle' ) );
01965 $wgOut->setRobotPolicy( 'noindex,nofollow' );
01966 $wgOut->setArticleRelated( false );
01967
01968 $wgOut->addHTML( wfMsgWikiHtml( 'whitelistedittext', $loginLink ) );
01969 $wgOut->returnToMain( false, $wgTitle );
01970 }
01971
01976 function noSuchSectionPage() {
01977 global $wgOut;
01978
01979 $wgOut->setPageTitle( wfMsg( 'nosuchsectiontitle' ) );
01980 $wgOut->setRobotPolicy( 'noindex,nofollow' );
01981 $wgOut->setArticleRelated( false );
01982
01983 $res = wfMsgExt( 'nosuchsectiontext', 'parse', $this->section );
01984 wfRunHooks( 'EditPageNoSuchSection', array( &$this, &$res ) );
01985 $wgOut->addHTML( $res );
01986
01987 $wgOut->returnToMain( false, $this->mTitle );
01988 }
01989
01995 function spamPage( $match = false ) {
01996 global $wgOut, $wgTitle;
01997
01998 $wgOut->setPageTitle( wfMsg( 'spamprotectiontitle' ) );
01999 $wgOut->setRobotPolicy( 'noindex,nofollow' );
02000 $wgOut->setArticleRelated( false );
02001
02002 $wgOut->addHTML( '<div id="spamprotected">' );
02003 $wgOut->addWikiMsg( 'spamprotectiontext' );
02004 if ( $match )
02005 $wgOut->addWikiMsg( 'spamprotectionmatch', wfEscapeWikiText( $match ) );
02006 $wgOut->addHTML( '</div>' );
02007
02008 $wgOut->returnToMain( false, $wgTitle );
02009 }
02010
02015 function mergeChangesInto( &$editText ){
02016 wfProfileIn( __METHOD__ );
02017
02018 $db = wfGetDB( DB_MASTER );
02019
02020
02021 $baseRevision = $this->getBaseRevision();
02022 if ( is_null( $baseRevision ) ) {
02023 wfProfileOut( __METHOD__ );
02024 return false;
02025 }
02026 $baseText = $baseRevision->getText();
02027
02028
02029 $currentRevision = Revision::loadFromTitle( $db, $this->mTitle );
02030 if ( is_null( $currentRevision ) ) {
02031 wfProfileOut( __METHOD__ );
02032 return false;
02033 }
02034 $currentText = $currentRevision->getText();
02035
02036 $result = '';
02037 if ( wfMerge( $baseText, $editText, $currentText, $result ) ) {
02038 $editText = $result;
02039 wfProfileOut( __METHOD__ );
02040 return true;
02041 } else {
02042 wfProfileOut( __METHOD__ );
02043 return false;
02044 }
02045 }
02046
02054 function checkUnicodeCompliantBrowser() {
02055 global $wgBrowserBlackList;
02056 if ( empty( $_SERVER["HTTP_USER_AGENT"] ) ) {
02057
02058 return true;
02059 }
02060 $currentbrowser = $_SERVER["HTTP_USER_AGENT"];
02061 foreach ( $wgBrowserBlackList as $browser ) {
02062 if ( preg_match($browser, $currentbrowser) ) {
02063 return false;
02064 }
02065 }
02066 return true;
02067 }
02068
02072 function pseudoParseSectionAnchor( $text ) {
02073 global $wgParser;
02074 return $wgParser->stripSectionName( $text );
02075 }
02076
02083 function sectionAnchor( $text ) {
02084 global $wgParser;
02085 return $wgParser->guessSectionNameFromWikiText( $text );
02086 }
02087
02095 static function getEditToolbar() {
02096 global $wgStylePath, $wgContLang, $wgLang;
02097
02110 $toolarray = array(
02111 array(
02112 'image' => $wgLang->getImageFile( 'button-bold' ),
02113 'id' => 'mw-editbutton-bold',
02114 'open' => '\'\'\'',
02115 'close' => '\'\'\'',
02116 'sample' => wfMsg( 'bold_sample' ),
02117 'tip' => wfMsg( 'bold_tip' ),
02118 'key' => 'B'
02119 ),
02120 array(
02121 'image' => $wgLang->getImageFile( 'button-italic' ),
02122 'id' => 'mw-editbutton-italic',
02123 'open' => '\'\'',
02124 'close' => '\'\'',
02125 'sample' => wfMsg( 'italic_sample' ),
02126 'tip' => wfMsg( 'italic_tip' ),
02127 'key' => 'I'
02128 ),
02129 array(
02130 'image' => $wgLang->getImageFile( 'button-link' ),
02131 'id' => 'mw-editbutton-link',
02132 'open' => '[[',
02133 'close' => ']]',
02134 'sample' => wfMsg( 'link_sample' ),
02135 'tip' => wfMsg( 'link_tip' ),
02136 'key' => 'L'
02137 ),
02138 array(
02139 'image' => $wgLang->getImageFile( 'button-extlink' ),
02140 'id' => 'mw-editbutton-extlink',
02141 'open' => '[',
02142 'close' => ']',
02143 'sample' => wfMsg( 'extlink_sample' ),
02144 'tip' => wfMsg( 'extlink_tip' ),
02145 'key' => 'X'
02146 ),
02147 array(
02148 'image' => $wgLang->getImageFile( 'button-headline' ),
02149 'id' => 'mw-editbutton-headline',
02150 'open' => "\n== ",
02151 'close' => " ==\n",
02152 'sample' => wfMsg( 'headline_sample' ),
02153 'tip' => wfMsg( 'headline_tip' ),
02154 'key' => 'H'
02155 ),
02156 array(
02157 'image' => $wgLang->getImageFile( 'button-image' ),
02158 'id' => 'mw-editbutton-image',
02159 'open' => '[[' . $wgContLang->getNsText( NS_FILE ) . ':',
02160 'close' => ']]',
02161 'sample' => wfMsg( 'image_sample' ),
02162 'tip' => wfMsg( 'image_tip' ),
02163 'key' => 'D'
02164 ),
02165 array(
02166 'image' => $wgLang->getImageFile( 'button-media' ),
02167 'id' => 'mw-editbutton-media',
02168 'open' => '[[' . $wgContLang->getNsText( NS_MEDIA ) . ':',
02169 'close' => ']]',
02170 'sample' => wfMsg( 'media_sample' ),
02171 'tip' => wfMsg( 'media_tip' ),
02172 'key' => 'M'
02173 ),
02174 array(
02175 'image' => $wgLang->getImageFile( 'button-math' ),
02176 'id' => 'mw-editbutton-math',
02177 'open' => "<math>",
02178 'close' => "</math>",
02179 'sample' => wfMsg( 'math_sample' ),
02180 'tip' => wfMsg( 'math_tip' ),
02181 'key' => 'C'
02182 ),
02183 array(
02184 'image' => $wgLang->getImageFile( 'button-nowiki' ),
02185 'id' => 'mw-editbutton-nowiki',
02186 'open' => "<nowiki>",
02187 'close' => "</nowiki>",
02188 'sample' => wfMsg( 'nowiki_sample' ),
02189 'tip' => wfMsg( 'nowiki_tip' ),
02190 'key' => 'N'
02191 ),
02192 array(
02193 'image' => $wgLang->getImageFile( 'button-sig' ),
02194 'id' => 'mw-editbutton-signature',
02195 'open' => '--~~~~',
02196 'close' => '',
02197 'sample' => '',
02198 'tip' => wfMsg( 'sig_tip' ),
02199 'key' => 'Y'
02200 ),
02201 array(
02202 'image' => $wgLang->getImageFile( 'button-hr' ),
02203 'id' => 'mw-editbutton-hr',
02204 'open' => "\n----\n",
02205 'close' => '',
02206 'sample' => '',
02207 'tip' => wfMsg( 'hr_tip' ),
02208 'key' => 'R'
02209 )
02210 );
02211 $toolbar = "<div id='toolbar'>\n";
02212
02213 $script = '';
02214 foreach ( $toolarray as $tool ) {
02215 $params = array(
02216 $image = $wgStylePath . '/common/images/' . $tool['image'],
02217
02218
02219
02220
02221 $tip = $tool['tip'],
02222 $open = $tool['open'],
02223 $close = $tool['close'],
02224 $sample = $tool['sample'],
02225 $cssId = $tool['id'],
02226 );
02227
02228 $paramList = implode( ',',
02229 array_map( array( 'Xml', 'encodeJsVar' ), $params ) );
02230 $script .= "addButton($paramList);\n";
02231 }
02232 $toolbar .= Html::inlineScript( "\n$script\n" );
02233
02234 $toolbar .= "\n</div>";
02235
02236 wfRunHooks( 'EditPageBeforeEditToolbar', array( &$toolbar ) );
02237
02238 return $toolbar;
02239 }
02240
02252 public function getCheckboxes( &$tabindex, $skin, $checked ) {
02253 global $wgUser;
02254
02255 $checkboxes = array();
02256
02257 $checkboxes['minor'] = '';
02258 $minorLabel = wfMsgExt( 'minoredit', array( 'parseinline' ) );
02259 if ( $wgUser->isAllowed( 'minoredit' ) ) {
02260 $attribs = array(
02261 'tabindex' => ++$tabindex,
02262 'accesskey' => wfMsg( 'accesskey-minoredit' ),
02263 'id' => 'wpMinoredit',
02264 );
02265 $checkboxes['minor'] =
02266 Xml::check( 'wpMinoredit', $checked['minor'], $attribs ) .
02267 " <label for='wpMinoredit'" . $skin->tooltip( 'minoredit', 'withaccess' ) . ">{$minorLabel}</label>";
02268 }
02269
02270 $watchLabel = wfMsgExt( 'watchthis', array( 'parseinline' ) );
02271 $checkboxes['watch'] = '';
02272 if ( $wgUser->isLoggedIn() ) {
02273 $attribs = array(
02274 'tabindex' => ++$tabindex,
02275 'accesskey' => wfMsg( 'accesskey-watch' ),
02276 'id' => 'wpWatchthis',
02277 );
02278 $checkboxes['watch'] =
02279 Xml::check( 'wpWatchthis', $checked['watch'], $attribs ) .
02280 " <label for='wpWatchthis'" . $skin->tooltip( 'watch', 'withaccess' ) . ">{$watchLabel}</label>";
02281 }
02282 wfRunHooks( 'EditPageBeforeEditChecks', array( &$this, &$checkboxes, &$tabindex ) );
02283 return $checkboxes;
02284 }
02285
02294 public function getEditButtons(&$tabindex) {
02295 $buttons = array();
02296
02297 $temp = array(
02298 'id' => 'wpSave',
02299 'name' => 'wpSave',
02300 'type' => 'submit',
02301 'tabindex' => ++$tabindex,
02302 'value' => wfMsg( 'savearticle' ),
02303 'accesskey' => wfMsg( 'accesskey-save' ),
02304 'title' => wfMsg( 'tooltip-save' ).' ['.wfMsg( 'accesskey-save' ).']',
02305 );
02306 $buttons['save'] = Xml::element('input', $temp, '');
02307
02308 ++$tabindex;
02309 $temp = array(
02310 'id' => 'wpPreview',
02311 'name' => 'wpPreview',
02312 'type' => 'submit',
02313 'tabindex' => $tabindex,
02314 'value' => wfMsg( 'showpreview' ),
02315 'accesskey' => wfMsg( 'accesskey-preview' ),
02316 'title' => wfMsg( 'tooltip-preview' ) . ' [' . wfMsg( 'accesskey-preview' ) . ']',
02317 );
02318 $buttons['preview'] = Xml::element( 'input', $temp, '' );
02319 $buttons['live'] = '';
02320
02321 $temp = array(
02322 'id' => 'wpDiff',
02323 'name' => 'wpDiff',
02324 'type' => 'submit',
02325 'tabindex' => ++$tabindex,
02326 'value' => wfMsg( 'showdiff' ),
02327 'accesskey' => wfMsg( 'accesskey-diff' ),
02328 'title' => wfMsg( 'tooltip-diff' ) . ' [' . wfMsg( 'accesskey-diff' ) . ']',
02329 );
02330 $buttons['diff'] = Xml::element( 'input', $temp, '' );
02331
02332 wfRunHooks( 'EditPageBeforeEditButtons', array( &$this, &$buttons, &$tabindex ) );
02333 return $buttons;
02334 }
02335
02348 function livePreview() {
02349 global $wgOut;
02350 $wgOut->disable();
02351 header( 'Content-type: text/xml; charset=utf-8' );
02352 header( 'Cache-control: no-cache' );
02353
02354 $previewText = $this->getPreviewText();
02355 #$categories = $skin->getCategoryLinks();
02356
02357 $s =
02358 '<?xml version="1.0" encoding="UTF-8" ?>' . "\n" .
02359 Xml::tags( 'livepreview', null,
02360 Xml::element( 'preview', null, $previewText )
02361 #. Xml::element( 'category', null, $categories )
02362 );
02363 echo $s;
02364 }
02365
02366
02367 public function getCancelLink() {
02368 global $wgUser, $wgTitle;
02369
02370 $cancelParams = array();
02371 if ( !$this->isConflict && $this->mArticle->getOldID() > 0 ) {
02372 $cancelParams['oldid'] = $this->mArticle->getOldID();
02373 }
02374
02375 return $wgUser->getSkin()->link(
02376 $wgTitle,
02377 wfMsgExt( 'cancel', array( 'parseinline' ) ),
02378 array( 'id' => 'mw-editform-cancel' ),
02379 $cancelParams,
02380 array( 'known', 'noclasses' )
02381 );
02382 }
02383
02391 function showDiff() {
02392 $oldtext = $this->mArticle->fetchContent();
02393 $newtext = $this->mArticle->replaceSection(
02394 $this->section, $this->textbox1, $this->summary, $this->edittime );
02395
02396 wfRunHooks( 'EditPageGetDiffText', array( $this, &$newtext ) );
02397
02398 $newtext = $this->mArticle->preSaveTransform( $newtext );
02399 $oldtitle = wfMsgExt( 'currentrev', array( 'parseinline' ) );
02400 $newtitle = wfMsgExt( 'yourtext', array( 'parseinline' ) );
02401 if ( $oldtext !== false || $newtext != '' ) {
02402 $de = new DifferenceEngine( $this->mTitle );
02403 $de->setText( $oldtext, $newtext );
02404 $difftext = $de->getDiff( $oldtitle, $newtitle );
02405 $de->showDiffStyle();
02406 } else {
02407 $difftext = '';
02408 }
02409
02410 global $wgOut;
02411 $wgOut->addHTML( '<div id="wikiDiff">' . $difftext . '</div>' );
02412 }
02413
02423 function safeUnicodeInput( $request, $field ) {
02424 $text = rtrim( $request->getText( $field ) );
02425 return $request->getBool( 'safemode' )
02426 ? $this->unmakesafe( $text )
02427 : $text;
02428 }
02429
02430 function safeUnicodeText( $request, $text ) {
02431 $text = rtrim( $text );
02432 return $request->getBool( 'safemode' )
02433 ? $this->unmakesafe( $text )
02434 : $text;
02435 }
02436
02445 function safeUnicodeOutput( $text ) {
02446 global $wgContLang;
02447 $codedText = $wgContLang->recodeForEdit( $text );
02448 return $this->checkUnicodeCompliantBrowser()
02449 ? $codedText
02450 : $this->makesafe( $codedText );
02451 }
02452
02466 function makesafe( $invalue ) {
02467
02468 $invalue = strtr( $invalue, array( "&#x" => "�" ) );
02469
02470 $bytesleft = 0;
02471 $result = "";
02472 $working = 0;
02473 for( $i = 0; $i < strlen( $invalue ); $i++ ) {
02474 $bytevalue = ord( $invalue{$i} );
02475 if ( $bytevalue <= 0x7F ) {
02476 $result .= chr( $bytevalue );
02477 $bytesleft = 0;
02478 } elseif ( $bytevalue <= 0xBF ) {
02479 $working = $working << 6;
02480 $working += ($bytevalue & 0x3F);
02481 $bytesleft--;
02482 if ( $bytesleft <= 0 ) {
02483 $result .= "&#x" . strtoupper( dechex( $working ) ) . ";";
02484 }
02485 } elseif ( $bytevalue <= 0xDF ) {
02486 $working = $bytevalue & 0x1F;
02487 $bytesleft = 1;
02488 } elseif ( $bytevalue <= 0xEF ) {
02489 $working = $bytevalue & 0x0F;
02490 $bytesleft = 2;
02491 } else {
02492 $working = $bytevalue & 0x07;
02493 $bytesleft = 3;
02494 }
02495 }
02496 return $result;
02497 }
02498
02508 function unmakesafe( $invalue ) {
02509 $result = "";
02510 for( $i = 0; $i < strlen( $invalue ); $i++ ) {
02511 if ( ( substr( $invalue, $i, 3 ) == "&#x" ) && ( $invalue{$i+3} != '0' ) ) {
02512 $i += 3;
02513 $hexstring = "";
02514 do {
02515 $hexstring .= $invalue{$i};
02516 $i++;
02517 } while( ctype_xdigit( $invalue{$i} ) && ( $i < strlen( $invalue ) ) );
02518
02519
02520
02521
02522 if ( (substr($invalue,$i,1)==";") and (strlen($hexstring) <= 6) ) {
02523 $codepoint = hexdec($hexstring);
02524 $result .= codepointToUtf8( $codepoint );
02525 } else {
02526 $result .= "&#x" . $hexstring . substr( $invalue, $i, 1 );
02527 }
02528 } else {
02529 $result .= substr( $invalue, $i, 1 );
02530 }
02531 }
02532
02533 return strtr( $result, array( "�" => "&#x" ) );
02534 }
02535
02536 function noCreatePermission() {
02537 global $wgOut;
02538 $wgOut->setPageTitle( wfMsg( 'nocreatetitle' ) );
02539 $wgOut->addWikiMsg( 'nocreatetext' );
02540 }
02541
02546 function attemptSave() {
02547 global $wgUser, $wgOut, $wgTitle;
02548
02549 $resultDetails = false;
02550 # Allow bots to exempt some edits from bot flagging
02551 $bot = $wgUser->isAllowed( 'bot' ) && $this->bot;
02552 $value = $this->internalAttemptSave( $resultDetails, $bot );
02553
02554 if ( $value == self::AS_SUCCESS_UPDATE || $value == self::AS_SUCCESS_NEW_ARTICLE ) {
02555 $this->didSave = true;
02556 }
02557
02558 switch ( $value ) {
02559 case self::AS_HOOK_ERROR_EXPECTED:
02560 case self::AS_CONTENT_TOO_BIG:
02561 case self::AS_ARTICLE_WAS_DELETED:
02562 case self::AS_CONFLICT_DETECTED:
02563 case self::AS_SUMMARY_NEEDED:
02564 case self::AS_TEXTBOX_EMPTY:
02565 case self::AS_MAX_ARTICLE_SIZE_EXCEEDED:
02566 case self::AS_END:
02567 return true;
02568
02569 case self::AS_HOOK_ERROR:
02570 case self::AS_FILTERING:
02571 case self::AS_SUCCESS_NEW_ARTICLE:
02572 case self::AS_SUCCESS_UPDATE:
02573 return false;
02574
02575 case self::AS_SPAM_ERROR:
02576 $this->spamPage( $resultDetails['spam'] );
02577 return false;
02578
02579 case self::AS_BLOCKED_PAGE_FOR_USER:
02580 $this->blockedPage();
02581 return false;
02582
02583 case self::AS_IMAGE_REDIRECT_ANON:
02584 $wgOut->showErrorPage( 'uploadnologin', 'uploadnologintext' );
02585 return false;
02586
02587 case self::AS_READ_ONLY_PAGE_ANON:
02588 $this->userNotLoggedInPage();
02589 return false;
02590
02591 case self::AS_READ_ONLY_PAGE_LOGGED:
02592 case self::AS_READ_ONLY_PAGE:
02593 $wgOut->readOnlyPage();
02594 return false;
02595
02596 case self::AS_RATE_LIMITED:
02597 $wgOut->rateLimited();
02598 return false;
02599
02600 case self::AS_NO_CREATE_PERMISSION:
02601 $this->noCreatePermission();
02602 return;
02603
02604 case self::AS_BLANK_ARTICLE:
02605 $wgOut->redirect( $wgTitle->getFullURL() );
02606 return false;
02607
02608 case self::AS_IMAGE_REDIRECT_LOGGED:
02609 $wgOut->permissionRequired( 'upload' );
02610 return false;
02611 }
02612 }
02613
02614 function getBaseRevision() {
02615 if ( $this->mBaseRevision == false ) {
02616 $db = wfGetDB( DB_MASTER );
02617 $baseRevision = Revision::loadFromTimestamp(
02618 $db, $this->mTitle, $this->edittime );
02619 return $this->mBaseRevision = $baseRevision;
02620 } else {
02621 return $this->mBaseRevision;
02622 }
02623 }
02624 }