/includes/EditPage.php
PHP | 3209 lines | 2020 code | 391 blank | 798 comment | 453 complexity | aec4618c0445e58adcc44a632b6155b0 MD5 | raw file
Possible License(s): GPL-2.0, Apache-2.0, LGPL-3.0
Large files files are truncated, but you can click here to view the full file
- <?php
- /**
- * Contains the EditPage class
- * @file
- */
- /**
- * The edit page/HTML interface (split from Article)
- * The actual database and text munging is still in Article,
- * but it should get easier to call those from alternate
- * interfaces.
- *
- * EditPage cares about two distinct titles:
- * $this->mContextTitle is the page that forms submit to, links point to,
- * redirects go to, etc. $this->mTitle (as well as $mArticle) is the
- * page in the database that is actually being edited. These are
- * usually the same, but they are now allowed to be different.
- *
- * Surgeon General's Warning: prolonged exposure to this class is known to cause
- * headaches, which may be fatal.
- */
- class EditPage {
- /**
- * Status: Article successfully updated
- */
- const AS_SUCCESS_UPDATE = 200;
- /**
- * Status: Article successfully created
- */
- const AS_SUCCESS_NEW_ARTICLE = 201;
- /**
- * Status: Article update aborted by a hook function
- */
- const AS_HOOK_ERROR = 210;
- /**
- * Status: The filter function set in $wgFilterCallback returned true (= block it)
- */
- const AS_FILTERING = 211;
- /**
- * Status: A hook function returned an error
- */
- const AS_HOOK_ERROR_EXPECTED = 212;
- /**
- * Status: User is blocked from editting this page
- */
- const AS_BLOCKED_PAGE_FOR_USER = 215;
- /**
- * Status: Content too big (> $wgMaxArticleSize)
- */
- const AS_CONTENT_TOO_BIG = 216;
- /**
- * Status: User cannot edit? (not used)
- */
- const AS_USER_CANNOT_EDIT = 217;
- /**
- * Status: this anonymous user is not allowed to edit this page
- */
- const AS_READ_ONLY_PAGE_ANON = 218;
- /**
- * Status: this logged in user is not allowed to edit this page
- */
- const AS_READ_ONLY_PAGE_LOGGED = 219;
- /**
- * Status: wiki is in readonly mode (wfReadOnly() == true)
- */
- const AS_READ_ONLY_PAGE = 220;
- /**
- * Status: rate limiter for action 'edit' was tripped
- */
- const AS_RATE_LIMITED = 221;
- /**
- * Status: article was deleted while editting and param wpRecreate == false or form
- * was not posted
- */
- const AS_ARTICLE_WAS_DELETED = 222;
- /**
- * Status: user tried to create this page, but is not allowed to do that
- * ( Title->usercan('create') == false )
- */
- const AS_NO_CREATE_PERMISSION = 223;
- /**
- * Status: user tried to create a blank page
- */
- const AS_BLANK_ARTICLE = 224;
- /**
- * Status: (non-resolvable) edit conflict
- */
- const AS_CONFLICT_DETECTED = 225;
- /**
- * Status: no edit summary given and the user has forceeditsummary set and the user is not
- * editting in his own userspace or talkspace and wpIgnoreBlankSummary == false
- */
- const AS_SUMMARY_NEEDED = 226;
- /**
- * Status: user tried to create a new section without content
- */
- const AS_TEXTBOX_EMPTY = 228;
- /**
- * Status: article is too big (> $wgMaxArticleSize), after merging in the new section
- */
- const AS_MAX_ARTICLE_SIZE_EXCEEDED = 229;
- /**
- * not used
- */
- const AS_OK = 230;
- /**
- * Status: WikiPage::doEdit() was unsuccessfull
- */
- const AS_END = 231;
- /**
- * Status: summary contained spam according to one of the regexes in $wgSummarySpamRegex
- */
- const AS_SPAM_ERROR = 232;
- /**
- * Status: anonymous user is not allowed to upload (User::isAllowed('upload') == false)
- */
- const AS_IMAGE_REDIRECT_ANON = 233;
- /**
- * Status: logged in user is not allowed to upload (User::isAllowed('upload') == false)
- */
- const AS_IMAGE_REDIRECT_LOGGED = 234;
- /**
- * @var Article
- */
- var $mArticle;
- /**
- * @var Title
- */
- var $mTitle;
- private $mContextTitle = null;
- var $action = 'submit';
- var $isConflict = false;
- var $isCssJsSubpage = false;
- var $isCssSubpage = false;
- var $isJsSubpage = false;
- var $isWrongCaseCssJsPage = false;
- var $isNew = false; // new page or new section
- var $deletedSinceEdit;
- var $formtype;
- var $firsttime;
- var $lastDelete;
- var $mTokenOk = false;
- var $mTokenOkExceptSuffix = false;
- var $mTriedSave = false;
- var $incompleteForm = false;
- var $tooBig = false;
- var $kblength = false;
- var $missingComment = false;
- var $missingSummary = false;
- var $allowBlankSummary = false;
- var $autoSumm = '';
- var $hookError = '';
- #var $mPreviewTemplates;
- /**
- * @var ParserOutput
- */
- 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 = '', $nosummary = false;
- var $edittime = '', $section = '', $sectiontitle = '', $starttime = '';
- 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
- public $editFormPageTop = ''; // Before even the preview
- public $editFormTextTop = '';
- public $editFormTextBeforeContent = '';
- public $editFormTextAfterWarn = '';
- public $editFormTextAfterTools = '';
- public $editFormTextBottom = '';
- public $editFormTextAfterContent = '';
- public $previewTextAfterContent = '';
- public $mPreloadText = '';
- /* $didSave should be set to true whenever an article was succesfully altered. */
- public $didSave = false;
- public $undidRev = 0;
- public $suppressIntro = false;
- /**
- * @param $article Article
- */
- public function __construct( Article $article ) {
- $this->mArticle = $article;
- $this->mTitle = $article->getTitle();
- }
- /**
- * @return Article
- */
- public function getArticle() {
- return $this->mArticle;
- }
- /**
- * @since 1.19
- * @return Title
- */
- public function getTitle() {
- return $this->mTitle;
- }
- /**
- * Set the context Title object
- *
- * @param $title Title object or null
- */
- public function setContextTitle( $title ) {
- $this->mContextTitle = $title;
- }
- /**
- * Get the context title object.
- * If not set, $wgTitle will be returned. This behavior might changed in
- * the future to return $this->mTitle instead.
- *
- * @return Title object
- */
- public function getContextTitle() {
- if ( is_null( $this->mContextTitle ) ) {
- global $wgTitle;
- return $wgTitle;
- } else {
- return $this->mContextTitle;
- }
- }
- function submit() {
- $this->edit();
- }
- /**
- * This is the function that gets called for "action=edit". It
- * sets up various member variables, then passes execution to
- * another function, usually showEditForm()
- *
- * The edit form is self-submitting, so that when things like
- * preview and edit conflicts occur, we get the same form back
- * with the extra stuff added. Only when the final submission
- * is made and all is well do we actually save and redirect to
- * the newly-edited page.
- */
- function edit() {
- global $wgOut, $wgRequest, $wgUser;
- // Allow extensions to modify/prevent this form or submission
- if ( !wfRunHooks( 'AlternateEdit', array( $this ) ) ) {
- return;
- }
- wfProfileIn( __METHOD__ );
- wfDebug( __METHOD__.": enter\n" );
- // 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() );
- wfProfileOut( __METHOD__ );
- return;
- }
- $this->importFormData( $wgRequest );
- $this->firsttime = false;
- if ( $this->live ) {
- $this->livePreview();
- wfProfileOut( __METHOD__ );
- return;
- }
- if ( wfReadOnly() && $this->save ) {
- // Force preview
- $this->save = false;
- $this->preview = true;
- }
- if ( $this->save ) {
- $this->formtype = 'save';
- } elseif ( $this->preview ) {
- $this->formtype = 'preview';
- } elseif ( $this->diff ) {
- $this->formtype = 'diff';
- } else { # First time through
- $this->firsttime = true;
- if ( $this->previewOnOpen() ) {
- $this->formtype = 'preview';
- } else {
- $this->formtype = 'initial';
- }
- }
- $permErrors = $this->getEditPermissionErrors();
- if ( $permErrors ) {
- wfDebug( __METHOD__ . ": User can't edit\n" );
- // Auto-block user's IP if the account was "hard" blocked
- $wgUser->spreadAnyEditBlock();
- $this->displayPermissionsError( $permErrors );
- wfProfileOut( __METHOD__ );
- return;
- }
- wfProfileIn( __METHOD__."-business-end" );
- $this->isConflict = false;
- // css / js subpages of user pages get a special treatment
- $this->isCssJsSubpage = $this->mTitle->isCssJsSubpage();
- $this->isCssSubpage = $this->mTitle->isCssSubpage();
- $this->isJsSubpage = $this->mTitle->isJsSubpage();
- $this->isWrongCaseCssJsPage = $this->isWrongCaseCssJsPage();
- $this->isNew = !$this->mTitle->exists() || $this->section == 'new';
- # Show applicable editing introductions
- if ( $this->formtype == 'initial' || $this->firsttime ) {
- $this->showIntro();
- }
- # Attempt submission here. This will check for edit conflicts,
- # and redundantly check for locked database, blocked IPs, etc.
- # that edit() already checked just in case someone tries to sneak
- # in the back door with a hand-edited submission URL.
- if ( 'save' == $this->formtype ) {
- if ( !$this->attemptSave() ) {
- wfProfileOut( __METHOD__."-business-end" );
- wfProfileOut( __METHOD__ );
- return;
- }
- }
- # First time through: get contents, set time for conflict
- # checking, etc.
- if ( 'initial' == $this->formtype || $this->firsttime ) {
- if ( $this->initialiseForm() === false ) {
- $this->noSuchSectionPage();
- wfProfileOut( __METHOD__."-business-end" );
- wfProfileOut( __METHOD__ );
- return;
- }
- if ( !$this->mTitle->getArticleId() )
- wfRunHooks( 'EditFormPreloadText', array( &$this->textbox1, &$this->mTitle ) );
- else
- wfRunHooks( 'EditFormInitialText', array( $this ) );
- }
- $this->showEditForm();
- wfProfileOut( __METHOD__."-business-end" );
- wfProfileOut( __METHOD__ );
- }
- /**
- * @return array
- */
- protected function getEditPermissionErrors() {
- global $wgUser;
- $permErrors = $this->mTitle->getUserPermissionsErrors( 'edit', $wgUser );
- # Can this title be created?
- if ( !$this->mTitle->exists() ) {
- $permErrors = array_merge( $permErrors,
- wfArrayDiff2( $this->mTitle->getUserPermissionsErrors( 'create', $wgUser ), $permErrors ) );
- }
- # 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' ) )
- {
- $remove[] = $error;
- }
- }
- $permErrors = wfArrayDiff2( $permErrors, $remove );
- return $permErrors;
- }
- /**
- * Display a permissions error page, like OutputPage::showPermissionsErrorPage(),
- * but with the following differences:
- * - If redlink=1, the user will be redirected to the page
- * - If there is content to display or the error occurs while either saving,
- * previewing or showing the difference, it will be a
- * "View source for ..." page displaying the source code after the error message.
- *
- * @since 1.19
- * @param $permErrors Array of permissions errors, as returned by
- * Title::getUserPermissionsErrors().
- */
- protected function displayPermissionsError( array $permErrors ) {
- global $wgRequest, $wgOut;
- if ( $wgRequest->getBool( 'redlink' ) ) {
- // The edit page was reached via a red link.
- // Redirect to the article page and let them click the edit tab if
- // they really want a permission error.
- $wgOut->redirect( $this->mTitle->getFullUrl() );
- return;
- }
- $content = $this->getContent();
- # Use the normal message if there's nothing to display
- if ( $this->firsttime && $content === '' ) {
- $action = $this->mTitle->exists() ? 'edit' :
- ( $this->mTitle->isTalkPage() ? 'createtalk' : 'createpage' );
- throw new PermissionsError( $action, $permErrors );
- }
- $wgOut->setPageTitle( wfMessage( 'viewsource-title', $this->getContextTitle()->getPrefixedText() ) );
- $wgOut->addBacklinkSubtitle( $this->getContextTitle() );
- $wgOut->addWikiText( $wgOut->formatPermissionsErrorMessage( $permErrors, 'edit' ) );
- $wgOut->addHTML( "<hr />\n" );
- # If the user made changes, preserve them when showing the markup
- # (This happens when a user is blocked during edit, for instance)
- if ( !$this->firsttime ) {
- $content = $this->textbox1;
- $wgOut->addWikiMsg( 'viewyourtext' );
- } else {
- $wgOut->addWikiMsg( 'viewsourcetext' );
- }
- $this->showTextbox( $content, 'wpTextbox1', array( 'readonly' ) );
- $wgOut->addHTML( Html::rawElement( 'div', array( 'class' => 'templatesUsed' ),
- Linker::formatTemplates( $this->getTemplates() ) ) );
- if ( $this->mTitle->exists() ) {
- $wgOut->returnToMain( null, $this->mTitle );
- }
- }
- /**
- * Show a read-only error
- * Parameters are the same as OutputPage:readOnlyPage()
- * Redirect to the article page if redlink=1
- * @deprecated in 1.19; use displayPermissionsError() instead
- */
- function readOnlyPage( $source = null, $protected = false, $reasons = array(), $action = null ) {
- wfDeprecated( __METHOD__, '1.19' );
-
- global $wgRequest, $wgOut;
- if ( $wgRequest->getBool( 'redlink' ) ) {
- // The edit page was reached via a red link.
- // Redirect to the article page and let them click the edit tab if
- // they really want a permission error.
- $wgOut->redirect( $this->mTitle->getFullUrl() );
- } else {
- $wgOut->readOnlyPage( $source, $protected, $reasons, $action );
- }
- }
- /**
- * Should we show a preview when the edit form is first shown?
- *
- * @return bool
- */
- protected function previewOnOpen() {
- global $wgRequest, $wgUser, $wgPreviewOnOpenNamespaces;
- if ( $wgRequest->getVal( 'preview' ) == 'yes' ) {
- // Explicit override from request
- return true;
- } elseif ( $wgRequest->getVal( 'preview' ) == 'no' ) {
- // Explicit override from request
- return false;
- } elseif ( $this->section == 'new' ) {
- // Nothing *to* preview for new sections
- return false;
- } elseif ( ( $wgRequest->getVal( 'preload' ) !== null || $this->mTitle->exists() ) && $wgUser->getOption( 'previewonfirst' ) ) {
- // Standard preference behaviour
- return true;
- } elseif ( !$this->mTitle->exists() &&
- isset($wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()]) &&
- $wgPreviewOnOpenNamespaces[$this->mTitle->getNamespace()] )
- {
- // Categories are special
- return true;
- } else {
- return false;
- }
- }
- /**
- * Checks whether the user entered a skin name in uppercase,
- * e.g. "User:Example/Monobook.css" instead of "monobook.css"
- *
- * @return bool
- */
- protected function isWrongCaseCssJsPage() {
- if( $this->mTitle->isCssJsSubpage() ) {
- $name = $this->mTitle->getSkinFromCssJsSubpage();
- $skins = array_merge(
- array_keys( Skin::getSkinNames() ),
- array( 'common' )
- );
- return !in_array( $name, $skins )
- && in_array( strtolower( $name ), $skins );
- } else {
- return false;
- }
- }
- /**
- * 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;
- }
- /**
- * This function collects the form data and uses it to populate various member variables.
- * @param $request WebRequest
- */
- function importFormData( &$request ) {
- global $wgLang, $wgUser;
- wfProfileIn( __METHOD__ );
- # Section edit can come from either the form or a link
- $this->section = $request->getVal( 'wpSection', $request->getVal( 'section' ) );
- if ( $request->wasPosted() ) {
- # These fields need to be checked for encoding.
- # 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' );
- 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 );
- # If the summary consists of a heading, e.g. '==Foobar==', extract the title from the
- # header syntax, e.g. 'Foobar'. This is mainly an issue when we are using wpSummary for
- # section titles.
- $this->summary = preg_replace( '/^\s*=+\s*(.*?)\s*=+\s*$/', '$1', $this->summary );
-
- # Treat sectiontitle the same way as summary.
- # Note that wpSectionTitle is not yet a part of the actual edit form, as wpSummary is
- # currently doing double duty as both edit summary and section title. Right now this
- # is just to allow API edits to work around this limitation, but this should be
- # incorporated into the actual edit form when EditPage is rewritten (Bugs 18654, 26312).
- $this->sectiontitle = $wgLang->truncate( $request->getText( 'wpSectionTitle' ), 250 );
- $this->sectiontitle = preg_replace( '/^\s*=+\s*(.*?)\s*=+\s*$/', '$1', $this->sectiontitle );
- $this->edittime = $request->getVal( 'wpEdittime' );
- $this->starttime = $request->getVal( 'wpStarttime' );
- $this->scrolltop = $request->getIntOrNull( 'wpScrolltop' );
- if ( $this->textbox1 === '' && $request->getVal( 'wpTextbox1' ) === null ) {
- // wpTextbox1 field is missing, possibly due to being "too big"
- // according to some filter rules such as Suhosin's setting for
- // suhosin.request.max_value_length (d'oh)
- $this->incompleteForm = true;
- } else {
- // edittime should be one of our last fields; if it's missing,
- // the submission probably broke somewhere in the middle.
- $this->incompleteForm = is_null( $this->edittime );
- }
- if ( $this->incompleteForm ) {
- # If the form is incomplete, force to preview.
- wfDebug( __METHOD__ . ": Form data appears to be incomplete\n" );
- wfDebug( "POST DATA: " . var_export( $_POST, true ) . "\n" );
- $this->preview = true;
- } else {
- /* Fallback for live preview */
- $this->preview = $request->getCheck( 'wpPreview' ) || $request->getCheck( 'wpLivePreview' );
- $this->diff = $request->getCheck( 'wpDiff' );
- // Remember whether a save was requested, so we can indicate
- // if we forced preview due to session failure.
- $this->mTriedSave = !$this->preview;
- if ( $this->tokenOk( $request ) ) {
- # Some browsers will not report any submit button
- # 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( __METHOD__ . ": Passed token check.\n" );
- } elseif ( $this->diff ) {
- # Failed token check, but only requested "Show Changes".
- 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( __METHOD__ . ": Failed token check; forcing preview\n" );
- $this->preview = true;
- }
- }
- $this->save = !$this->preview && !$this->diff;
- if ( !preg_match( '/^\d{14}$/', $this->edittime ) ) {
- $this->edittime = null;
- }
- if ( !preg_match( '/^\d{14}$/', $this->starttime ) ) {
- $this->starttime = null;
- }
- $this->recreate = $request->getCheck( 'wpRecreate' );
- $this->minoredit = $request->getCheck( 'wpMinoredit' );
- $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() )
- {
- $this->allowBlankSummary = true;
- } else {
- $this->allowBlankSummary = $request->getBool( 'wpIgnoreBlankSummary' ) || !$wgUser->getOption( 'forceeditsummary');
- }
- $this->autoSumm = $request->getText( 'wpAutoSummary' );
- } else {
- # Not a posted form? Start with nothing.
- wfDebug( __METHOD__ . ": Not a posted form.\n" );
- $this->textbox1 = '';
- $this->summary = '';
- $this->sectiontitle = '';
- $this->edittime = '';
- $this->starttime = wfTimestampNow();
- $this->edit = false;
- $this->preview = false;
- $this->save = false;
- $this->diff = false;
- $this->minoredit = false;
- $this->watchthis = $request->getBool( 'watchthis', false ); // Watch may be overriden by request parameters
- $this->recreate = false;
-
- // When creating a new section, we can preload a section title by passing it as the
- // preloadtitle parameter in the URL (Bug 13100)
- if ( $this->section == 'new' && $request->getVal( 'preloadtitle' ) ) {
- $this->sectiontitle = $request->getVal( 'preloadtitle' );
- // Once wpSummary isn't being use for setting section titles, we should delete this.
- $this->summary = $request->getVal( 'preloadtitle' );
- }
- 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' );
- $this->oldid = $request->getInt( 'oldid' );
- $this->live = $request->getCheck( 'live' );
- $this->editintro = $request->getText( 'editintro',
- // Custom edit intro for new sections
- $this->section === 'new' ? 'MediaWiki:addsection-editintro' : '' );
- // Allow extensions to modify form data
- wfRunHooks( 'EditPage::importFormData', array( $this, $request ) );
- wfProfileOut( __METHOD__ );
- }
- /**
- * 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
- }
- /**
- * Initialise form fields in the object
- * Called on the first invocation, e.g. when a user clicks an edit link
- * @return 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 ( $wgUser->getOption( 'minordefault' ) && !$this->isNew ) {
- $this->minoredit = true;
- }
- if ( $this->textbox1 === false ) {
- return false;
- }
- wfProxyCheck();
- return true;
- }
- /**
- * Fetch initial editing page content.
- *
- * @param $def_text string
- * @return mixed string on success, $def_text for invalid sections
- * @private
- */
- function getContent( $def_text = '' ) {
- global $wgOut, $wgRequest, $wgParser;
- wfProfileIn( __METHOD__ );
- $text = false;
- // For message page not locally set, use the i18n message.
- // For other non-existent articles, use preload text if any.
- if ( !$this->mTitle->exists() || $this->section == 'new' ) {
- if ( $this->mTitle->getNamespace() == NS_MEDIAWIKI && $this->section != 'new' ) {
- # If this is a system message, get the default text.
- $text = $this->mTitle->getDefaultMessageText();
- }
- if ( $text === false ) {
- # If requested, preload some text.
- $preload = $wgRequest->getVal( 'preload',
- // Custom preload text for new sections
- $this->section === 'new' ? 'MediaWiki:addsection-preload' : '' );
- $text = $this->getPreloadedText( $preload );
- }
- // For existing pages, get text based on "undo" or section parameters.
- } else {
- if ( $this->section != '' ) {
- // Get section edit text (returns $def_text for invalid sections)
- $text = $wgParser->getSection( $this->getOriginalContent(), $this->section, $def_text );
- } else {
- $undoafter = $wgRequest->getInt( 'undoafter' );
- $undo = $wgRequest->getInt( 'undo' );
- if ( $undo > 0 && $undoafter > 0 ) {
- if ( $undo < $undoafter ) {
- # If they got undoafter and undo round the wrong way, switch them
- list( $undo, $undoafter ) = array( $undoafter, $undo );
- }
- $undorev = Revision::newFromId( $undo );
- $oldrev = Revision::newFromId( $undoafter );
- # Sanity check, make sure it's the right page,
- # the revisions exist and they were not deleted.
- # Otherwise, $text will be left as-is.
- if ( !is_null( $undorev ) && !is_null( $oldrev ) &&
- $undorev->getPage() == $oldrev->getPage() &&
- $undorev->getPage() == $this->mTitle->getArticleId() &&
- !$undorev->isDeleted( Revision::DELETED_TEXT ) &&
- !$oldrev->isDeleted( Revision::DELETED_TEXT ) ) {
- $text = $this->mArticle->getUndoText( $undorev, $oldrev );
- if ( $text === false ) {
- # Warn the user that something went wrong
- $undoMsg = 'failure';
- } else {
- # Inform the user of our success and set an automatic edit summary
- $undoMsg = 'success';
- # If we just undid one rev, use an autosummary
- $firstrev = $oldrev->getNext();
- if ( $firstrev->getId() == $undo ) {
- $undoSummary = wfMsgForContent( 'undo-summary', $undo, $undorev->getUserText() );
- if ( $this->summary === '' ) {
- $this->summary = $undoSummary;
- } else {
- $this->summary = $undoSummary . wfMsgForContent( 'colon-separator' ) . $this->summary;
- }
- $this->undidRev = $undo;
- }
- $this->formtype = 'diff';
- }
- } else {
- // Failed basic sanity checks.
- // Older revisions may have been removed since the link
- // was created, or we may simply have got bogus input.
- $undoMsg = 'norev';
- }
- $class = ( $undoMsg == 'success' ? '' : 'error ' ) . "mw-undo-{$undoMsg}";
- $this->editFormPageTop .= $wgOut->parse( "<div class=\"{$class}\">" .
- wfMsgNoTrans( 'undo-' . $undoMsg ) . '</div>', true, /* interface */true );
- }
- if ( $text === false ) {
- $text = $this->getOriginalContent();
- }
- }
- }
- wfProfileOut( __METHOD__ );
- return $text;
- }
- /**
- * Get the content of the wanted revision, without section extraction.
- *
- * The result of this function can be used to compare user's input with
- * section replaced in its context (using WikiPage::replaceSection())
- * to the original text of the edit.
- *
- * This difers from Article::getContent() that when a missing revision is
- * encountered the result will be an empty string and not the
- * 'missing-article' message.
- *
- * @since 1.19
- * @return string
- */
- private function getOriginalContent() {
- if ( $this->section == 'new' ) {
- return $this->getCurrentText();
- }
- $revision = $this->mArticle->getRevisionFetched();
- if ( $revision === null ) {
- return '';
- }
- return $this->mArticle->getContent();
- }
- /**
- * Get the actual text of the page. This is basically similar to
- * WikiPage::getRawText() except that when the page doesn't exist an empty
- * string is returned instead of false.
- *
- * @since 1.19
- * @return string
- */
- private function getCurrentText() {
- $text = $this->mArticle->getRawText();
- if ( $text === false ) {
- return '';
- } else {
- return $text;
- }
- }
- /**
- * Use this method before edit() to preload some text into the edit box
- *
- * @param $text string
- */
- public function setPreloadedText( $text ) {
- $this->mPreloadText = $text;
- }
- /**
- * Get the contents to be preloaded into the box, either set by
- * an earlier setPreloadText() or by loading the given page.
- *
- * @param $preload String: representing the title to preload from.
- * @return String
- */
- protected function getPreloadedText( $preload ) {
- global $wgUser, $wgParser;
- if ( !empty( $this->mPreloadText ) ) {
- return $this->mPreloadText;
- }
-
- if ( $preload === '' ) {
- return '';
- }
- $title = Title::newFromText( $preload );
- # Check for existence to avoid getting MediaWiki:Noarticletext
- if ( $title === null || !$title->exists() || !$title->userCan( 'read' ) ) {
- return '';
- }
- $page = WikiPage::factory( $title );
- if ( $page->isRedirect() ) {
- $title = $page->getRedirectTarget();
- # Same as before
- if ( $title === null || !$title->exists() || !$title->userCan( 'read' ) ) {
- return '';
- }
- $page = WikiPage::factory( $title );
- }
- $parserOptions = ParserOptions::newFromUser( $wgUser );
- return $wgParser->getPreloadText( $page->getRawText(), $title, $parserOptions );
- }
- /**
- * Make sure the form isn't faking a user's credentials.
- *
- * @param $request WebRequest
- * @return bool
- * @private
- */
- function tokenOk( &$request ) {
- global $wgUser;
- $token = $request->getVal( 'wpEditToken' );
- $this->mTokenOk = $wgUser->matchEditToken( $token );
- $this->mTokenOkExceptSuffix = $wgUser->matchEditTokenNoSuffix( $token );
- return $this->mTokenOk;
- }
- /**
- * Attempt submission
- * @return bool false if output is done, true if the rest of the form should be displayed
- */
- function attemptSave() {
- global $wgUser, $wgOut;
- $resultDetails = false;
- # Allow bots to exempt some edits from bot flagging
- $bot = $wgUser->isAllowed( 'bot' ) && $this->bot;
- $status = $this->internalAttemptSave( $resultDetails, $bot );
- // FIXME: once the interface for internalAttemptSave() is made nicer, this should use the message in $status
- if ( $status->value == self::AS_SUCCESS_UPDATE || $status->value == self::AS_SUCCESS_NEW_ARTICLE ) {
- $this->didSave = true;
- }
- switch ( $status->value ) {
- case self::AS_HOOK_ERROR_EXPECTED:
- case self::AS_CONTENT_TOO_BIG:
- case self::AS_ARTICLE_WAS_DELETED:
- case self::AS_CONFLICT_DETECTED:
- case self::AS_SUMMARY_NEEDED:
- case self::AS_TEXTBOX_EMPTY:
- case self::AS_MAX_ARTICLE_SIZE_EXCEEDED:
- case self::AS_END:
- return true;
- case self::AS_HOOK_ERROR:
- case self::AS_FILTERING:
- return false;
- case self::AS_SUCCESS_NEW_ARTICLE:
- $query = $resultDetails['redirect'] ? 'redirect=no' : '';
- $anchor = isset ( $resultDetails['sectionanchor'] ) ? $resultDetails['sectionanchor'] : '';
- $wgOut->redirect( $this->mTitle->getFullURL( $query ) . $anchor );
- return false;
- case self::AS_SUCCESS_UPDATE:
- $extraQuery = '';
- $sectionanchor = $resultDetails['sectionanchor'];
- // Give extensions a chance to modify URL query on update
- wfRunHooks( 'ArticleUpdateBeforeRedirect', array( $this->mArticle, &$sectionanchor, &$extraQuery ) );
- if ( $resultDetails['redirect'] ) {
- if ( $extraQuery == '' ) {
- $extraQuery = 'redirect=no';
- } else {
- $extraQuery = 'redirect=no&' . $extraQuery;
- }
- }
- $wgOut->redirect( $this->mTitle->getFullURL( $extraQuery ) . $sectionanchor );
- return false;
- case self::AS_BLANK_ARTICLE:
- $wgOut->redirect( $this->getContextTitle()->getFullURL() );
- return false;
- case self::AS_SPAM_ERROR:
- $this->spamPageWithContent( $resultDetails['spam'] );
- return false;
- case self::AS_BLOCKED_PAGE_FOR_USER:
- throw new UserBlockedError( $wgUser->mBlock );
- case self::AS_IMAGE_REDIRECT_ANON:
- case self::AS_IMAGE_REDIRECT_LOGGED:
- throw new PermissionsError( 'upload' );
- case self::AS_READ_ONLY_PAGE_ANON:
- case self::AS_READ_ONLY_PAGE_LOGGED:
- throw new PermissionsError( 'edit' );
- case self::AS_READ_ONLY_PAGE:
- throw new ReadOnlyError;
- case self::AS_RATE_LIMITED:
- throw new ThrottledError();
- case self::AS_NO_CREATE_PERMISSION:
- $permission = $this->mTitle->isTalkPage() ? 'createtalk' : 'createpage';
- throw new PermissionsError( $permission );
- }
- return false;
- }
- /**
- * Attempt submission (no UI)
- *
- * @param $result
- * @param $bot bool
- *
- * @return Status object, possibly with a message, but always with one of the AS_* constants in $status->value,
- *
- * FIXME: This interface is TERRIBLE, but hard to get rid of due to various error display idiosyncrasies. There are
- * also lots of cases where error metadata is set in the object and retrieved later instead of being returned, e.g.
- * AS_CONTENT_TOO_BIG and AS_BLOCKED_PAGE_FOR_USER. All that stuff needs to be cleaned up some time.
- */
- function internalAttemptSave( &$result, $bot = false ) {
- global $wgFilterCallback, $wgUser, $wgRequest, $wgParser;
- global $wgMaxArticleSize;
- $status = Status::newGood();
- wfProfileIn( __METHOD__ );
- wfProfileIn( __METHOD__ . '-checks' );
- if ( !wfRunHooks( 'EditPage::attemptSave', array( $this ) ) ) {
- wfDebug( "Hook 'EditPage::attemptSave' aborted article saving\n" );
- $status->fatal( 'hookaborted' );
- $status->value = self::AS_HOOK_ERROR;
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
- return $status;
- }
- # Check image redirect
- if ( $this->mTitle->getNamespace() == NS_FILE &&
- Title::newFromRedirect( $this->textbox1 ) instanceof Title &&
- !$wgUser->isAllowed( 'upload' ) ) {
- $code = $wgUser->isAnon() ? self::AS_IMAGE_REDIRECT_ANON : self::AS_IMAGE_REDIRECT_LOGGED;
- $status->setResult( false, $code );
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
- return $status;
- }
- # Check for spam
- $match = self::matchSummarySpamRegex( $this->summary );
- if ( $match === false ) {
- $match = self::matchSpamRegex( $this->textbox1 );
- }
- if ( $match !== false ) {
- $result['spam'] = $match;
- $ip = $wgRequest->getIP();
- $pdbk = $this->mTitle->getPrefixedDBkey();
- $match = str_replace( "\n", '', $match );
- wfDebugLog( 'SpamRegex', "$ip spam regex hit [[$pdbk]]: \"$match\"" );
- $status->fatal( 'spamprotectionmatch', $match );
- $status->value = self::AS_SPAM_ERROR;
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
- return $status;
- }
- if ( $wgFilterCallback && is_callable( $wgFilterCallback ) && $wgFilterCallback( $this->mTitle, $this->textbox1, $this->section, $this->hookError, $this->summary ) ) {
- # Error messages or other handling should be performed by the filter function
- $status->setResult( false, self::AS_FILTERING );
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
- return $status;
- }
- if ( !wfRunHooks( 'EditFilter', array( $this, $this->textbox1, $this->section, &$this->hookError, $this->summary ) ) ) {
- # Error messages etc. could be handled within the hook...
- $status->fatal( 'hookaborted' );
- $status->value = self::AS_HOOK_ERROR;
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
- return $status;
- } elseif ( $this->hookError != '' ) {
- # ...or the hook could be expecting us to produce an error
- $status->fatal( 'hookaborted' );
- $status->value = self::AS_HOOK_ERROR_EXPECTED;
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
- return $status;
- }
- if ( $wgUser->isBlockedFrom( $this->mTitle, false ) ) {
- // Auto-block user's IP if the account was "hard" blocked
- $wgUser->spreadAnyEditBlock();
- # Check block state against master, thus 'false'.
- $status->setResult( false, self::AS_BLOCKED_PAGE_FOR_USER );
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
- return $status;
- }
- $this->kblength = (int)( strlen( $this->textbox1 ) / 1024 );
- if ( $this->kblength > $wgMaxArticleSize ) {
- // Error will be displayed by showEditForm()
- $this->tooBig = true;
- $status->setResult( false, self::AS_CONTENT_TOO_BIG );
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
- return $status;
- }
- if ( !$wgUser->isAllowed( 'edit' ) ) {
- if ( $wgUser->isAnon() ) {
- $status->setResult( false, self::AS_READ_ONLY_PAGE_ANON );
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
- return $status;
- } else {
- $status->fatal( 'readonlytext' );
- $status->value = self::AS_READ_ONLY_PAGE_LOGGED;
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
- return $status;
- }
- }
- if ( wfReadOnly() ) {
- $status->fatal( 'readonlytext' );
- $status->value = self::AS_READ_ONLY_PAGE;
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
- return $status;
- }
- if ( $wgUser->pingLimiter() ) {
- $status->fatal( 'actionthrottledtext' );
- $status->value = self::AS_RATE_LIMITED;
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
- return $status;
- }
- # If the article has been deleted while editing, don't save it without
- # confirmation
- if ( $this->wasDeletedSinceLastEdit() && !$this->recreate ) {
- $status->setResult( false, self::AS_ARTICLE_WAS_DELETED );
- wfProfileOut( __METHOD__ . '-checks' );
- wfProfileOut( __METHOD__ );
- return $status;
- }
- wfProfileOut( __METHOD__ . '-checks' );
- # If article is new, insert it.
- $aid = $this->mTitle->getArticleID( Title::GAID_FOR_UPDATE );
- $new = ( $aid == 0 );
- if ( $new ) {
- // Late check for create permission, just in case *PARANOIA*
- if ( !$this->mTitle->userCan( 'create' ) ) {
- $status->fatal( 'nocreatetext' );
- $status->value = self::AS_NO_CREATE_PERMISSION;
- wfDebug( __METHOD__ . ": no create permission\n" );
- wfProfileOut( __METHOD__ );
- return $status;
- }
- # Don't save a new article if it's blank.
- if ( $this->textbox1 == '' ) {
- $status->setResult( false, self::AS_BLANK_ARTICLE );
- wfProfileOut( __METHOD__ );
- return $status;
- }
- // 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...
- $status->fatal( 'hookaborted' );
- $status->value = self::AS_HOOK_ERROR;
- wfProfileOut( __METHOD__ );
- return $status;
- } elseif ( $this->hookError != '' ) {
- # ...or the hook could be expecting us to produce an error
- $status->fatal( 'hookaborted' );
- $status->value = self::AS_HOOK_ERROR_EXPECTED;
- wfProfileOut( __METHOD__ );
- return $status;
- }
- # 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;
- $status->fatal( 'missingsummary' ); // or 'missingcommentheader' if $section == 'new'. Blegh
- $status->value = self::AS_SUMMARY_NEEDED;
- wfProfileOut( __METHOD__ );
- return $status;
- }
- }
- $text = $this->textbox1;
- $result['sectionanchor'] = '';
- if ( $this->section == 'new' ) {
- if ( $this->sectiontitle !== '' ) {
- // Insert the section title above the content.
- $text = wfMsgForContent( 'newsectionheaderdefaultlevel', $this->sectiontitle ) . "\n\n" . $text;
-
- // Jump to the new section
- $result['sectionanchor'] = $wgParser->guessLegacySectionNameFromWikiText( $this->sectiontitle );
-
- // If no edit summary was specified, create one automatically from the section
- // title and have it link to the new section. Otherwise, respect the summary as
- // passed.
- if ( $this->summary === '' ) {
- $cleanSectionTitle = $wgParser->stripSectionName( $this->sectiontitle );
- $this->summary = wfMsgForContent( 'newsectionsummary', $cleanSectionTitle );
- }
- } elseif ( $this->summary !== '' ) {
- // Insert the section title above the content.
- $text = wfMsgForContent( 'newsectionheaderdefaultlevel', $this->summary ) . "\n\n" . $text;
-
- // Jump to the new section
- $result['sectionanchor'] = $wgParser->guessLegacySectionNameFromWikiText( $this->summary );
- // Create a link to the new section from the edit summary.
- $cleanSummary = $wgParser->stripSectionName( $this->summary );
- $this->summary = wfMsgForContent( 'newsectionsummary', $cleanSummary );
- }
- }
- $status->value = self::AS_SUCCESS_NEW_ARTICLE;
- } else {
- # Article exists. Check for edit conflict.
- $this->mArticle->clear(); # Force reload of dates, etc.
- $timestamp = $this->mArticle->getTimestamp();
- wfDebug( "timestamp: {$timestamp}, edittime: {$this->edittime}\n" );
- if ( $timestamp != $this->edittime ) {
- $this->isConflict = true;
- if ( $this->section == 'new' ) {
- if ( $this->mArticle->getUserText() == $wgUser->getName() &&
- $this->mArticle->getComment() == $this->summary ) {
- // 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( __METHOD__ . ": duplicate new section submission; trigger edit conflict!\n" );
- } else {
- // New comment; suppress conflict.
- $this->isConflict = false;
- wfDebug( __METHOD__ .": conflict suppressed; new section\n" );
- }
- } elseif ( $this->section == '' && $this->userWasLastToEdit( $wgUser->getId(), $this->edittime ) ) {
- # Suppress edit conflict with self, except for section edits where merging is required.
- wfDebug( __METHOD__ . ": Suppressing edit conflict, same user.\n" );
- $this->isConflict = false;
- }
- }
-
- // If sectiontitle is set, use it, otherwise use the summary as the section title (for
- // backwards compatibility with old forms/bots).
- if ( $this->sectiontitle !== '' ) {
- $sectionTitle = $this->sectiontitle;
- } else {
- $sectionTitle = $this->summary;
- }
-
- if ( $this->isConflict ) {
- wfDebug( __METHOD__ . ": conflict! getting section '$this->section' for time '$this->edittime' (article time '{$timestamp}')\n" );
- $text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $sectionTitle, $this->edittime );
- } else {
- wfDebug( __METHOD__ . ": getting section '$this->section'\n" );
- $text = $this->mArticle->replaceSection( $this->section, $this->textbox1, $sectionTitle );
- }
- if ( is_null( $text ) ) {
- wfDebug( __METHOD__ . ": activating conflict; section replace failed.\n" );
- $this->isConflict = true;
- $text = $this->textbox1; // do not try to merge here!
- } elseif ( $this->isConflict ) {
- # Attempt merge
- if ( $this->mergeChangesInto( $text ) ) {
- // Successful merge! Maybe we should tell the user the good news?
- $this->isConflict = false;
- wfDebug( __METHOD__ . ": Suppressing edit conflict, successful merge.\n" );
- } else {
- $this->section = '';
- $this->textbox1 = $text;
- wfDebug( __METHOD__ . ": Keeping edit conflict, failed merge.\n" );
- }
- }
- if ( $this->isConflict ) {
- $status->setResult( false, self::AS_CONFLICT_DETECTED );
- wfProfileOut( __METHOD__ );
- return $status;
- }
- // 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...
- $status->fatal( 'hookaborted' );
- $status->value = self::AS_HOOK_ERROR;
- wfProfileOut( __METHOD__ );
- return $status;
- } elseif ( $this->hookError != '' ) {
- # ...or the hook could be expecting us to produce an error
- $status->fatal( 'hookaborted' );
- $status->value = self::AS_HOOK_ERROR_EXPECTED;
- wfProfileOut( __METHOD__ );
- return $status;
- }
- # Handle the user preference to force summaries here, but not for null edits
- if ( $this->section != 'new' && !$this->allowBlankSummary
- && $this->getOriginalContent() != $text
- && !Title::newFromRedirect( $text ) ) # check if it's not a redirect
- {
- if ( md5( $this->summary ) == $this->autoSumm ) {
- $this->missingSummary = true;
- $status->fatal( 'missingsummary' );
- $status->value = self::AS_SUMMARY_NEEDED;
- wfProfileOut( __METHOD__ );
- return $status;
- }
- }
- # And a similar thing for new sections
- if ( $this->section == 'new' && !$this->allowBlankSummary ) {
- if ( trim( $this->summary ) == '' ) {
- $this->missingSummary = true;
- $status->fatal( 'missingsummary' ); // or 'missingcommentheader' if $section == 'new'. Blegh
- $status->value = self::AS_SUMMARY_NEEDED;
- wfProfileOut( __METHOD__ );
- return $status;
- }
- }
- # All's well
- wfProfileIn( __METHOD__ . '-sectionanchor' );
- $sectionanchor = '';
- if ( $this->section == 'new' ) {
- if ( $this->textbox1 == '' ) {
- $this->missingComment = true;
- $status->fatal( 'missingcommenttext' );
- $status->value = self::AS_TEXTBOX_EMPTY;
- wfProfileOut( __METHOD__ . '-sectionanchor' );
- wfProfileOut( __METHOD__ );
- return $status;
- }
- if ( $this->sectiontitle !== '' ) {
- $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText( $this->sectiontitle );
- // If no edit summary was specified, create one automatically from the section
- // title and have it link to the new section. Otherwise, respect the summary as
- // passed.
- if ( $this->summary === '' ) {
- $cleanSectionTitle = $wgParser->stripSectionName( $this->sectiontitle );
- $this->summary = wfMsgForContent( 'newsectionsummary', $cleanSectionTitle );
- }
- } elseif ( $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 );
- $this->summary = wfMsgForContent( 'newsectionsummary', $cleanSummary );
- }
- } elseif ( $this->section != '' ) {
- # Try to get a section anchor from the section source, redirect to edited section if header found
- # XXX: might be better to integrate this into Article::replaceSection
- # for duplicate heading checking and maybe parsing
- $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 && strlen( $matches[2] ) > 0 ) {
- $sectionanchor = $wgParser->guessLegacySectionNameFromWikiText( $matches[2] );
- }
- }
- $result['sectionanchor'] = $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
- // so that later submission of conflict forms won't try to
- // replace that into a duplicated mess.
- $this->textbox1 = $text;
- $this->section = '';
- $status->value = self::AS_SUCCESS_UPDATE;
- }
- // Check for length errors again now that the section is merged in
- $this->kblength = (int)( strlen( $text ) / 1024 );
- if ( $this->kblength > $wgMaxArticleSize ) {
- $this->tooBig = true;
- $status->setResult( false, self::AS_MAX_ARTICLE_SIZE_EXCEEDED );
- wfProfileOut( __METHOD__ );
- return $status;
- }
- $flags = EDIT_DEFER_UPDATES | EDIT_AUTOSUMMARY |
- ( $new ? EDIT_NEW : EDIT_UPDATE ) |
- ( ( $this->minoredit && !$this->isNew ) ? EDIT_MINOR : 0 ) |
- ( $bot ? EDIT_FORCE_BOT : 0 );
- $doEditStatus = $this->mArticle->doEdit( $text, $this->summary, $flags );
- if ( $doEditStatus->isOK() ) {
- $result['redirect'] = Title::newFromRedirect( $text ) !== null;
- $this->commitWatch();
- wfProfileOut( __METHOD__ );
- return $status;
- } else {
- $this->isConflict = true;
- $doEditStatus->value = self::AS_END; // Destroys data doEdit() put in $status->value but who cares
- wfProfileOut( __METHOD__ );
- return $doEditStatus;
- }
- }
- /**
- * Commit the change of watch status
- */
- protected function commitWatch() {
- global $wgUser;
- if ( $this->watchthis xor $this->mTitle->userIsWatching() ) {
- $dbw = wfGetDB( DB_MASTER );
- $dbw->begin();
- if ( $this->watchthis ) {
- WatchAction::doWatch( $this->mTitle, $wgUser );
- } else {
- WatchAction::doUnwatch( $this->mTitle, $wgUser );
- }
- $dbw->commit();
- }
- }
- /**
- * Check if no edits were made by other users since
- * the time a user started editing the page. Limit to
- * 50 revisions for the sake of performance.
- *
- * @param $id int
- * @param $edittime string
- *
- * @return bool
- */
- protected function userWasLastToEdit( $id, $edittime ) {
- if( !$id ) return false;
- $dbw = wfGetDB( DB_MASTER );
- $res = $dbw->select( 'revision',
- 'rev_user',
- array(
- 'rev_page' => $this->mTitle->getArticleId(),
- 'rev_timestamp > '.$dbw->addQuotes( $dbw->timestamp($edittime) )
- ),
- __METHOD__,
- array( 'ORDER BY' => 'rev_timestamp ASC', 'LIMIT' => 50 ) );
- foreach ( $res as $row ) {
- if( $row->rev_user != $id ) {
- return false;
- }
- }
- return true;
- }
- /**
- * @private
- * @todo document
- *
- * @parma $editText string
- *
- * @return bool
- */
- function mergeChangesInto( &$editText ){
- wfProfileIn( __METHOD__ );
- $db = wfGetDB( DB_MASTER );
- // This is the revision the editor started from
- $baseRevision = $this->getBaseRevision();
- if ( is_null( $baseRevision ) ) {
- wfProfileOut( __METHOD__ );
- return false;
- }
- $baseText = $baseRevision->getText();
- // The current state, we want to merge updates into it
- $currentRevision = Revision::loadFromTitle( $db, $this->mTitle );
- if ( is_null( $currentRevision ) ) {
- wfProfileOut( __METHOD__ );
- return false;
- }
- $currentText = $currentRevision->g…
Large files files are truncated, but you can click here to view the full file