/** * Returns the page's content model id (see the CONTENT_MODEL_XXX constants). * * Will use the revisions actual content model if the page exists, * and the page's default if the page doesn't exist yet. * * @return String * * @since 1.21 */ public function getContentModel() { if ($this->exists()) { # look at the revision's actual content model $rev = $this->getRevision(); if ($rev !== null) { return $rev->getContentModel(); } else { $title = $this->mTitle->getPrefixedDBkey(); wfWarn("Page {$title} exists but has no (visible) revisions!"); } } # use the default model for this page return $this->mTitle->getContentModel(); }
/** * Does various sanity checks that the move is * valid. Only things based on the two titles * should be checked here. * * @return Status */ public function isValidMove() { global $wgContentHandlerUseDB; $status = new Status(); if ($this->oldTitle->equals($this->newTitle)) { $status->fatal('selfmove'); } if (!$this->oldTitle->isMovable()) { $status->fatal('immobile-source-namespace', $this->oldTitle->getNsText()); } if ($this->newTitle->isExternal()) { $status->fatal('immobile-target-namespace-iw'); } if (!$this->newTitle->isMovable()) { $status->fatal('immobile-target-namespace', $this->newTitle->getNsText()); } $oldid = $this->oldTitle->getArticleID(); if (strlen($this->newTitle->getDBkey()) < 1) { $status->fatal('articleexists'); } if ($this->oldTitle->getDBkey() == '' || !$oldid || $this->newTitle->getDBkey() == '') { $status->fatal('badarticleerror'); } # The move is allowed only if (1) the target doesn't exist, or # (2) the target is a redirect to the source, and has no history # (so we can undo bad moves right after they're done). if ($this->newTitle->getArticleID() && !$this->isValidMoveTarget()) { $status->fatal('articleexists'); } // Content model checks if (!$wgContentHandlerUseDB && $this->oldTitle->getContentModel() !== $this->newTitle->getContentModel()) { // can't move a page if that would change the page's content model $status->fatal('bad-target-model', ContentHandler::getLocalizedName($this->oldTitle->getContentModel()), ContentHandler::getLocalizedName($this->newTitle->getContentModel())); } // Image-specific checks if ($this->oldTitle->inNamespace(NS_FILE)) { $status->merge($this->isValidFileMove()); } if ($this->newTitle->inNamespace(NS_FILE) && !$this->oldTitle->inNamespace(NS_FILE)) { $status->fatal('nonfile-cannot-move-to-file'); } // Hook for extensions to say a title can't be moved for technical reasons Hooks::run('MovePageIsValidMove', array($this->oldTitle, $this->newTitle, $status)); return $status; }
/** * Returns the page's content model id (see the CONTENT_MODEL_XXX constants). * * Will use the revisions actual content model if the page exists, * and the page's default if the page doesn't exist yet. * * @return string * * @since 1.21 */ public function getContentModel() { if ($this->exists()) { $cache = ObjectCache::getMainWANInstance(); return $cache->getWithSetCallback($cache->makeKey('page', 'content-model', $this->getLatest()), $cache::TTL_MONTH, function () { $rev = $this->getRevision(); if ($rev) { // Look at the revision's actual content model return $rev->getContentModel(); } else { $title = $this->mTitle->getPrefixedDBkey(); wfWarn("Page {$title} exists but has no (visible) revisions!"); return $this->mTitle->getContentModel(); } }); } // use the default model for this page return $this->mTitle->getContentModel(); }
/** * Returns the appropriate ContentHandler singleton for the given title. * * @since 1.21 * * @param Title $title * * @return ContentHandler */ public static function getForTitle(Title $title) { $modelId = $title->getContentModel(); return ContentHandler::getForModelID($modelId); }
public function onSubmit(array $data) { global $wgContLang; if ($data['pagetitle'] === '') { // Initial form view of special page, pass return false; } // At this point, it has to be a POST request. This is enforced by HTMLForm, // but lets be safe verify that. if (!$this->getRequest()->wasPosted()) { throw new RuntimeException("Form submission was not POSTed"); } $this->title = Title::newFromText($data['pagetitle']); $user = $this->getUser(); // Check permissions and make sure the user has permission to edit the specific page $errors = $this->title->getUserPermissionsErrors('editcontentmodel', $user); $errors = wfMergeErrorArrays($errors, $this->title->getUserPermissionsErrors('edit', $user)); if ($errors) { $out = $this->getOutput(); $wikitext = $out->formatPermissionsErrorMessage($errors); // Hack to get our wikitext parsed return Status::newFatal(new RawMessage('$1', array($wikitext))); } $page = WikiPage::factory($this->title); if ($this->oldRevision === null) { $this->oldRevision = $page->getRevision() ?: false; } $oldModel = $this->title->getContentModel(); if ($this->oldRevision) { $oldContent = $this->oldRevision->getContent(); try { $newContent = ContentHandler::makeContent($oldContent->getNativeData(), $this->title, $data['model']); } catch (MWException $e) { return Status::newFatal($this->msg('changecontentmodel-cannot-convert')->params($this->title->getPrefixedText(), ContentHandler::getLocalizedName($data['model']))); } } else { // Page doesn't exist, create an empty content object $newContent = ContentHandler::getForModelID($data['model'])->makeEmptyContent(); } $flags = $this->oldRevision ? EDIT_UPDATE : EDIT_NEW; if ($user->isAllowed('bot')) { $flags |= EDIT_FORCE_BOT; } $log = new ManualLogEntry('contentmodel', 'change'); $log->setPerformer($user); $log->setTarget($this->title); $log->setComment($data['reason']); $log->setParameters(array('4::oldmodel' => $oldModel, '5::newmodel' => $data['model'])); $formatter = LogFormatter::newFromEntry($log); $formatter->setContext(RequestContext::newExtraneousContext($this->title)); $reason = $formatter->getPlainActionText(); if ($data['reason'] !== '') { $reason .= $this->msg('colon-separator')->inContentLanguage()->text() . $data['reason']; } # Truncate for whole multibyte characters. $reason = $wgContLang->truncate($reason, 255); $status = $page->doEditContent($newContent, $reason, $flags, $this->oldRevision ? $this->oldRevision->getId() : false, $user); if (!$status->isOK()) { return $status; } $logid = $log->insert(); $log->publish($logid); return $status; }
/** * Get a result array with information about a title * @param int $pageid Page ID (negative for missing titles) * @param Title $title * @return array|null */ private function extractPageInfo($pageid, $title) { $pageInfo = array(); // $title->exists() needs pageid, which is not set for all title objects $titleExists = $pageid > 0; $ns = $title->getNamespace(); $dbkey = $title->getDBkey(); $pageInfo['contentmodel'] = $title->getContentModel(); $pageInfo['pagelanguage'] = $title->getPageLanguage()->getCode(); if ($titleExists) { $pageInfo['touched'] = wfTimestamp(TS_ISO_8601, $this->pageTouched[$pageid]); $pageInfo['lastrevid'] = intval($this->pageLatest[$pageid]); $pageInfo['length'] = intval($this->pageLength[$pageid]); if (isset($this->pageIsRedir[$pageid]) && $this->pageIsRedir[$pageid]) { $pageInfo['redirect'] = true; } if ($this->pageIsNew[$pageid]) { $pageInfo['new'] = true; } } if (!is_null($this->params['token'])) { $tokenFunctions = $this->getTokenFunctions(); $pageInfo['starttimestamp'] = wfTimestamp(TS_ISO_8601, time()); foreach ($this->params['token'] as $t) { $val = call_user_func($tokenFunctions[$t], $pageid, $title); if ($val === false) { $this->setWarning("Action '{$t}' is not allowed for the current user"); } else { $pageInfo[$t . 'token'] = $val; } } } if ($this->fld_protection) { $pageInfo['protection'] = array(); if (isset($this->protections[$ns][$dbkey])) { $pageInfo['protection'] = $this->protections[$ns][$dbkey]; } ApiResult::setIndexedTagName($pageInfo['protection'], 'pr'); $pageInfo['restrictiontypes'] = array(); if (isset($this->restrictionTypes[$ns][$dbkey])) { $pageInfo['restrictiontypes'] = $this->restrictionTypes[$ns][$dbkey]; } ApiResult::setIndexedTagName($pageInfo['restrictiontypes'], 'rt'); } if ($this->fld_watched) { $pageInfo['watched'] = isset($this->watched[$ns][$dbkey]); } if ($this->fld_watchers) { if (isset($this->watchers[$ns][$dbkey])) { $pageInfo['watchers'] = $this->watchers[$ns][$dbkey]; } elseif ($this->showZeroWatchers) { $pageInfo['watchers'] = 0; } } if ($this->fld_notificationtimestamp) { $pageInfo['notificationtimestamp'] = ''; if (isset($this->notificationtimestamps[$ns][$dbkey])) { $pageInfo['notificationtimestamp'] = wfTimestamp(TS_ISO_8601, $this->notificationtimestamps[$ns][$dbkey]); } } if ($this->fld_talkid && isset($this->talkids[$ns][$dbkey])) { $pageInfo['talkid'] = $this->talkids[$ns][$dbkey]; } if ($this->fld_subjectid && isset($this->subjectids[$ns][$dbkey])) { $pageInfo['subjectid'] = $this->subjectids[$ns][$dbkey]; } if ($this->fld_url) { $pageInfo['fullurl'] = wfExpandUrl($title->getFullURL(), PROTO_CURRENT); $pageInfo['editurl'] = wfExpandUrl($title->getFullURL('action=edit'), PROTO_CURRENT); $pageInfo['canonicalurl'] = wfExpandUrl($title->getFullURL(), PROTO_CANONICAL); } if ($this->fld_readable) { $pageInfo['readable'] = $title->userCan('read', $this->getUser()); } if ($this->fld_preload) { if ($titleExists) { $pageInfo['preload'] = ''; } else { $text = null; Hooks::run('EditFormPreloadText', array(&$text, &$title)); $pageInfo['preload'] = $text; } } if ($this->fld_displaytitle) { if (isset($this->displaytitles[$pageid])) { $pageInfo['displaytitle'] = $this->displaytitles[$pageid]; } else { $pageInfo['displaytitle'] = $title->getPrefixedText(); } } if ($this->params['testactions']) { $limit = $this->getMain()->canApiHighLimits() ? self::LIMIT_SML1 : self::LIMIT_SML2; if ($this->countTestedActions >= $limit) { return null; // force a continuation } $user = $this->getUser(); $pageInfo['actions'] = array(); foreach ($this->params['testactions'] as $action) { $this->countTestedActions++; $pageInfo['actions'][$action] = $title->userCan($action, $user); } } return $pageInfo; }
/** * Attempt submission (no UI) * * @param array $result Array to add statuses to, currently with the * possible keys: * - spam (string): Spam string from content if any spam is detected by * matchSpamRegex. * - sectionanchor (string): Section anchor for a section save. * - nullEdit (boolean): Set if doEditContent is OK. True if null edit, * false otherwise. * - redirect (bool): Set if doEditContent is OK. True if resulting * revision is a redirect. * @param bool $bot True if edit is being made under the bot right. * * @return Status Status object, possibly with a message, but always with * one of the AS_* constants in $status->value, * * @todo 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 $wgUser, $wgRequest, $wgParser, $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; } $spam = $wgRequest->getText('wpAntispam'); if ($spam !== '') { wfDebugLog('SimpleAntiSpam', $wgUser->getName() . ' editing "' . $this->mTitle->getPrefixedText() . '" submitted bogus field "' . $spam . '"'); $status->fatal('spamprotectionmatch', false); $status->value = self::AS_SPAM_ERROR; wfProfileOut(__METHOD__ . '-checks'); wfProfileOut(__METHOD__); return $status; } try { # Construct Content object $textbox_content = $this->toEditContent($this->textbox1); } catch (MWContentSerializationException $ex) { $status->fatal('content-failed-to-parse', $this->contentModel, $this->contentFormat, $ex->getMessage()); $status->value = self::AS_PARSE_ERROR; wfProfileOut(__METHOD__ . '-checks'); wfProfileOut(__METHOD__); return $status; } # Check image redirect if ($this->mTitle->getNamespace() == NS_FILE && $textbox_content->isRedirect() && !$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 && $this->section == 'new') { # $wgSpamRegex is enforced on this new heading/summary because, unlike # regular summaries, it is added to the actual wikitext. if ($this->sectiontitle !== '') { # This branch is taken when the API is used with the 'sectiontitle' parameter. $match = self::matchSpamRegex($this->sectiontitle); } else { # This branch is taken when the "Add Topic" user interface is used, or the API # is used with the 'summary' parameter. $match = self::matchSpamRegex($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 (!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 ($this->contentModel !== $this->mTitle->getContentModel() && !$wgUser->isAllowed('editcontentmodel')) { $status->setResult(false, self::AS_NO_CHANGE_CONTENT_MODEL); 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() || $wgUser->pingLimiter('linkpurge', 0)) { $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'); # Load the page data from the master. If anything changes in the meantime, # we detect it by using page_latest like a token in a 1 try compare-and-swap. $this->mArticle->loadPageData('fromdbmaster'); $new = !$this->mArticle->exists(); if ($new) { // Late check for create permission, just in case *PARANOIA* if (!$this->mTitle->userCan('create', $wgUser)) { $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 page if it's blank or if it's a MediaWiki: // message with content equivalent to default (allow empty pages // in this case to disable messages, see bug 50124) $defaultMessageText = $this->mTitle->getDefaultMessageText(); if ($this->mTitle->getNamespace() === NS_MEDIAWIKI && $defaultMessageText !== false) { $defaultText = $defaultMessageText; } else { $defaultText = ''; } if (!$this->allowBlankArticle && $this->textbox1 === $defaultText) { $this->blankArticle = true; $status->fatal('blankarticle'); $status->setResult(false, self::AS_BLANK_ARTICLE); wfProfileOut(__METHOD__); return $status; } if (!$this->runPostMergeFilters($textbox_content, $status, $wgUser)) { wfProfileOut(__METHOD__); return $status; } $content = $textbox_content; $result['sectionanchor'] = ''; if ($this->section == 'new') { if ($this->sectiontitle !== '') { // Insert the section title above the content. $content = $content->addSectionHeader($this->sectiontitle); } elseif ($this->summary !== '') { // Insert the section title above the content. $content = $content->addSectionHeader($this->summary); } $this->summary = $this->newSectionSummary($result['sectionanchor']); } $status->value = self::AS_SUCCESS_NEW_ARTICLE; } else { # not $new # 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->newSectionSummary()) { // 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 == '' && Revision::userWasLastToEdit(DB_MASTER, $this->mTitle->getArticleID(), $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. if ($this->sectiontitle !== '') { $sectionTitle = $this->sectiontitle; } else { $sectionTitle = $this->summary; } $content = null; if ($this->isConflict) { wfDebug(__METHOD__ . ": conflict! getting section '{$this->section}' for time '{$this->edittime}'" . " (article time '{$timestamp}')\n"); $content = $this->mArticle->replaceSectionContent($this->section, $textbox_content, $sectionTitle, $this->edittime); } else { wfDebug(__METHOD__ . ": getting section '{$this->section}'\n"); $content = $this->mArticle->replaceSectionContent($this->section, $textbox_content, $sectionTitle); } if (is_null($content)) { wfDebug(__METHOD__ . ": activating conflict; section replace failed.\n"); $this->isConflict = true; $content = $textbox_content; // do not try to merge here! } elseif ($this->isConflict) { # Attempt merge if ($this->mergeChangesIntoContent($content)) { // 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 = ContentHandler::getContentText($content); wfDebug(__METHOD__ . ": Keeping edit conflict, failed merge.\n"); } } if ($this->isConflict) { $status->setResult(false, self::AS_CONFLICT_DETECTED); wfProfileOut(__METHOD__); return $status; } if (!$this->runPostMergeFilters($content, $status, $wgUser)) { wfProfileOut(__METHOD__); return $status; } if ($this->section == 'new') { // Handle the user preference to force summaries here if (!$this->allowBlankSummary && 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; } // Do not allow the user to post an empty comment if ($this->textbox1 == '') { $this->missingComment = true; $status->fatal('missingcommenttext'); $status->value = self::AS_TEXTBOX_EMPTY; wfProfileOut(__METHOD__); return $status; } } elseif (!$this->allowBlankSummary && !$content->equals($this->getOriginalContent($wgUser)) && !$content->isRedirect() && md5($this->summary) == $this->autoSumm) { $this->missingSummary = true; $status->fatal('missingsummary'); $status->value = self::AS_SUMMARY_NEEDED; wfProfileOut(__METHOD__); return $status; } # All's well wfProfileIn(__METHOD__ . '-sectionanchor'); $sectionanchor = ''; if ($this->section == 'new') { $this->summary = $this->newSectionSummary($sectionanchor); } 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 = $this->toEditText($content); $this->section = ''; $status->value = self::AS_SUCCESS_UPDATE; } // Check for length errors again now that the section is merged in $this->kblength = (int) (strlen($this->toEditText($content)) / 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->doEditContent($content, $this->summary, $flags, false, null, $content->getDefaultFormat()); if (!$doEditStatus->isOK()) { // Failure from doEdit() // Show the edit conflict page for certain recognized errors from doEdit(), // but don't show it for errors from extension hooks $errors = $doEditStatus->getErrorsArray(); if (in_array($errors[0][0], array('edit-gone-missing', 'edit-conflict', 'edit-already-exists'))) { $this->isConflict = true; // Destroys data doEdit() put in $status->value but who cares $doEditStatus->value = self::AS_END; } wfProfileOut(__METHOD__); return $doEditStatus; } $result['nullEdit'] = $doEditStatus->hasMessage('edit-no-change'); if ($result['nullEdit']) { // We don't know if it was a null edit until now, so increment here $wgUser->pingLimiter('linkpurge'); } $result['redirect'] = $content->isRedirect(); $this->updateWatchlist(); wfProfileOut(__METHOD__); return $status; }
/** * Move page to a title which is either a redirect to the * source page or nonexistent * * @fixme This was basically directly moved from Title, it should be split into smaller functions * @param User $user the User doing the move * @param Title $nt The page to move to, which should be a redirect or nonexistent * @param string $reason The reason for the move * @param bool $createRedirect Whether to leave a redirect at the old title. Does not check * if the user has the suppressredirect right * @throws MWException */ private function moveToInternal(User $user, &$nt, $reason = '', $createRedirect = true) { global $wgContLang; if ($nt->exists()) { $moveOverRedirect = true; $logType = 'move_redir'; } else { $moveOverRedirect = false; $logType = 'move'; } if ($createRedirect) { if ($this->oldTitle->getNamespace() == NS_CATEGORY && !wfMessage('category-move-redirect-override')->inContentLanguage()->isDisabled()) { $redirectContent = new WikitextContent(wfMessage('category-move-redirect-override')->params($nt->getPrefixedText())->inContentLanguage()->plain()); } else { $contentHandler = ContentHandler::getForTitle($this->oldTitle); $redirectContent = $contentHandler->makeRedirectContent($nt, wfMessage('move-redirect-text')->inContentLanguage()->plain()); } // NOTE: If this page's content model does not support redirects, $redirectContent will be null. } else { $redirectContent = null; } // Figure out whether the content model is no longer the default $oldDefault = ContentHandler::getDefaultModelFor($this->oldTitle); $contentModel = $this->oldTitle->getContentModel(); $newDefault = ContentHandler::getDefaultModelFor($nt); $defaultContentModelChanging = $oldDefault !== $newDefault && $oldDefault === $contentModel; // bug 57084: log_page should be the ID of the *moved* page $oldid = $this->oldTitle->getArticleID(); $logTitle = clone $this->oldTitle; $logEntry = new ManualLogEntry('move', $logType); $logEntry->setPerformer($user); $logEntry->setTarget($logTitle); $logEntry->setComment($reason); $logEntry->setParameters(array('4::target' => $nt->getPrefixedText(), '5::noredir' => $redirectContent ? '0' : '1')); $formatter = LogFormatter::newFromEntry($logEntry); $formatter->setContext(RequestContext::newExtraneousContext($this->oldTitle)); $comment = $formatter->getPlainActionText(); if ($reason) { $comment .= wfMessage('colon-separator')->inContentLanguage()->text() . $reason; } # Truncate for whole multibyte characters. $comment = $wgContLang->truncate($comment, 255); $dbw = wfGetDB(DB_MASTER); $oldpage = WikiPage::factory($this->oldTitle); $oldcountable = $oldpage->isCountable(); $newpage = WikiPage::factory($nt); if ($moveOverRedirect) { $newid = $nt->getArticleID(); $newcontent = $newpage->getContent(); # Delete the old redirect. We don't save it to history since # by definition if we've got here it's rather uninteresting. # We have to remove it so that the next step doesn't trigger # a conflict on the unique namespace+title index... $dbw->delete('page', array('page_id' => $newid), __METHOD__); $newpage->doDeleteUpdates($newid, $newcontent); } # Save a null revision in the page's history notifying of the move $nullRevision = Revision::newNullRevision($dbw, $oldid, $comment, true, $user); if (!is_object($nullRevision)) { throw new MWException('No valid null revision produced in ' . __METHOD__); } $nullRevision->insertOn($dbw); # Change the name of the target page: $dbw->update('page', array('page_namespace' => $nt->getNamespace(), 'page_title' => $nt->getDBkey()), array('page_id' => $oldid), __METHOD__); // clean up the old title before reset article id - bug 45348 if (!$redirectContent) { WikiPage::onArticleDelete($this->oldTitle); } $this->oldTitle->resetArticleID(0); // 0 == non existing $nt->resetArticleID($oldid); $newpage->loadPageData(WikiPage::READ_LOCKING); // bug 46397 $newpage->updateRevisionOn($dbw, $nullRevision); Hooks::run('NewRevisionFromEditComplete', array($newpage, $nullRevision, $nullRevision->getParentId(), $user)); $newpage->doEditUpdates($nullRevision, $user, array('changed' => false, 'moved' => true, 'oldcountable' => $oldcountable)); // If the default content model changes, we need to populate rev_content_model if ($defaultContentModelChanging) { $dbw->update('revision', array('rev_content_model' => $contentModel), array('rev_page' => $nt->getArticleID(), 'rev_content_model IS NULL'), __METHOD__); } if (!$moveOverRedirect) { WikiPage::onArticleCreate($nt); } # Recreate the redirect, this time in the other direction. if ($redirectContent) { $redirectArticle = WikiPage::factory($this->oldTitle); $redirectArticle->loadFromRow(false, WikiPage::READ_LOCKING); // bug 46397 $newid = $redirectArticle->insertOn($dbw); if ($newid) { // sanity $this->oldTitle->resetArticleID($newid); $redirectRevision = new Revision(array('title' => $this->oldTitle, 'page' => $newid, 'user_text' => $user->getName(), 'user' => $user->getId(), 'comment' => $comment, 'content' => $redirectContent)); $redirectRevision->insertOn($dbw); $redirectArticle->updateRevisionOn($dbw, $redirectRevision, 0); Hooks::run('NewRevisionFromEditComplete', array($redirectArticle, $redirectRevision, false, $user)); $redirectArticle->doEditUpdates($redirectRevision, $user, array('created' => true)); } } # Log the move $logid = $logEntry->insert(); $logEntry->publish($logid); }
/** * @param $article Article */ public function __construct(Article $article) { $this->mArticle = $article; $this->mTitle = $article->getTitle(); $this->contentModel = $this->mTitle->getContentModel(); $handler = ContentHandler::getForModelID($this->contentModel); $this->contentFormat = $handler->getDefaultFormat(); }
public static function onTitleMoveComplete(Title $oldTitle, Title $newTitle, User $user, $pageid, $redirid, $reason) { if ($newTitle->getContentModel() === CONTENT_MODEL_FLOW_BOARD) { Container::get('board_mover')->commit(); } return true; }