/** * Fetch additional information that is related to the saving that has just happened, * e.g. regarding the last edit date. In runs where this hook is not triggered, the * last DB entry (of MW) will be used to fill such properties. * * @note This method directly accesses a member of Article that is informally declared to * be private. However, there is no way to otherwise access an article's parseroutput for * the purpose of adding information there. If the private access ever becomes a problem, * a global/static variable appears to be the only way to get more article data to * LinksUpdate. * * @param WikiPage|Article $article WikiPage on 1.19 and later * @param Revision $rev * @param integer $baseID * @param User $user * * @return true */ public static function onNewRevisionFromEditComplete($article, Revision $rev, $baseID, User $user) { global $smwgPageSpecialProperties; if ($article->mPreparedEdit && $article->mPreparedEdit->output instanceof ParserOutput) { $output = $article->mPreparedEdit->output; $title = $article->getTitle(); if (!isset($title)) { return true; // nothing we can do } if (!isset($output->mSMWData)) { // no data container yet, make one $output->mSMWData = new SMWSemanticData(new SMWDIWikiPage($title->getDBkey(), $title->getNamespace(), $title->getInterwiki())); } $semdata = $output->mSMWData; } else { // give up, just keep the old data return true; } if (in_array('_MDAT', $smwgPageSpecialProperties)) { $timestamp = $article->getTimestamp(); $di = self::getDataItemFromMWTimestamp($timestamp); if (!is_null($di)) { $semdata->addPropertyObjectValue(new SMWDIProperty('_MDAT'), $di); } } if (in_array('_LEDT', $smwgPageSpecialProperties)) { $di = SMWDIWikiPage::newFromTitle($user->getUserPage()); if (!is_null($di)) { $semdata->addPropertyObjectValue(new SMWDIProperty('_LEDT'), $di); } } if (in_array('_NEWP', $smwgPageSpecialProperties)) { $semdata->addPropertyObjectValue(new SMWDIProperty('_NEWP'), new SMWDIBoolean(is_null($rev->getParentId()))); } return true; }
/** * @return bool */ protected function showHeader() { global $wgOut, $wgUser, $wgMaxArticleSize, $wgLang; global $wgAllowUserCss, $wgAllowUserJs; if ($this->mTitle->isTalkPage()) { $wgOut->addWikiMsg('talkpagetext'); } // Add edit notices $editNotices = $this->mTitle->getEditNotices($this->oldid); if (count($editNotices)) { $wgOut->addHTML(implode("\n", $editNotices)); } else { $msg = wfMessage('editnotice-notext'); if (!$msg->isDisabled()) { $wgOut->addHTML('<div class="mw-editnotice-notext">' . $msg->parseAsBlock() . '</div>'); } } if ($this->isConflict) { $wgOut->wrapWikiMsg("<div class='mw-explainconflict'>\n\$1\n</div>", 'explainconflict'); $this->edittime = $this->page->getTimestamp(); } else { if ($this->section != '' && !$this->isSectionEditSupported()) { // We use $this->section to much before this and getVal('wgSection') directly in other places // at this point we can't reset $this->section to '' to fallback to non-section editing. // Someone is welcome to try refactoring though $wgOut->showErrorPage('sectioneditnotsupported-title', 'sectioneditnotsupported-text'); return false; } if ($this->section != '' && $this->section != 'new') { if (!$this->summary && !$this->preview && !$this->diff) { $sectionTitle = self::extractSectionTitle($this->textbox1); // FIXME: use Content object if ($sectionTitle !== false) { $this->summary = "/* {$sectionTitle} */ "; } } } if ($this->missingComment) { $wgOut->wrapWikiMsg("<div id='mw-missingcommenttext'>\n\$1\n</div>", 'missingcommenttext'); } if ($this->missingSummary && $this->section != 'new') { $wgOut->wrapWikiMsg("<div id='mw-missingsummary'>\n\$1\n</div>", 'missingsummary'); } if ($this->missingSummary && $this->section == 'new') { $wgOut->wrapWikiMsg("<div id='mw-missingcommentheader'>\n\$1\n</div>", 'missingcommentheader'); } if ($this->blankArticle) { $wgOut->wrapWikiMsg("<div id='mw-blankarticle'>\n\$1\n</div>", 'blankarticle'); } if ($this->selfRedirect) { $wgOut->wrapWikiMsg("<div id='mw-selfredirect'>\n\$1\n</div>", 'selfredirect'); } if ($this->hookError !== '') { $wgOut->addWikiText($this->hookError); } if (!$this->checkUnicodeCompliantBrowser()) { $wgOut->addWikiMsg('nonunicodebrowser'); } if ($this->section != 'new') { $revision = $this->mArticle->getRevisionFetched(); if ($revision) { // Let sysop know that this will make private content public if saved if (!$revision->userCan(Revision::DELETED_TEXT, $wgUser)) { $wgOut->wrapWikiMsg("<div class='mw-warning plainlinks'>\n\$1\n</div>\n", 'rev-deleted-text-permission'); } elseif ($revision->isDeleted(Revision::DELETED_TEXT)) { $wgOut->wrapWikiMsg("<div class='mw-warning plainlinks'>\n\$1\n</div>\n", 'rev-deleted-text-view'); } if (!$revision->isCurrent()) { $this->mArticle->setOldSubtitle($revision->getId()); $wgOut->addWikiMsg('editingold'); } } elseif ($this->mTitle->exists()) { // Something went wrong $wgOut->wrapWikiMsg("<div class='errorbox'>\n\$1\n</div>\n", array('missing-revision', $this->oldid)); } } } if (wfReadOnly()) { $wgOut->wrapWikiMsg("<div id=\"mw-read-only-warning\">\n\$1\n</div>", array('readonlywarning', wfReadOnlyReason())); } elseif ($wgUser->isAnon()) { if ($this->formtype != 'preview') { $wgOut->wrapWikiMsg("<div id='mw-anon-edit-warning'>\n\$1\n</div>", array('anoneditwarning', '{{fullurl:Special:UserLogin|returnto={{FULLPAGENAMEE}}}}', '{{fullurl:Special:UserLogin/signup|returnto={{FULLPAGENAMEE}}}}')); } else { $wgOut->wrapWikiMsg("<div id=\"mw-anon-preview-warning\">\n\$1</div>", 'anonpreviewwarning'); } } else { if ($this->isCssJsSubpage) { # Check the skin exists if ($this->isWrongCaseCssJsPage) { $wgOut->wrapWikiMsg("<div class='error' id='mw-userinvalidcssjstitle'>\n\$1\n</div>", array('userinvalidcssjstitle', $this->mTitle->getSkinFromCssJsSubpage())); } if ($this->getTitle()->isSubpageOf($wgUser->getUserPage())) { if ($this->formtype !== 'preview') { if ($this->isCssSubpage && $wgAllowUserCss) { $wgOut->wrapWikiMsg("<div id='mw-usercssyoucanpreview'>\n\$1\n</div>", array('usercssyoucanpreview')); } if ($this->isJsSubpage && $wgAllowUserJs) { $wgOut->wrapWikiMsg("<div id='mw-userjsyoucanpreview'>\n\$1\n</div>", array('userjsyoucanpreview')); } } } } } if ($this->mTitle->isProtected('edit') && MWNamespace::getRestrictionLevels($this->mTitle->getNamespace()) !== array('')) { # Is the title semi-protected? if ($this->mTitle->isSemiProtected()) { $noticeMsg = 'semiprotectedpagewarning'; } else { # Then it must be protected based on static groups (regular) $noticeMsg = 'protectedpagewarning'; } LogEventsList::showLogExtract($wgOut, 'protect', $this->mTitle, '', array('lim' => 1, 'msgKey' => array($noticeMsg))); } if ($this->mTitle->isCascadeProtected()) { # Is this page under cascading protection from some source pages? /** @var Title[] $cascadeSources */ list($cascadeSources, ) = $this->mTitle->getCascadeProtectionSources(); $notice = "<div class='mw-cascadeprotectedwarning'>\n\$1\n"; $cascadeSourcesCount = count($cascadeSources); if ($cascadeSourcesCount > 0) { # Explain, and list the titles responsible foreach ($cascadeSources as $page) { $notice .= '* [[:' . $page->getPrefixedText() . "]]\n"; } } $notice .= '</div>'; $wgOut->wrapWikiMsg($notice, array('cascadeprotectedwarning', $cascadeSourcesCount)); } if (!$this->mTitle->exists() && $this->mTitle->getRestrictions('create')) { LogEventsList::showLogExtract($wgOut, 'protect', $this->mTitle, '', array('lim' => 1, 'showIfEmpty' => false, 'msgKey' => array('titleprotectedwarning'), 'wrap' => "<div class=\"mw-titleprotectedwarning\">\n\$1</div>")); } if ($this->kblength === false) { $this->kblength = (int) (strlen($this->textbox1) / 1024); } if ($this->tooBig || $this->kblength > $wgMaxArticleSize) { $wgOut->wrapWikiMsg("<div class='error' id='mw-edit-longpageerror'>\n\$1\n</div>", array('longpageerror', $wgLang->formatNum($this->kblength), $wgLang->formatNum($wgMaxArticleSize))); } else { if (!wfMessage('longpage-hint')->isDisabled()) { $wgOut->wrapWikiMsg("<div id='mw-edit-longpage-hint'>\n\$1\n</div>", array('longpage-hint', $wgLang->formatSize(strlen($this->textbox1)), strlen($this->textbox1))); } } # Add header copyright warning $this->showHeaderCopyrightWarning(); return true; }
/** * doSaveAsArticle store comment as article * * @static * * @param String $text * @param Article|WikiPage $article * @param User $user * @param array $metadata * @param string $summary * * @return Status TODO: Document */ protected static function doSaveAsArticle($text, $article, $user, $metadata = array(), $summary = '') { $result = null; $editPage = new EditPage($article); $editPage->edittime = $article->getTimestamp(); $editPage->textbox1 = self::removeMetadataTag($text); $editPage->summary = $summary; $editPage->watchthis = $user->isWatched($article->getTitle()); if (!empty($metadata)) { $editPage->textbox1 = $text . Xml::element('ac_metadata', $metadata, ' '); } $bot = $user->isAllowed('bot'); return $editPage->internalAttemptSave($result, $bot); }
/** * 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; global $wgContentHandlerUseDB; $status = Status::newGood(); if (!Hooks::run('EditPage::attemptSave', [$this])) { wfDebug("Hook 'EditPage::attemptSave' aborted article saving\n"); $status->fatal('hookaborted'); $status->value = self::AS_HOOK_ERROR; 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; 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; 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); 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; return $status; } if (!Hooks::run('EditFilter', [$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; 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; return $status; } if ($wgUser->isBlockedFrom($this->mTitle, false)) { // Auto-block user's IP if the account was "hard" blocked if (!wfReadOnly()) { $wgUser->spreadAnyEditBlock(); } # Check block state against master, thus 'false'. $status->setResult(false, self::AS_BLOCKED_PAGE_FOR_USER); return $status; } $this->contentLength = strlen($this->textbox1); if ($this->contentLength > $wgMaxArticleSize * 1024) { // Error will be displayed by showEditForm() $this->tooBig = true; $status->setResult(false, self::AS_CONTENT_TOO_BIG); return $status; } if (!$wgUser->isAllowed('edit')) { if ($wgUser->isAnon()) { $status->setResult(false, self::AS_READ_ONLY_PAGE_ANON); return $status; } else { $status->fatal('readonlytext'); $status->value = self::AS_READ_ONLY_PAGE_LOGGED; return $status; } } $changingContentModel = false; if ($this->contentModel !== $this->mTitle->getContentModel()) { if (!$wgContentHandlerUseDB) { $status->fatal('editpage-cannot-use-custom-model'); $status->value = self::AS_CANNOT_USE_CUSTOM_MODEL; return $status; } elseif (!$wgUser->isAllowed('editcontentmodel')) { $status->setResult(false, self::AS_NO_CHANGE_CONTENT_MODEL); return $status; } // Make sure the user can edit the page under the new content model too $titleWithNewContentModel = clone $this->mTitle; $titleWithNewContentModel->setContentModel($this->contentModel); if (!$titleWithNewContentModel->userCan('editcontentmodel', $wgUser) || !$titleWithNewContentModel->userCan('edit', $wgUser)) { $status->setResult(false, self::AS_NO_CHANGE_CONTENT_MODEL); return $status; } $changingContentModel = true; $oldContentModel = $this->mTitle->getContentModel(); } if ($this->changeTags) { $changeTagsStatus = ChangeTags::canAddTagsAccompanyingChange($this->changeTags, $wgUser); if (!$changeTagsStatus->isOK()) { $changeTagsStatus->value = self::AS_CHANGE_TAG_ERROR; return $changeTagsStatus; } } if (wfReadOnly()) { $status->fatal('readonlytext'); $status->value = self::AS_READ_ONLY_PAGE; return $status; } if ($wgUser->pingLimiter() || $wgUser->pingLimiter('linkpurge', 0) || $changingContentModel && $wgUser->pingLimiter('editcontentmodel')) { $status->fatal('actionthrottledtext'); $status->value = self::AS_RATE_LIMITED; 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); return $status; } # 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->page->loadPageData('fromdbmaster'); $new = !$this->page->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"); 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); return $status; } if (!$this->runPostMergeFilters($textbox_content, $status, $wgUser)) { 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->page->clear(); # Force reload of dates, etc. $timestamp = $this->page->getTimestamp(); $latest = $this->page->getLatest(); wfDebug("timestamp: {$timestamp}, edittime: {$this->edittime}\n"); // Check editRevId if set, which handles same-second timestamp collisions if ($timestamp != $this->edittime || $this->editRevId !== null && $this->editRevId != $latest) { $this->isConflict = true; if ($this->section == 'new') { if ($this->page->getUserText() == $wgUser->getName() && $this->page->getComment() == $this->newSectionSummary()) { // Probably a duplicate submission of a new comment. // This can happen when CDN 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}'" . " (id '{$this->editRevId}') (article time '{$timestamp}')\n"); // @TODO: replaceSectionAtRev() with base ID (not prior current) for ?oldid=X case // ...or disable section editing for non-current revisions (not exposed anyway). if ($this->editRevId !== null) { $content = $this->page->replaceSectionAtRev($this->section, $textbox_content, $sectionTitle, $this->editRevId); } else { $content = $this->page->replaceSectionContent($this->section, $textbox_content, $sectionTitle, $this->edittime); } } else { wfDebug(__METHOD__ . ": getting section '{$this->section}'\n"); $content = $this->page->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); return $status; } if (!$this->runPostMergeFilters($content, $status, $wgUser)) { 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; 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; 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; return $status; } # All's well $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::replaceSectionAtRev # 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; // 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; } if (!$this->allowSelfRedirect && $content->isRedirect() && $content->getRedirectTarget()->equals($this->getTitle())) { // If the page already redirects to itself, don't warn. $currentTarget = $this->getCurrentContent()->getRedirectTarget(); if (!$currentTarget || !$currentTarget->equals($this->getTitle())) { $this->selfRedirect = true; $status->fatal('selfredirect'); $status->value = self::AS_SELF_REDIRECT; return $status; } } // Check for length errors again now that the section is merged in $this->contentLength = strlen($this->toEditText($content)); if ($this->contentLength > $wgMaxArticleSize * 1024) { $this->tooBig = true; $status->setResult(false, self::AS_MAX_ARTICLE_SIZE_EXCEEDED); return $status; } $flags = EDIT_AUTOSUMMARY | ($new ? EDIT_NEW : EDIT_UPDATE) | ($this->minoredit && !$this->isNew ? EDIT_MINOR : 0) | ($bot ? EDIT_FORCE_BOT : 0); $doEditStatus = $this->page->doEditContent($content, $this->summary, $flags, false, $wgUser, $content->getDefaultFormat(), $this->changeTags); 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], ['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; } 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(); // If the content model changed, add a log entry if ($changingContentModel) { $this->addContentModelChangeLogEntry($wgUser, $new ? false : $oldContentModel, $this->contentModel, $this->summary); } return $status; }
/** * Fetch additional information that is related to the saving that has just happened, * e.g. regarding the last edit date. In runs where this hook is not triggered, the * last DB entry (of MW) will be used to fill such properties. * * @note This method directly accesses a member of Article that is informally declared to * be private. However, there is no way to otherwise access an article's parseroutput for * the purpose of adding information there. If the private access ever becomes a problem, * a global/static variable appears to be the only way to get more article data to * LinksUpdate. * * @param WikiPage|Article $article WikiPage on 1.19 and later * @param Revision $rev * @param integer $baseID * @param User $user * * @return true */ public static function onNewRevisionFromEditComplete($article, Revision $rev, $baseID, User $user) { global $smwgPageSpecialProperties; if ($article->mPreparedEdit && $article->mPreparedEdit->output instanceof ParserOutput) { $semdata = self::getSMWDataFromParserOutput($article->mPreparedEdit->output, $article->getTitle()); } else { // give up, just keep the old data return true; } if (in_array('_MDAT', $smwgPageSpecialProperties)) { $timestamp = $article->getTimestamp(); $di = self::getDataItemFromMWTimestamp($timestamp); if (!is_null($di)) { $semdata->addPropertyObjectValue(new SMWDIProperty('_MDAT'), $di); } } if (in_array('_LEDT', $smwgPageSpecialProperties)) { $di = SMWDIWikiPage::newFromTitle($user->getUserPage()); if (!is_null($di)) { $semdata->addPropertyObjectValue(new SMWDIProperty('_LEDT'), $di); } } if (in_array('_NEWP', $smwgPageSpecialProperties)) { $semdata->addPropertyObjectValue(new SMWDIProperty('_NEWP'), new SMWDIBoolean(is_null($rev->getParentId()))); } return true; }