public function testBatchAddWatch() { $itemOne = new WatchedItem($this->getMockUser(1), new TitleValue(0, 'Title1'), null); $itemTwo = new WatchedItem($this->getMockUser(3), Title::newFromText('Title2'), '20150101010101'); $store = $this->getMockWatchedItemStore(); $store->expects($this->exactly(2))->method('addWatchBatchForUser'); $store->expects($this->at(0))->method('addWatchBatchForUser')->with($itemOne->getUser(), [$itemOne->getTitle()->getSubjectPage(), $itemOne->getTitle()->getTalkPage()]); $store->expects($this->at(1))->method('addWatchBatchForUser')->with($itemTwo->getUser(), [$itemTwo->getTitle()->getSubjectPage(), $itemTwo->getTitle()->getTalkPage()]); $this->setService('WatchedItemStore', $store); WatchedItem::batchAddWatch([$itemOne, $itemTwo]); }
/** * Adds users to watchlist if: * - User is watching parent page * - User has 'watchlistsubpages' turned on * * @param $watchers array of user ID * @param $title Title object * @param $editor User object * @param $notificationTimeoutSql string timeout to the watchlist * * @author Jakub Kurcek <*****@*****.**> */ public static function NotifyOnSubPageChange($watchers, $title, $editor, $notificationTimeoutSql) { wfProfileIn(__METHOD__); // Gets parent data $arrTitle = explode('/', $title->getDBkey()); if (empty($arrTitle)) { wfProfileOut(__METHOD__); return true; } // make Title $t = reset($arrTitle); $newTitle = Title::newFromDBkey($t); if (!$newTitle instanceof Title) { return true; } $dbw = wfGetDB(DB_MASTER); /** @var $dbw Database */ $res = $dbw->select(array('watchlist'), array('wl_user'), array('wl_title' => $newTitle->getDBkey(), 'wl_namespace' => $newTitle->getNamespace(), 'wl_user != ' . intval($editor->getID()), $notificationTimeoutSql), __METHOD__); // Gets user settings $parentpageWatchers = array(); while ($row = $dbw->fetchObject($res)) { $userId = intval($row->wl_user); $tmpUser = User::newFromId($userId); if ($tmpUser->getBoolOption(self::PREFERENCES_ENTRY)) { $parentpageWatchers[] = $userId; } unset($tmpUser); } // Updates parent watchlist timestamp for $parentOnlyWatchers. $parentOnlyWatchers = array_diff($parentpageWatchers, $watchers); $wl = WatchedItem::fromUserTitle($editor, $newTitle); $wl->updateWatch($parentOnlyWatchers); wfProfileOut(__METHOD__); return true; }
/** * Get the timestamp when this page was updated since the user last saw it. * * @param User $user * @return string|null */ public function getNotificationTimestamp($user = null) { global $wgUser; // Assume current user if none given if (!$user) { $user = $wgUser; } // Check cache first $uid = $user->getId(); if (!$uid) { return false; } // avoid isset here, as it'll return false for null entries if (array_key_exists($uid, $this->mNotificationTimestamp)) { return $this->mNotificationTimestamp[$uid]; } // Don't cache too much! if (count($this->mNotificationTimestamp) >= self::CACHE_MAX) { $this->mNotificationTimestamp = array(); } $watchedItem = WatchedItem::fromUserTitle($user, $this); $this->mNotificationTimestamp[$uid] = $watchedItem->getNotificationTimestamp(); return $this->mNotificationTimestamp[$uid]; }
/** * Move a title to a new location * * @param $nt Title the new title * @param $auth Bool indicates whether $wgUser's permissions * should be checked * @param $reason String the reason for the move * @param $createRedirect Bool Whether to create a redirect from the old title to the new title. * Ignored if the user doesn't have the suppressredirect right. * @return Mixed true on success, getUserPermissionsErrors()-like array on failure */ public function moveTo(&$nt, $auth = true, $reason = '', $createRedirect = true) { global $wgUser; $err = $this->isValidMoveOperation($nt, $auth, $reason); if (is_array($err)) { // Auto-block user's IP if the account was "hard" blocked $wgUser->spreadAnyEditBlock(); return $err; } // If it is a file, move it first. It is done before all other moving stuff is // done because it's hard to revert $dbw = wfGetDB(DB_MASTER); if ($this->getNamespace() == NS_FILE) { $file = wfLocalFile($this); if ($file->exists()) { $status = $file->move($nt); if (!$status->isOk()) { return $status->getErrorsArray(); } } } $dbw->begin(); # If $file was a LocalFile, its transaction would have closed our own. $pageid = $this->getArticleID(self::GAID_FOR_UPDATE); $protected = $this->isProtected(); $pageCountChange = ($createRedirect ? 1 : 0) - ($nt->exists() ? 1 : 0); // Do the actual move $err = $this->moveToInternal($nt, $reason, $createRedirect); if (is_array($err)) { # @todo FIXME: What about the File we have already moved? $dbw->rollback(); return $err; } $redirid = $this->getArticleID(); // Refresh the sortkey for this row. Be careful to avoid resetting // cl_timestamp, which may disturb time-based lists on some sites. $prefixes = $dbw->select('categorylinks', array('cl_sortkey_prefix', 'cl_to'), array('cl_from' => $pageid), __METHOD__); foreach ($prefixes as $prefixRow) { $prefix = $prefixRow->cl_sortkey_prefix; $catTo = $prefixRow->cl_to; $dbw->update('categorylinks', array('cl_sortkey' => Collation::singleton()->getSortKey($nt->getCategorySortkey($prefix)), 'cl_timestamp=cl_timestamp'), array('cl_from' => $pageid, 'cl_to' => $catTo), __METHOD__); } if ($protected) { # Protect the redirect title as the title used to be... $dbw->insertSelect('page_restrictions', 'page_restrictions', array('pr_page' => $redirid, 'pr_type' => 'pr_type', 'pr_level' => 'pr_level', 'pr_cascade' => 'pr_cascade', 'pr_user' => 'pr_user', 'pr_expiry' => 'pr_expiry'), array('pr_page' => $pageid), __METHOD__, array('IGNORE')); # Update the protection log $log = new LogPage('protect'); $comment = wfMsgForContent('prot_1movedto2', $this->getPrefixedText(), $nt->getPrefixedText()); if ($reason) { $comment .= wfMsgForContent('colon-separator') . $reason; } // @todo FIXME: $params? $log->addEntry('move_prot', $nt, $comment, array($this->getPrefixedText())); } # Update watchlists $oldnamespace = $this->getNamespace() & ~1; $newnamespace = $nt->getNamespace() & ~1; $oldtitle = $this->getDBkey(); $newtitle = $nt->getDBkey(); if ($oldnamespace != $newnamespace || $oldtitle != $newtitle) { WatchedItem::duplicateEntries($this, $nt); } # Update search engine $u = new SearchUpdate($pageid, $nt->getPrefixedDBkey()); $u->doUpdate(); $u = new SearchUpdate($redirid, $this->getPrefixedDBkey(), ''); $u->doUpdate(); $dbw->commit(); # Update site_stats if ($this->isContentPage() && !$nt->isContentPage()) { # No longer a content page # Not viewed, edited, removing $u = new SiteStatsUpdate(0, 1, -1, $pageCountChange); } elseif (!$this->isContentPage() && $nt->isContentPage()) { # Now a content page # Not viewed, edited, adding $u = new SiteStatsUpdate(0, 1, +1, $pageCountChange); } elseif ($pageCountChange) { # Redirect added $u = new SiteStatsUpdate(0, 0, 0, 1); } else { # Nothing special $u = false; } if ($u) { $u->doUpdate(); } # Update message cache for interface messages if ($this->getNamespace() == NS_MEDIAWIKI) { # @bug 17860: old article can be deleted, if this the case, # delete it from message cache if ($this->getArticleID() === 0) { MessageCache::singleton()->replace($this->getDBkey(), false); } else { $oldarticle = new Article($this); MessageCache::singleton()->replace($this->getDBkey(), $oldarticle->getContent()); } } if ($nt->getNamespace() == NS_MEDIAWIKI) { $newarticle = new Article($nt); MessageCache::singleton()->replace($nt->getDBkey(), $newarticle->getContent()); } global $wgUser; wfRunHooks('TitleMoveComplete', array(&$this, &$nt, &$wgUser, $pageid, $redirid)); return true; }
/** * Get a WatchedItem for this user and $title. * * @since 1.22 $checkRights parameter added * @param Title $title * @param int $checkRights Whether to check 'viewmywatchlist'/'editmywatchlist' rights. * Pass WatchedItem::CHECK_USER_RIGHTS or WatchedItem::IGNORE_USER_RIGHTS. * @return WatchedItem */ public function getWatchedItem($title, $checkRights = WatchedItem::CHECK_USER_RIGHTS) { $key = $checkRights . ':' . $title->getNamespace() . ':' . $title->getDBkey(); if (isset($this->mWatchedItems[$key])) { return $this->mWatchedItems[$key]; } if (count($this->mWatchedItems) >= self::MAX_WATCHED_ITEMS_CACHE) { $this->mWatchedItems = array(); } $this->mWatchedItems[$key] = WatchedItem::fromUserTitle($this, $title, $checkRights); return $this->mWatchedItems[$key]; }
/** * Allow the user to clear their watchlist * * @param $out Output object * @param $request Request object * @param $par Parameters passed to the watchlist page * @return bool True if it's been taken care of; false indicates the watchlist * code needs to do something further */ function wlHandleClear(&$out, &$request, $par) { # Check this function has something to do if ($request->getText('action') == 'clear' || $par == 'clear') { global $wgUser; $out->setPageTitle(wfMsgHtml('clearwatchlist')); $count = wlCountItems($wgUser); if ($count > 0) { # See if we're clearing or confirming if ($request->wasPosted() && $wgUser->matchEditToken($request->getText('token'), 'clearwatchlist')) { # Clearing, so do it and report the result $dbw =& wfGetDB(DB_MASTER); // WERELATE - don't remove pages in your trees; call WatchedItem::removeWatch // $dbw->delete( 'watchlist', array( 'wl_user' => $wgUser->mId ), 'wlHandleClear' ); $sql = 'SELECT wl_namespace, wl_title FROM watchlist where wl_user='******' AND NOT EXISTS (SELECT fp_tree_id FROM familytree_page WHERE fp_namespace=(wl_namespace & ~1) AND fp_title=wl_title AND fp_user_id=wl_user)'; $rows = $dbw->query($sql, 'wlHandleClear'); while ($row = $dbw->fetchObject($rows)) { $title = Title::makeTitle($row->wl_namespace, $row->wl_title); if ($title) { $wl = WatchedItem::fromUserTitle($wgUser, $title); $wl->removeWatch(); } } $dbw->freeResult($rows); $out->addWikiText(wfMsg('watchlistcleardone', $count)); $out->returnToMain(); } else { # Confirming, so show a form $wlTitle = Title::makeTitle(NS_SPECIAL, 'Watchlist'); $out->addHTML(wfElement('form', array('method' => 'post', 'action' => $wlTitle->getLocalUrl('action=clear')), NULL)); $out->addWikiText(wfMsg('watchlistcount', $count)); $out->addWikiText(wfMsg('watchlistcleartext')); $out->addHTML(wfElement('input', array('type' => 'hidden', 'name' => 'token', 'value' => $wgUser->editToken('clearwatchlist')), '')); $out->addHTML(wfElement('input', array('type' => 'submit', 'name' => 'submit', 'value' => wfMsgHtml('watchlistclearbutton')), '')); $out->addHTML(wfCloseElement('form')); } return true; } else { # Nothing on the watchlist; nothing to do here $out->addWikiText(wfMsg('nowatchlist')); $out->returnToMain(); return true; } } else { return false; } }
/** * Clear the user's notification timestamp for the given title. * If e-notif e-mails are on, they will receive notification mails on * the next change of the page if it's watched etc. * @param $title Title of the article to look at */ public function clearNotification(&$title) { global $wgUseEnotif, $wgShowUpdatedMarker; # Do nothing if the database is locked to writes if (wfReadOnly()) { return; } if ($title->getNamespace() == NS_USER_TALK && $title->getText() == $this->getName()) { if (!wfRunHooks('UserClearNewTalkNotification', array(&$this))) { return; } $this->setNewtalk(false); } if (!$wgUseEnotif && !$wgShowUpdatedMarker) { return; } if ($this->isAnon()) { // Nothing else to do... return; } // Only update the timestamp if the page is being watched. // The query to find out if it is watched is cached both in memcached and per-invocation, // and when it does have to be executed, it can be on a slave // If this is the user's newtalk page, we always update the timestamp if ($title->getNamespace() == NS_USER_TALK && $title->getText() == $this->getName()) { $watched = true; } else { $watched = $this->isWatched($title); } // If the page is watched by the user (or may be watched), update the timestamp on any // any matching rows if ($watched) { $wl = WatchedItem::fromUserTitle($this, $title); $wl->clearWatch(); } }
private function extractOutputData(WatchedItem $watchedItem, array $recentChangeInfo) { /* Determine the title of the page that has been changed. */ $title = Title::makeTitle($watchedItem->getLinkTarget()->getNamespace(), $watchedItem->getLinkTarget()->getDBkey()); $user = $this->getUser(); /* Our output data. */ $vals = []; $type = intval($recentChangeInfo['rc_type']); $vals['type'] = RecentChange::parseFromRCType($type); $anyHidden = false; /* Create a new entry in the result for the title. */ if ($this->fld_title || $this->fld_ids) { // These should already have been filtered out of the query, but just in case. if ($type === RC_LOG && $recentChangeInfo['rc_deleted'] & LogPage::DELETED_ACTION) { $vals['actionhidden'] = true; $anyHidden = true; } if ($type !== RC_LOG || LogEventsList::userCanBitfield($recentChangeInfo['rc_deleted'], LogPage::DELETED_ACTION, $user)) { if ($this->fld_title) { ApiQueryBase::addTitleInfo($vals, $title); } if ($this->fld_ids) { $vals['pageid'] = intval($recentChangeInfo['rc_cur_id']); $vals['revid'] = intval($recentChangeInfo['rc_this_oldid']); $vals['old_revid'] = intval($recentChangeInfo['rc_last_oldid']); } } } /* Add user data and 'anon' flag, if user is anonymous. */ if ($this->fld_user || $this->fld_userid) { if ($recentChangeInfo['rc_deleted'] & Revision::DELETED_USER) { $vals['userhidden'] = true; $anyHidden = true; } if (Revision::userCanBitfield($recentChangeInfo['rc_deleted'], Revision::DELETED_USER, $user)) { if ($this->fld_userid) { $vals['userid'] = (int) $recentChangeInfo['rc_user']; // for backwards compatibility $vals['user'] = (int) $recentChangeInfo['rc_user']; } if ($this->fld_user) { $vals['user'] = $recentChangeInfo['rc_user_text']; } if (!$recentChangeInfo['rc_user']) { $vals['anon'] = true; } } } /* Add flags, such as new, minor, bot. */ if ($this->fld_flags) { $vals['bot'] = (bool) $recentChangeInfo['rc_bot']; $vals['new'] = $recentChangeInfo['rc_type'] == RC_NEW; $vals['minor'] = (bool) $recentChangeInfo['rc_minor']; } /* Add sizes of each revision. (Only available on 1.10+) */ if ($this->fld_sizes) { $vals['oldlen'] = intval($recentChangeInfo['rc_old_len']); $vals['newlen'] = intval($recentChangeInfo['rc_new_len']); } /* Add the timestamp. */ if ($this->fld_timestamp) { $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $recentChangeInfo['rc_timestamp']); } if ($this->fld_notificationtimestamp) { $vals['notificationtimestamp'] = $watchedItem->getNotificationTimestamp() == null ? '' : wfTimestamp(TS_ISO_8601, $watchedItem->getNotificationTimestamp()); } /* Add edit summary / log summary. */ if ($this->fld_comment || $this->fld_parsedcomment) { if ($recentChangeInfo['rc_deleted'] & Revision::DELETED_COMMENT) { $vals['commenthidden'] = true; $anyHidden = true; } if (Revision::userCanBitfield($recentChangeInfo['rc_deleted'], Revision::DELETED_COMMENT, $user)) { if ($this->fld_comment && isset($recentChangeInfo['rc_comment'])) { $vals['comment'] = $recentChangeInfo['rc_comment']; } if ($this->fld_parsedcomment && isset($recentChangeInfo['rc_comment'])) { $vals['parsedcomment'] = Linker::formatComment($recentChangeInfo['rc_comment'], $title); } } } /* Add the patrolled flag */ if ($this->fld_patrol) { $vals['patrolled'] = $recentChangeInfo['rc_patrolled'] == 1; $vals['unpatrolled'] = ChangesList::isUnpatrolled((object) $recentChangeInfo, $user); } if ($this->fld_loginfo && $recentChangeInfo['rc_type'] == RC_LOG) { if ($recentChangeInfo['rc_deleted'] & LogPage::DELETED_ACTION) { $vals['actionhidden'] = true; $anyHidden = true; } if (LogEventsList::userCanBitfield($recentChangeInfo['rc_deleted'], LogPage::DELETED_ACTION, $user)) { $vals['logid'] = intval($recentChangeInfo['rc_logid']); $vals['logtype'] = $recentChangeInfo['rc_log_type']; $vals['logaction'] = $recentChangeInfo['rc_log_action']; $vals['logparams'] = LogFormatter::newFromRow($recentChangeInfo)->formatParametersForApi(); } } if ($anyHidden && $recentChangeInfo['rc_deleted'] & Revision::DELETED_RESTRICTED) { $vals['suppressed'] = true; } return $vals; }
private function cache(WatchedItem $item) { $user = $item->getUser(); $target = $item->getLinkTarget(); $key = $this->getCacheKey($user, $target); $this->cache->set($key, $item); $this->cacheIndex[$target->getNamespace()][$target->getDBkey()][$user->getId()] = $key; $this->stats->increment('WatchedItemStore.cache'); }
function wrArticleSave(&$article, &$user, $text) { $newTitle = Title::newFromRedirect($text); // can't use $article->content or SD:getRedirectToTitle because they return the wrong results if ($newTitle != null) { // if this article is a redirect $newTitle = StructuredData::getRedirectToTitle($newTitle, true); // get final redirect; unreliable on the article title for some reason $newRevision = StructuredData::getRevision($newTitle, false, true); $newText = $newRevision ? $newRevision->getText() : ''; $summary = StructuredData::getWatchlistSummary($newTitle, $newText); WatchedItem::duplicateEntries($article->getTitle(), $newTitle, $summary); StructuredData::purgeTitle($newTitle, +1); StructuredData::requestIndex($newTitle); // remove watchers from redirected titles StructuredData::removeAllWatchers($article->getTitle()); } return true; }
/** * Check if the given title already is watched by the user, and if so * add watches on a new title. To be used for page renames and such. * * @param Title $ot Page title to duplicate entries from, if present * @param Title $nt Page title to add watches on * @static */ function duplicateEntries($ot, $nt, $summary = null) { //WERELATE - add optional $summary parameter WatchedItem::doDuplicateEntries($ot->getSubjectPage(), $nt->getSubjectPage(), $summary); WatchedItem::doDuplicateEntries($ot->getTalkPage(), $nt->getTalkPage()); }
public function testDuplicateEntries() { $oldTitle = Title::newFromText('OldTitle'); $newTitle = Title::newFromText('NewTitle'); $store = $this->getMockWatchedItemStore(); $store->expects($this->once())->method('duplicateAllAssociatedEntries')->with($oldTitle, $newTitle); $this->setService('WatchedItemStore', $store); WatchedItem::duplicateEntries($oldTitle, $newTitle); }
/** * Send emails corresponding to the user $editor editing the page $title. * Also updates wl_notificationtimestamp. * * May be deferred via the job queue. * * @param $editor User object * @param $title Title object * @param $timestamp * @param $summary * @param $minorEdit * @param $oldid (default: false) * @param $action(Wikia) * @param $otherParam(Wikia) */ public function notifyOnPageChange($editor, $title, $timestamp, $summary, $minorEdit, $oldid = false, $action = '', $otherParam = array()) { global $wgEnotifUseJobQ, $wgEnotifWatchlist, $wgShowUpdatedMarker, $wgEnotifMinorEdits, $wgUsersNotifiedOnAllChanges, $wgEnotifUserTalk; if ($title->getNamespace() < 0) { return; } # <Wikia> if (!wfRunHooks('AllowNotifyOnPageChange', array($editor, $title))) { return false; } if (!empty($otherParam['watchers'])) { $watchers = $otherParam['watchers']; } # </Wikia> // Build a list of users to notfiy $watchers = array(); if ($wgEnotifWatchlist || $wgShowUpdatedMarker) { /* Wikia change begin - @author: wladek & tomek */ /* RT#55604: Add a timeout to the watchlist email block */ global $wgEnableWatchlistNotificationTimeout, $wgWatchlistNotificationTimeout; if (!empty($otherParam['notisnull'])) { $notificationTimeoutSql = "1"; } elseif (!empty($wgEnableWatchlistNotificationTimeout) && isset($wgWatchlistNotificationTimeout)) { $blockTimeout = wfTimestamp(TS_MW, wfTimestamp(TS_UNIX, $timestamp) - intval($wgWatchlistNotificationTimeout)); $notificationTimeoutSql = "wl_notificationtimestamp IS NULL OR wl_notificationtimestamp < '{$blockTimeout}'"; } else { $notificationTimeoutSql = 'wl_notificationtimestamp IS NULL'; } /* Wikia change end */ $dbw = wfGetDB(DB_MASTER); $res = $dbw->select(array('watchlist'), array('wl_user'), array('wl_title' => $title->getDBkey(), 'wl_namespace' => $title->getNamespace(), 'wl_user != ' . intval($editor->getID()), $notificationTimeoutSql), __METHOD__); foreach ($res as $row) { $watchers[] = intval($row->wl_user); } if ($watchers) { // Update wl_notificationtimestamp for all watching users except // the editor $wl = WatchedItem::fromUserTitle($editor, $title); $wl->updateWatch($watchers, $timestamp); } /* Wikia change begin - @author: Jakub Kurcek */ wfRunHooks('NotifyOnSubPageChange', array($watchers, $title, $editor, $notificationTimeoutSql)); /* Wikia change end */ } $sendEmail = true; // If nobody is watching the page, and there are no users notified on all changes // don't bother creating a job/trying to send emails // $watchers deals with $wgEnotifWatchlist if (!count($watchers) && !count($wgUsersNotifiedOnAllChanges)) { $sendEmail = false; // Only send notification for non minor edits, unless $wgEnotifMinorEdits if (!$minorEdit || $wgEnotifMinorEdits && !$editor->isAllowed('nominornewtalk')) { $isUserTalkPage = $title->getNamespace() == NS_USER_TALK; if ($wgEnotifUserTalk && $isUserTalkPage && $this->canSendUserTalkEmail($editor, $title, $minorEdit)) { $sendEmail = true; } } } if (!$sendEmail) { return; } if ($wgEnotifUseJobQ) { $params = array('editor' => $editor->getName(), 'editorID' => $editor->getID(), 'timestamp' => $timestamp, 'summary' => $summary, 'minorEdit' => $minorEdit, 'oldid' => $oldid, 'watchers' => $watchers, 'action' => $action, 'otherParam' => $othersParam); $job = new EnotifNotifyJob($title, $params); $job->insert(); } else { $this->actuallyNotifyOnPageChange($editor, $title, $timestamp, $summary, $minorEdit, $oldid, $watchers, $action, $otherParam); } }
function duplicateEntries($ot, $nt) { # duplicate watchlist entries for the target page # When using ENotif, talkpages have distinct watchlist entries # existing talkpage entries in the watchlist will be moved, too WatchedItem::duplicateEntriesNs($ot, $nt, false); WatchedItem::duplicateEntriesNs($ot, $nt, true); }
/** * Back-end article deletion * Deletes the article with database consistency, writes logs, purges caches * Returns success */ function doDeleteArticle($reason, $rc = true) { global $wgUseSquid, $wgDeferredUpdateList; global $wgPostCommitUpdateList, $wgUseTrackbacks; wfDebug(__METHOD__ . "\n"); $dbw =& wfGetDB(DB_MASTER); $ns = $this->mTitle->getNamespace(); $t = $this->mTitle->getDBkey(); $id = $this->mTitle->getArticleID(); if ($t == '' || $id == 0) { return false; } $u = new SiteStatsUpdate(0, 1, -(int) $this->isCountable($this->getContent()), -1); array_push($wgDeferredUpdateList, $u); // For now, shunt the revision data into the archive table. // Text is *not* removed from the text table; bulk storage // is left intact to avoid breaking block-compression or // immutable storage schemes. // // For backwards compatibility, note that some older archive // table entries will have ar_text and ar_flags fields still. // // In the future, we may keep revisions and mark them with // the rev_deleted field, which is reserved for this purpose. $dbw->insertSelect('archive', array('page', 'revision'), array('ar_namespace' => 'page_namespace', 'ar_title' => 'page_title', 'ar_comment' => 'rev_comment', 'ar_user' => 'rev_user', 'ar_user_text' => 'rev_user_text', 'ar_timestamp' => 'rev_timestamp', 'ar_minor_edit' => 'rev_minor_edit', 'ar_rev_id' => 'rev_id', 'ar_text_id' => 'rev_text_id'), array('page_id' => $id, 'page_id = rev_page'), __METHOD__); # Now that it's safely backed up, delete it $dbw->delete('revision', array('rev_page' => $id), __METHOD__); $dbw->delete('page', array('page_id' => $id), __METHOD__); if ($wgUseTrackbacks) { $dbw->delete('trackbacks', array('tb_page' => $id), __METHOD__); } # Clean up recentchanges entries... $dbw->delete('recentchanges', array('rc_namespace' => $ns, 'rc_title' => $t), __METHOD__); // WERELATE: remove from watchlists on delete if (Namespac::isMain($ns)) { $rows = $dbw->select(array('watchlist', 'user'), array('user_name'), array('wl_user=user_id', 'wl_namespace' => $ns, 'wl_title' => $t)); while ($row = $dbw->fetchObject($rows)) { $user = User::newFromName($row->user_name, false); $wl = WatchedItem::fromUserTitle($user, $this->mTitle); $wl->removeWatch(); } $dbw->freeResult($rows); } # Finally, clean up the link tables $t = $this->mTitle->getPrefixedDBkey(); # Clear caches Article::onArticleDelete($this->mTitle); # Delete outgoing links $dbw->delete('pagelinks', array('pl_from' => $id)); $dbw->delete('imagelinks', array('il_from' => $id)); $dbw->delete('categorylinks', array('cl_from' => $id)); $dbw->delete('templatelinks', array('tl_from' => $id)); $dbw->delete('externallinks', array('el_from' => $id)); $dbw->delete('langlinks', array('ll_from' => $id)); # Log the deletion $log = new LogPage('delete', $rc); // WERELATE - add id as a log param $log->addEntry('delete', $this->mTitle, $reason, array($id)); # Clear the cached article id so the interface doesn't act like we exist $this->mTitle->resetArticleID(0); $this->mTitle->mArticleID = 0; return true; }
/** * Move a title to a new location * @param &$nt \type{Title} the new title * @param $auth \type{\bool} indicates whether $wgUser's permissions * should be checked * @param $reason \type{\string} The reason for the move * @param $createRedirect \type{\bool} Whether to create a redirect from the old title to the new title. * Ignored if the user doesn't have the suppressredirect right. * @return \type{\mixed} true on success, getUserPermissionsErrors()-like array on failure */ public function moveTo(&$nt, $auth = true, $reason = '', $createRedirect = true) { $err = $this->isValidMoveOperation($nt, $auth, $reason); if (is_array($err)) { return $err; } // If it is a file, move it first. It is done before all other moving stuff is done because it's hard to revert $dbw = wfGetDB(DB_MASTER); if ($this->getNamespace() == NS_FILE) { $file = wfLocalFile($this); if ($file->exists()) { $status = $file->move($nt); if (!$status->isOk()) { return $status->getErrorsArray(); } } } $pageid = $this->getArticleID(); $protected = $this->isProtected(); if ($nt->exists()) { $err = $this->moveOverExistingRedirect($nt, $reason, $createRedirect); $pageCountChange = $createRedirect ? 0 : -1; } else { # Target didn't exist, do normal move. $err = $this->moveToNewTitle($nt, $reason, $createRedirect); $pageCountChange = $createRedirect ? 1 : 0; } if (is_array($err)) { return $err; } $redirid = $this->getArticleID(); // Category memberships include a sort key which may be customized. // If it's left as the default (the page title), we need to update // the sort key to match the new title. // // Be careful to avoid resetting cl_timestamp, which may disturb // time-based lists on some sites. // // Warning -- if the sort key is *explicitly* set to the old title, // we can't actually distinguish it from a default here, and it'll // be set to the new title even though it really shouldn't. // It'll get corrected on the next edit, but resetting cl_timestamp. $dbw->update('categorylinks', array('cl_sortkey' => $nt->getPrefixedText(), 'cl_timestamp=cl_timestamp'), array('cl_from' => $pageid, 'cl_sortkey' => $this->getPrefixedText()), __METHOD__); if ($protected) { # Protect the redirect title as the title used to be... $dbw->insertSelect('page_restrictions', 'page_restrictions', array('pr_page' => $redirid, 'pr_type' => 'pr_type', 'pr_level' => 'pr_level', 'pr_cascade' => 'pr_cascade', 'pr_user' => 'pr_user', 'pr_expiry' => 'pr_expiry'), array('pr_page' => $pageid), __METHOD__, array('IGNORE')); # Update the protection log $log = new LogPage('protect'); $comment = wfMsgForContent('prot_1movedto2', $this->getPrefixedText(), $nt->getPrefixedText()); if ($reason) { $comment .= wfMsgForContent('colon-separator') . $reason; } $log->addEntry('move_prot', $nt, $comment, array($this->getPrefixedText())); // FIXME: $params? } # Update watchlists $oldnamespace = $this->getNamespace() & ~1; $newnamespace = $nt->getNamespace() & ~1; $oldtitle = $this->getDBkey(); $newtitle = $nt->getDBkey(); if ($oldnamespace != $newnamespace || $oldtitle != $newtitle) { WatchedItem::duplicateEntries($this, $nt); } # Update search engine $u = new SearchUpdate($pageid, $nt->getPrefixedDBkey()); $u->doUpdate(); $u = new SearchUpdate($redirid, $this->getPrefixedDBkey(), ''); $u->doUpdate(); # Update site_stats if ($this->isContentPage() && !$nt->isContentPage()) { # No longer a content page # Not viewed, edited, removing $u = new SiteStatsUpdate(0, 1, -1, $pageCountChange); } elseif (!$this->isContentPage() && $nt->isContentPage()) { # Now a content page # Not viewed, edited, adding $u = new SiteStatsUpdate(0, 1, +1, $pageCountChange); } elseif ($pageCountChange) { # Redirect added $u = new SiteStatsUpdate(0, 0, 0, 1); } else { # Nothing special $u = false; } if ($u) { $u->doUpdate(); } # Update message cache for interface messages if ($nt->getNamespace() == NS_MEDIAWIKI) { global $wgMessageCache; # @bug 17860: old article can be deleted, if this the case, # delete it from message cache if ($this->getArticleID() === 0) { $wgMessageCache->replace($this->getDBkey(), false); } else { $oldarticle = new Article($this); $wgMessageCache->replace($this->getDBkey(), $oldarticle->getContent()); } $newarticle = new Article($nt); $wgMessageCache->replace($nt->getDBkey(), $newarticle->getContent()); } global $wgUser; wfRunHooks('TitleMoveComplete', array(&$this, &$nt, &$wgUser, $pageid, $redirid)); return true; }
private function processActionOnWatchlist($user, $followedUserName, $action) { wfProfileIn(__METHOD__); $watchTitle = Title::newFromText($followedUserName, NS_USER); if ($watchTitle instanceof Title) { $wl = new WatchedItem(); $wl->mTitle = $watchTitle; $wl->id = $user->getId(); $wl->ns = $watchTitle->getNamespace(); $wl->ti = $watchTitle->getDBkey(); if ($action === 'add') { $wl->addWatch(); } elseif ($action === 'remove') { $wl->removeWatch(); } } else { //just-in-case -- it shouldn't happen but if it does we want to know about it Wikia::log(__METHOD__, false, 'WALL_HOOK_ERROR: No title instance while syncing follows. User name: ' . $followedUserName); } wfProfileOut(__METHOD__); }
<?php require_once "commandLine.inc"; require_once "{$IP}/extensions/structuredNamespaces/StructuredData.php"; require_once "{$IP}/extensions/familytree/FamilyTreePropagator.php"; require_once "{$IP}/includes/Title.php"; $db =& wfGetDB(DB_MASTER); $rows = $db->query("select page_namespace, page_title from page where page_is_redirect > 0 and (page_namespace = 108 or page_namespace = 110)"); while ($row = $db->fetchObject($rows)) { $title = Title::makeTitle($row->page_namespace, $row->page_title); $newTitle = StructuredData::getRedirectToTitle($title); if ($title->getPrefixedText() != $newTitle->getPrefixedText()) { // print "title=" . $title->getPrefixedText() . " newTitle=" . $newTitle->getPrefixedText() . "\n"; // from wrArticleSave WatchedItem::duplicateEntries($title, $newTitle); } } $db->freeResult($rows);
/** * Move a title to a new location * * @param Title $nt The new title * @param bool $auth Indicates whether $wgUser's permissions * should be checked * @param string $reason The reason for the move * @param bool $createRedirect Whether to create a redirect from the old title to the new title. * Ignored if the user doesn't have the suppressredirect right. * @return array|bool True on success, getUserPermissionsErrors()-like array on failure */ public function moveTo(&$nt, $auth = true, $reason = '', $createRedirect = true) { global $wgUser; $err = $this->isValidMoveOperation($nt, $auth, $reason); if (is_array($err)) { // Auto-block user's IP if the account was "hard" blocked $wgUser->spreadAnyEditBlock(); return $err; } // Check suppressredirect permission if ($auth && !$wgUser->isAllowed('suppressredirect')) { $createRedirect = true; } wfRunHooks('TitleMove', array($this, $nt, $wgUser)); // If it is a file, move it first. // It is done before all other moving stuff is done because it's hard to revert. $dbw = wfGetDB(DB_MASTER); if ($this->getNamespace() == NS_FILE) { $file = wfLocalFile($this); if ($file->exists()) { $status = $file->move($nt); if (!$status->isOk()) { return $status->getErrorsArray(); } } // Clear RepoGroup process cache RepoGroup::singleton()->clearCache($this); RepoGroup::singleton()->clearCache($nt); # clear false negative cache } $dbw->begin(__METHOD__); # If $file was a LocalFile, its transaction would have closed our own. $pageid = $this->getArticleID(self::GAID_FOR_UPDATE); $protected = $this->isProtected(); // Do the actual move $this->moveToInternal($nt, $reason, $createRedirect); // Refresh the sortkey for this row. Be careful to avoid resetting // cl_timestamp, which may disturb time-based lists on some sites. $prefixes = $dbw->select('categorylinks', array('cl_sortkey_prefix', 'cl_to'), array('cl_from' => $pageid), __METHOD__); foreach ($prefixes as $prefixRow) { $prefix = $prefixRow->cl_sortkey_prefix; $catTo = $prefixRow->cl_to; $dbw->update('categorylinks', array('cl_sortkey' => Collation::singleton()->getSortKey($nt->getCategorySortkey($prefix)), 'cl_timestamp=cl_timestamp'), array('cl_from' => $pageid, 'cl_to' => $catTo), __METHOD__); } $redirid = $this->getArticleID(); if ($protected) { # Protect the redirect title as the title used to be... $dbw->insertSelect('page_restrictions', 'page_restrictions', array('pr_page' => $redirid, 'pr_type' => 'pr_type', 'pr_level' => 'pr_level', 'pr_cascade' => 'pr_cascade', 'pr_user' => 'pr_user', 'pr_expiry' => 'pr_expiry'), array('pr_page' => $pageid), __METHOD__, array('IGNORE')); # Update the protection log $log = new LogPage('protect'); $comment = wfMessage('prot_1movedto2', $this->getPrefixedText(), $nt->getPrefixedText())->inContentLanguage()->text(); if ($reason) { $comment .= wfMessage('colon-separator')->inContentLanguage()->text() . $reason; } // @todo FIXME: $params? $logId = $log->addEntry('move_prot', $nt, $comment, array($this->getPrefixedText()), $wgUser); // reread inserted pr_ids for log relation $insertedPrIds = $dbw->select('page_restrictions', 'pr_id', array('pr_page' => $redirid), __METHOD__); $logRelationsValues = array(); foreach ($insertedPrIds as $prid) { $logRelationsValues[] = $prid->pr_id; } $log->addRelations('pr_id', $logRelationsValues, $logId); } // Update *_from_namespace fields as needed if ($this->getNamespace() != $nt->getNamespace()) { $dbw->update('pagelinks', array('pl_from_namespace' => $nt->getNamespace()), array('pl_from' => $pageid), __METHOD__); $dbw->update('templatelinks', array('tl_from_namespace' => $nt->getNamespace()), array('tl_from' => $pageid), __METHOD__); $dbw->update('imagelinks', array('il_from_namespace' => $nt->getNamespace()), array('il_from' => $pageid), __METHOD__); } # Update watchlists $oldtitle = $this->getDBkey(); $newtitle = $nt->getDBkey(); $oldsnamespace = MWNamespace::getSubject($this->getNamespace()); $newsnamespace = MWNamespace::getSubject($nt->getNamespace()); if ($oldsnamespace != $newsnamespace || $oldtitle != $newtitle) { WatchedItem::duplicateEntries($this, $nt); } $dbw->commit(__METHOD__); wfRunHooks('TitleMoveComplete', array(&$this, &$nt, &$wgUser, $pageid, $redirid, $reason)); return true; }
/** * Creates or changes a review for a page. Called by remote handler. * @return bool Allow other hooked methods to be executed. Always true. */ public static function doEditReview() { if (BsCore::checkAccessAdmission('workflowedit') === false) { return true; } $aAnswer = array('success' => true, 'errors' => array(), 'messages' => array()); $oUser = BsCore::loadCurrentUser(); $oReview = BsExtensionManager::getExtension('Review'); $userIsSysop = in_array('sysop', $oUser->getGroups()); //TODO: getEffectiveGroups()? if (!$userIsSysop && !$oUser->isAllowed('workflowedit')) { $aAnswer['success'] = false; $aAnswer['messages'][] = wfMessage('bs-review-save-norights')->plain(); return json_encode($aAnswer); } global $wgRequest; $paramRvPid = $wgRequest->getInt('pid', -1); // Check for id 0 prevents special pages to be put on a review if (empty($paramRvPid)) { $aAnswer['success'] = false; $aAnswer['messages'][] = wfMessage('bs-review-save-noid')->plain(); return json_encode($aAnswer); } $oReviewProcess = BsReviewProcess::newFromPid($paramRvPid); $bIsEdit = false; if (is_object($oReviewProcess) && $oReviewProcess->hasSteps()) { $bIsEdit = true; } if (!$userIsSysop && $oReviewProcess && BsConfig::get('MW::Review::CheckOwner') && $oReviewProcess->owner != $oUser->getID()) { $aAnswer['success'] = false; $aAnswer['messages'][] = wfMessage('bs-review-save-norights')->plain(); return json_encode($aAnswer); } $paramCmd = $wgRequest->getVal('cmd', ''); $paramSaveTmpl = $wgRequest->getInt('save_tmpl', 0); if (!($paramCmd === false)) { switch ($paramCmd) { case 'insert': $aErrors = array(); $review = BsReviewProcess::newFromJSON($wgRequest->getVal('review', ''), $aErrors); if (is_array($aErrors) && count($aErrors) > 0) { $aAnswer['success'] = false; foreach ($aErrors as $sError) { $aAnswer['messages'][] = wfMessage('bs-review-' . $sError)->plain(); } return json_encode($aAnswer); } $review->setOwner($oUser->getID()); $oOldReview = BsReviewProcess::newFromPid($paramRvPid); $update = is_object($oOldReview) ? $oOldReview->getPid() : false; BsReviewProcess::removeReviewSteps($paramRvPid); if ($paramSaveTmpl == 1) { $paramTmplChoice = $wgRequest->getInt('tmpl_choice', -1); $paramTmplName = $wgRequest->getVal('tmpl_name', ''); $review->asTemplate($paramTmplChoice, $paramTmplName); } if (!is_array($review->steps)) { $aAnswer['success'] = false; $aAnswer['messages'][] = wfMessage('bs-review-save-nosteps')->plain(); return json_encode($aAnswer); } if ($review->store($update)) { $oTitle = Title::newFromID($paramRvPid); $oTitle->invalidateCache(); $oWatchlist = WatchedItem::fromUserTitle($oUser, $oTitle); if (!$oWatchlist->isWatched()) { $oWatchlist->addWatch(); } $aParams = array('action' => $bIsEdit ? 'modify' : 'create', 'target' => $oTitle, 'comment' => '', 'params' => null, 'doer' => $oUser); $oReview->oLogger->addEntry($aParams['action'], $aParams['target'], $aParams['comment'], $aParams['params'], $aParams['doer']); $aAnswer['messages'][] = wfMessage('bs-review-save-success')->plain(); // Identify owner $oReviewProcess = BsReviewProcess::newFromPid($paramRvPid); $oReview->emailNotifyNextUsers($oReviewProcess); return json_encode($aAnswer); } else { $aAnswer['success'] = false; $aAnswer['messages'][] = wfMessage('bs-review-save-error')->plain(); return json_encode($aAnswer); } break; // 22.08.13 STM: WTF? // 22.08.13 STM: WTF? case 'delete': BsReviewProcess::removeReviews($paramRvPid); $oTitle = Title::newFromID($paramRvPid); $oTitle->invalidateCache(); $oWatchlist = WatchedItem::fromUserTitle($oUser, $oTitle); if ($oWatchlist->isWatched()) { $oWatchlist->removeWatch(); } $aParams = array('action' => 'delete', 'target' => $oTitle, 'comment' => '', 'params' => null, 'doer' => $oUser); $oReview->oLogger->addEntry($aParams['action'], $aParams['target'], $aParams['comment'], $aParams['params'], $aParams['doer']); $aAnswer['messages'][] = wfMessage('bs-review-save-removed')->plain(); return json_encode($aAnswer); break; } } return true; }
/** * Move a title to a new location * @param Title &$nt the new title * @param bool $auth indicates whether $wgUser's permissions * should be checked * @param string $reason The reason for the move * @param bool $createRedirect Whether to create a redirect from the old title to the new title. * Ignored if the user doesn't have the suppressredirect right. * @return mixed true on success, message name on failure */ public function moveTo(&$nt, $auth = true, $reason = '', $createRedirect = true) { $err = $this->isValidMoveOperation($nt, $auth); if (is_string($err)) { return $err; } $pageid = $this->getArticleID(); if ($nt->exists()) { $this->moveOverExistingRedirect($nt, $reason, $createRedirect); $pageCountChange = $createRedirect ? 0 : -1; } else { # Target didn't exist, do normal move. $this->moveToNewTitle($nt, $reason, $createRedirect); $pageCountChange = $createRedirect ? 1 : 0; } $redirid = $this->getArticleID(); // Category memberships include a sort key which may be customized. // If it's left as the default (the page title), we need to update // the sort key to match the new title. // // Be careful to avoid resetting cl_timestamp, which may disturb // time-based lists on some sites. // // Warning -- if the sort key is *explicitly* set to the old title, // we can't actually distinguish it from a default here, and it'll // be set to the new title even though it really shouldn't. // It'll get corrected on the next edit, but resetting cl_timestamp. $dbw = wfGetDB(DB_MASTER); $dbw->update('categorylinks', array('cl_sortkey' => $nt->getPrefixedText(), 'cl_timestamp=cl_timestamp'), array('cl_from' => $pageid, 'cl_sortkey' => $this->getPrefixedText()), __METHOD__); # Update watchlists $oldnamespace = $this->getNamespace() & ~1; $newnamespace = $nt->getNamespace() & ~1; $oldtitle = $this->getDBkey(); $newtitle = $nt->getDBkey(); if ($oldnamespace != $newnamespace || $oldtitle != $newtitle) { WatchedItem::duplicateEntries($this, $nt); } # Update search engine $u = new SearchUpdate($pageid, $nt->getPrefixedDBkey()); $u->doUpdate(); $u = new SearchUpdate($redirid, $this->getPrefixedDBkey(), ''); $u->doUpdate(); # Update site_stats if ($this->isContentPage() && !$nt->isContentPage()) { # No longer a content page # Not viewed, edited, removing $u = new SiteStatsUpdate(0, 1, -1, $pageCountChange); } elseif (!$this->isContentPage() && $nt->isContentPage()) { # Now a content page # Not viewed, edited, adding $u = new SiteStatsUpdate(0, 1, +1, $pageCountChange); } elseif ($pageCountChange) { # Redirect added $u = new SiteStatsUpdate(0, 0, 0, 1); } else { # Nothing special $u = false; } if ($u) { $u->doUpdate(); } # Update message cache for interface messages if ($nt->getNamespace() == NS_MEDIAWIKI) { global $wgMessageCache; $oldarticle = new Article($this); $wgMessageCache->replace($this->getDBkey(), $oldarticle->getContent()); $newarticle = new Article($nt); $wgMessageCache->replace($nt->getDBkey(), $newarticle->getContent()); } global $wgUser; wfRunHooks('TitleMoveComplete', array(&$this, &$nt, &$wgUser, $pageid, $redirid)); return true; }
/** * auto-unwatch all comments if blog post was unwatched * * @access public * @static */ public static function UnwatchBlogComments($oUser, $oArticle) { wfProfileIn(__METHOD__); if (wfReadOnly()) { wfProfileOut(__METHOD__); return true; } /* @var $oUser User */ if (!$oUser instanceof User) { wfProfileOut(__METHOD__); return true; } /* @var $oArticle WikiPage */ if (!$oArticle instanceof Article) { wfProfileOut(__METHOD__); return true; } /* @var $oTitle Title */ $oTitle = $oArticle->getTitle(); if (!$oTitle instanceof Title) { wfProfileOut(__METHOD__); return true; } $list = array(); $dbr = wfGetDB(DB_SLAVE); $like = $dbr->buildLike(sprintf("%s/", $oTitle->getDBkey()), $dbr->anyString()); $res = $dbr->select('watchlist', '*', array('wl_user' => $oUser->getId(), 'wl_namespace' => NS_BLOG_ARTICLE_TALK, "wl_title {$like}"), __METHOD__); if ($res->numRows() > 0) { while ($row = $res->fetchObject()) { $oCommentTitle = Title::makeTitleSafe($row->wl_namespace, $row->wl_title); if ($oCommentTitle instanceof Title) { $list[] = $oCommentTitle; } } $dbr->freeResult($res); } if (!empty($list)) { foreach ($list as $oCommentTitle) { $oWItem = WatchedItem::fromUserTitle($oUser, $oCommentTitle); $oWItem->removeWatch(); } $oUser->invalidateCache(); } wfProfileOut(__METHOD__); return true; }
public function testGetNotificationTimestamp_falseOnNotWatched() { $user = $this->getUser(); $title = Title::newFromText('WatchedItemIntegrationTestPage'); WatchedItem::fromUserTitle($user, $title)->removeWatch(); $this->assertFalse(WatchedItem::fromUserTitle($user, $title)->isWatched()); $this->assertFalse(WatchedItem::fromUserTitle($user, $title)->getNotificationTimestamp()); }
/** * update local watchlist */ private function updateLocalWatchlistForUser($iUserId, $sTitle, $iNamespace) { $dbw = wfGetDB(DB_MASTER); $oUser = User::newFromId($iUserId); if (!is_object($oUser)) { return false; } $oTitle = Title::makeTitle($iNamespace, $sTitle); if (!is_object($oTitle)) { return false; } $wl = WatchedItem::fromUserTitle($oUser, $oTitle); $wl->clearWatch(); return true; }
/** * Move a title to a new location * @param Title &$nt the new title * @param bool $auth indicates whether $wgUser's permissions * should be checked * @return mixed true on success, message name on failure */ public function moveTo(&$nt, $auth = true, $reason = '') { $err = $this->isValidMoveOperation($nt, $auth); if (is_string($err)) { return $err; } $pageid = $this->getArticleID(); if ($nt->exists()) { $this->moveOverExistingRedirect($nt, $reason); $pageCountChange = 0; } else { # Target didn't exist, do normal move. $this->moveToNewTitle($nt, $reason); $pageCountChange = 1; } $redirid = $this->getArticleID(); # Fixing category links (those without piped 'alternate' names) to be sorted under the new title $dbw = wfGetDB(DB_MASTER); $categorylinks = $dbw->tableName('categorylinks'); $sql = "UPDATE {$categorylinks} SET cl_sortkey=" . $dbw->addQuotes($nt->getPrefixedText()) . " WHERE cl_from=" . $dbw->addQuotes($pageid) . " AND cl_sortkey=" . $dbw->addQuotes($this->getPrefixedText()); $dbw->query($sql, 'SpecialMovepage::doSubmit'); # Update watchlists $oldnamespace = $this->getNamespace() & ~1; $newnamespace = $nt->getNamespace() & ~1; $oldtitle = $this->getDBkey(); $newtitle = $nt->getDBkey(); if ($oldnamespace != $newnamespace || $oldtitle != $newtitle) { WatchedItem::duplicateEntries($this, $nt); } # Update search engine $u = new SearchUpdate($pageid, $nt->getPrefixedDBkey()); $u->doUpdate(); $u = new SearchUpdate($redirid, $this->getPrefixedDBkey(), ''); $u->doUpdate(); # Update site_stats if ($this->isContentPage() && !$nt->isContentPage()) { # No longer a content page # Not viewed, edited, removing $u = new SiteStatsUpdate(0, 1, -1, $pageCountChange); } elseif (!$this->isContentPage() && $nt->isContentPage()) { # Now a content page # Not viewed, edited, adding $u = new SiteStatsUpdate(0, 1, +1, $pageCountChange); } elseif ($pageCountChange) { # Redirect added $u = new SiteStatsUpdate(0, 0, 0, 1); } else { # Nothing special $u = false; } if ($u) { $u->doUpdate(); } global $wgUser; wfRunHooks('TitleMoveComplete', array(&$this, &$nt, &$wgUser, $pageid, $redirid)); return true; }
/** * Update wl_notificationtimestamp for all watching users except the editor */ private function updateWatchedItem(array $watchers) { $wl = WatchedItem::fromUserTitle($this->editor, $this->title); $wl->updateWatch($watchers, $this->timestamp); }
/** * Stop watching an article */ function removeWatch($title) { $wl = WatchedItem::fromUserTitle($this, $title); $wl->removeWatch(); $this->invalidateCache(); }
/** * @param User $user * @param string $reason * @param bool $createRedirect * @return Status */ public function move(User $user, $reason, $createRedirect) { global $wgCategoryCollation; Hooks::run('TitleMove', array($this->oldTitle, $this->newTitle, $user)); // If it is a file, move it first. // It is done before all other moving stuff is done because it's hard to revert. $dbw = wfGetDB(DB_MASTER); if ($this->oldTitle->getNamespace() == NS_FILE) { $file = wfLocalFile($this->oldTitle); $file->load(File::READ_LATEST); if ($file->exists()) { $status = $file->move($this->newTitle); if (!$status->isOk()) { return $status; } } // Clear RepoGroup process cache RepoGroup::singleton()->clearCache($this->oldTitle); RepoGroup::singleton()->clearCache($this->newTitle); # clear false negative cache } $dbw->begin(__METHOD__); # If $file was a LocalFile, its transaction would have closed our own. $pageid = $this->oldTitle->getArticleID(Title::GAID_FOR_UPDATE); $protected = $this->oldTitle->isProtected(); // Do the actual move $this->moveToInternal($user, $this->newTitle, $reason, $createRedirect); // Refresh the sortkey for this row. Be careful to avoid resetting // cl_timestamp, which may disturb time-based lists on some sites. // @todo This block should be killed, it's duplicating code // from LinksUpdate::getCategoryInsertions() and friends. $prefixes = $dbw->select('categorylinks', array('cl_sortkey_prefix', 'cl_to'), array('cl_from' => $pageid), __METHOD__); if ($this->newTitle->getNamespace() == NS_CATEGORY) { $type = 'subcat'; } elseif ($this->newTitle->getNamespace() == NS_FILE) { $type = 'file'; } else { $type = 'page'; } foreach ($prefixes as $prefixRow) { $prefix = $prefixRow->cl_sortkey_prefix; $catTo = $prefixRow->cl_to; $dbw->update('categorylinks', array('cl_sortkey' => Collation::singleton()->getSortKey($this->newTitle->getCategorySortkey($prefix)), 'cl_collation' => $wgCategoryCollation, 'cl_type' => $type, 'cl_timestamp=cl_timestamp'), array('cl_from' => $pageid, 'cl_to' => $catTo), __METHOD__); } $redirid = $this->oldTitle->getArticleID(); if ($protected) { # Protect the redirect title as the title used to be... $dbw->insertSelect('page_restrictions', 'page_restrictions', array('pr_page' => $redirid, 'pr_type' => 'pr_type', 'pr_level' => 'pr_level', 'pr_cascade' => 'pr_cascade', 'pr_user' => 'pr_user', 'pr_expiry' => 'pr_expiry'), array('pr_page' => $pageid), __METHOD__, array('IGNORE')); // Build comment for log $comment = wfMessage('prot_1movedto2', $this->oldTitle->getPrefixedText(), $this->newTitle->getPrefixedText())->inContentLanguage()->text(); if ($reason) { $comment .= wfMessage('colon-separator')->inContentLanguage()->text() . $reason; } // reread inserted pr_ids for log relation $insertedPrIds = $dbw->select('page_restrictions', 'pr_id', array('pr_page' => $redirid), __METHOD__); $logRelationsValues = array(); foreach ($insertedPrIds as $prid) { $logRelationsValues[] = $prid->pr_id; } // Update the protection log $logEntry = new ManualLogEntry('protect', 'move_prot'); $logEntry->setTarget($this->newTitle); $logEntry->setComment($comment); $logEntry->setPerformer($user); $logEntry->setParameters(array('4::oldtitle' => $this->oldTitle->getPrefixedText())); $logEntry->setRelations(array('pr_id' => $logRelationsValues)); $logId = $logEntry->insert(); $logEntry->publish($logId); } // Update *_from_namespace fields as needed if ($this->oldTitle->getNamespace() != $this->newTitle->getNamespace()) { $dbw->update('pagelinks', array('pl_from_namespace' => $this->newTitle->getNamespace()), array('pl_from' => $pageid), __METHOD__); $dbw->update('templatelinks', array('tl_from_namespace' => $this->newTitle->getNamespace()), array('tl_from' => $pageid), __METHOD__); $dbw->update('imagelinks', array('il_from_namespace' => $this->newTitle->getNamespace()), array('il_from' => $pageid), __METHOD__); } # Update watchlists $oldtitle = $this->oldTitle->getDBkey(); $newtitle = $this->newTitle->getDBkey(); $oldsnamespace = MWNamespace::getSubject($this->oldTitle->getNamespace()); $newsnamespace = MWNamespace::getSubject($this->newTitle->getNamespace()); if ($oldsnamespace != $newsnamespace || $oldtitle != $newtitle) { WatchedItem::duplicateEntries($this->oldTitle, $this->newTitle); } $dbw->commit(__METHOD__); Hooks::run('TitleMoveComplete', array(&$this->oldTitle, &$this->newTitle, &$user, $pageid, $redirid, $reason)); return Status::newGood(); }
/** * Check if the given title already is watched by the user, and if so * add watches on a new title. To be used for page renames and such. * * @param $ot Title: page title to duplicate entries from, if present * @param $nt Title: page title to add watches on */ public static function duplicateEntries($ot, $nt) { WatchedItem::doDuplicateEntries($ot->getSubjectPage(), $nt->getSubjectPage()); WatchedItem::doDuplicateEntries($ot->getTalkPage(), $nt->getTalkPage()); }
/** * Do the rename operation */ function rename() { global $wgMemc, $wgDBname, $wgAuth; $fname = 'RenameuserSQL::rename'; wfProfileIn($fname); $dbw =& wfGetDB(DB_MASTER); foreach ($this->tables as $table => $field) { $dbw->update($table, array($field => $this->new), array($field => $this->old), $fname); } $fields = array('user_touched' => $dbw->timestamp()); // WERELATE: added isVandal if ($this->isVandal) { $fields = $fields + array('user_password' => '12345678901234567890123456789012', 'user_email' => ''); } $dbw->update('user', $fields, array('user_name' => $this->new), $fname); // Clear the user cache $wgMemc->delete("{$wgDBname}:user:id:{$this->uid}"); //WERELATE - newFromId doesn't exist // $user = User::newFromId( $this->uid ); $user = User::newFromName($this->new, false); $user->setID($this->uid); // WERELATE: added isVandal if ($this->isVandal) { // delete watchlist $rows = $dbw->select('watchlist', array('wl_namespace', 'wl_title'), array('wl_user' => $this->uid)); while ($row = $dbw->fetchObject($rows)) { $title = Title::makeTitle($row->wl_namespace, $row->wl_title); if ($title && !$title->isTalkPage()) { $wl = WatchedItem::fromUserTitle($user, $title); $wl->removeWatch(); } } $dbw->freeResult($rows); } // Inform authentication plugin of the change $wgAuth->updateExternalDB($user); wfProfileOut($fname); }