/** * Writes the data in this object to the database * @param bool $noudp */ public function save($noudp = false) { global $wgPutIPinRC, $wgUseEnotif, $wgShowUpdatedMarker, $wgContLang; $dbw = wfGetDB(DB_MASTER); if (!is_array($this->mExtra)) { $this->mExtra = []; } if (!$wgPutIPinRC) { $this->mAttribs['rc_ip'] = ''; } # Strict mode fixups (not-NULL fields) foreach (['minor', 'bot', 'new', 'patrolled', 'deleted'] as $field) { $this->mAttribs["rc_{$field}"] = (int) $this->mAttribs["rc_{$field}"]; } # ...more fixups (NULL fields) foreach (['old_len', 'new_len'] as $field) { $this->mAttribs["rc_{$field}"] = isset($this->mAttribs["rc_{$field}"]) ? (int) $this->mAttribs["rc_{$field}"] : null; } # If our database is strict about IP addresses, use NULL instead of an empty string $strictIPs = in_array($dbw->getType(), ['oracle', 'postgres']); // legacy if ($strictIPs && $this->mAttribs['rc_ip'] == '') { unset($this->mAttribs['rc_ip']); } # Trim spaces on user supplied text $this->mAttribs['rc_comment'] = trim($this->mAttribs['rc_comment']); # Make sure summary is truncated (whole multibyte characters) $this->mAttribs['rc_comment'] = $wgContLang->truncate($this->mAttribs['rc_comment'], 255); # Fixup database timestamps $this->mAttribs['rc_timestamp'] = $dbw->timestamp($this->mAttribs['rc_timestamp']); $this->mAttribs['rc_id'] = $dbw->nextSequenceValue('recentchanges_rc_id_seq'); # # If we are using foreign keys, an entry of 0 for the page_id will fail, so use NULL if ($this->mAttribs['rc_cur_id'] == 0) { unset($this->mAttribs['rc_cur_id']); } # Insert new row $dbw->insert('recentchanges', $this->mAttribs, __METHOD__); # Set the ID $this->mAttribs['rc_id'] = $dbw->insertId(); # Notify extensions Hooks::run('RecentChange_save', [&$this]); if (count($this->tags)) { ChangeTags::addTags($this->tags, $this->mAttribs['rc_id'], $this->mAttribs['rc_this_oldid'], $this->mAttribs['rc_logid'], null, $this); } # Notify external application via UDP if (!$noudp) { $this->notifyRCFeeds(); } # E-mail notifications if ($wgUseEnotif || $wgShowUpdatedMarker) { $editor = $this->getPerformer(); $title = $this->getTitle(); // Never send an RC notification email about categorization changes if ($this->mAttribs['rc_type'] != RC_CATEGORIZE && Hooks::run('AbortEmailNotification', [$editor, $title, $this])) { // @FIXME: This would be better as an extension hook // Send emails or email jobs once this row is safely committed $dbw->onTransactionIdle(function () use($editor, $title) { $enotif = new EmailNotification(); $enotif->notifyOnPageChange($editor, $title, $this->mAttribs['rc_timestamp'], $this->mAttribs['rc_comment'], $this->mAttribs['rc_minor'], $this->mAttribs['rc_last_oldid'], $this->mExtra['pageStatus']); }, __METHOD__); } } // Update the cached list of active users if ($this->mAttribs['rc_user'] > 0) { JobQueueGroup::singleton()->lazyPush(RecentChangesUpdateJob::newCacheUpdateJob()); } }
/** * Writes the data in this object to the database * @param bool $noudp */ public function save($noudp = false) { global $wgPutIPinRC, $wgUseEnotif, $wgShowUpdatedMarker, $wgContLang; $dbw = wfGetDB(DB_MASTER); if (!is_array($this->mExtra)) { $this->mExtra = array(); } if (!$wgPutIPinRC) { $this->mAttribs['rc_ip'] = ''; } # If our database is strict about IP addresses, use NULL instead of an empty string if ($dbw->strictIPs() && $this->mAttribs['rc_ip'] == '') { unset($this->mAttribs['rc_ip']); } # Trim spaces on user supplied text $this->mAttribs['rc_comment'] = trim($this->mAttribs['rc_comment']); # Make sure summary is truncated (whole multibyte characters) $this->mAttribs['rc_comment'] = $wgContLang->truncate($this->mAttribs['rc_comment'], 255); # Fixup database timestamps $this->mAttribs['rc_timestamp'] = $dbw->timestamp($this->mAttribs['rc_timestamp']); $this->mAttribs['rc_id'] = $dbw->nextSequenceValue('recentchanges_rc_id_seq'); # # If we are using foreign keys, an entry of 0 for the page_id will fail, so use NULL if ($dbw->cascadingDeletes() && $this->mAttribs['rc_cur_id'] == 0) { unset($this->mAttribs['rc_cur_id']); } # Insert new row $dbw->insert('recentchanges', $this->mAttribs, __METHOD__); # Set the ID $this->mAttribs['rc_id'] = $dbw->insertId(); # Notify extensions Hooks::run('RecentChange_save', array(&$this)); # Notify external application via UDP if (!$noudp) { $this->notifyRCFeeds(); } # E-mail notifications if ($wgUseEnotif || $wgShowUpdatedMarker) { $editor = $this->getPerformer(); $title = $this->getTitle(); // Never send an RC notification email about categorization changes if ($this->mAttribs['rc_type'] != RC_CATEGORIZE) { if (Hooks::run('AbortEmailNotification', array($editor, $title, $this))) { # @todo FIXME: This would be better as an extension hook $enotif = new EmailNotification(); $enotif->notifyOnPageChange($editor, $title, $this->mAttribs['rc_timestamp'], $this->mAttribs['rc_comment'], $this->mAttribs['rc_minor'], $this->mAttribs['rc_last_oldid'], $this->mExtra['pageStatus']); } } } // Update the cached list of active users if ($this->mAttribs['rc_user'] > 0) { JobQueueGroup::singleton()->lazyPush(RecentChangesUpdateJob::newCacheUpdateJob()); } }
/** * Do standard deferred updates after page edit. * Update links tables, site stats, search index and message cache. * Purges pages that include this page if the text was changed here. * Every 100th edit, prune the recent changes table. * * @param Revision $revision * @param User $user User object that did the revision * @param array $options Array of options, following indexes are used: * - changed: boolean, whether the revision changed the content (default true) * - created: boolean, whether the revision created the page (default false) * - moved: boolean, whether the page was moved (default false) * - oldcountable: boolean, null, or string 'no-change' (default null): * - boolean: whether the page was counted as an article before that * revision, only used in changed is true and created is false * - null: if created is false, don't update the article count; if created * is true, do update the article count * - 'no-change': don't update the article count, ever */ public function doEditUpdates(Revision $revision, User $user, array $options = array()) { global $wgEnableParserCache; $options += array('changed' => true, 'created' => false, 'moved' => false, 'oldcountable' => null); $content = $revision->getContent(); // Parse the text // Be careful not to do pre-save transform twice: $text is usually // already pre-save transformed once. if (!$this->mPreparedEdit || $this->mPreparedEdit->output->getFlag('vary-revision')) { wfDebug(__METHOD__ . ": No prepared edit or vary-revision is set...\n"); $editInfo = $this->prepareContentForEdit($content, $revision, $user); } else { wfDebug(__METHOD__ . ": No vary-revision, using prepared edit...\n"); $editInfo = $this->mPreparedEdit; } // Save it to the parser cache if ($wgEnableParserCache) { $parserCache = ParserCache::singleton(); $parserCache->save($editInfo->output, $this, $editInfo->popts, $editInfo->timestamp, $editInfo->revid); } // Update the links tables and other secondary data if ($content) { $recursive = $options['changed']; // bug 50785 $updates = $content->getSecondaryDataUpdates($this->getTitle(), null, $recursive, $editInfo->output); DataUpdate::runUpdates($updates); } Hooks::run('ArticleEditUpdates', array(&$this, &$editInfo, $options['changed'])); if (Hooks::run('ArticleEditUpdatesDeleteFromRecentchanges', array(&$this))) { JobQueueGroup::singleton()->push(array(RecentChangesUpdateJob::newPurgeJob(), RecentChangesUpdateJob::newCacheUpdateJob())); } if (!$this->exists()) { return; } $id = $this->getId(); $title = $this->mTitle->getPrefixedDBkey(); $shortTitle = $this->mTitle->getDBkey(); if ($options['oldcountable'] === 'no-change' || !$options['changed'] && !$options['moved']) { $good = 0; } elseif ($options['created']) { $good = (int) $this->isCountable($editInfo); } elseif ($options['oldcountable'] !== null) { $good = (int) $this->isCountable($editInfo) - (int) $options['oldcountable']; } else { $good = 0; } $edits = $options['changed'] ? 1 : 0; $total = $options['created'] ? 1 : 0; DeferredUpdates::addUpdate(new SiteStatsUpdate(0, $edits, $good, $total)); DeferredUpdates::addUpdate(new SearchUpdate($id, $title, $content)); // If this is another user's talk page, update newtalk. // Don't do this if $options['changed'] = false (null-edits) nor if // it's a minor edit and the user doesn't want notifications for those. if ($options['changed'] && $this->mTitle->getNamespace() == NS_USER_TALK && $shortTitle != $user->getTitleKey() && !($revision->isMinor() && $user->isAllowed('nominornewtalk'))) { $recipient = User::newFromName($shortTitle, false); if (!$recipient) { wfDebug(__METHOD__ . ": invalid username\n"); } else { // Allow extensions to prevent user notification when a new message is added to their talk page if (Hooks::run('ArticleEditUpdateNewTalk', array(&$this, $recipient))) { if (User::isIP($shortTitle)) { // An anonymous user $recipient->setNewtalk(true, $revision); } elseif ($recipient->isLoggedIn()) { $recipient->setNewtalk(true, $revision); } else { wfDebug(__METHOD__ . ": don't need to notify a nonexistent user\n"); } } } } if ($this->mTitle->getNamespace() == NS_MEDIAWIKI) { // XXX: could skip pseudo-messages like js/css here, based on content model. $msgtext = $content ? $content->getWikitextForTransclusion() : null; if ($msgtext === false || $msgtext === null) { $msgtext = ''; } MessageCache::singleton()->replace($shortTitle, $msgtext); } if ($options['created']) { self::onArticleCreate($this->mTitle); } elseif ($options['changed']) { // bug 50785 self::onArticleEdit($this->mTitle); } }