/** * Cleans up the revision list in Redis to remove old entries. * * @access public * @param array Named arguments passed by the command that queued this job. * - site_key The MD5 site key for this global watch list entry. * @return boolean Success */ public function execute($args = []) { $siteKey = $args['site_key']; if (strlen($siteKey) != 32 && $siteKey != 'master') { return false; } try { $grl = globalRevisionList::newFromSite($siteKey); if ($grl !== false) { $grl->getList(); $grl->save(); } return true; } catch (Exception $e) { self::queue($args); return false; } }
/** * Handle dispatcher watch list updates when an article is saved. * * @access public * @param object $article: WikiPage modified * @param object $user: User performing the modification * @param object $content: MW 1.19, Raw Text, MW 1.21 New content, as a Content object * @param string $summary: Edit summary/comment * @param boolean $isMinor: Whether or not the edit was marked as minor * @param boolean $isWatch: (No longer used) * @param object $section: (No longer used) * @param integer $flags: Flags passed to WikiPage::doEditContent() * @param mixed $revision: New Revision of the article * @param object $status: Status object about to be returned by doEditContent() * @param integer $baseRevId: the rev ID (or false) this edit was based on * @return boolean True */ public static function onPageContentSaveComplete($article, $user, $content, $summary, $isMinor, $isWatch, $section, $flags, $revision, $status, $baseRevId) { if ($revision instanceof Revision) { self::init(); $results = self::$DB->select(['watchlist'], ['wl_title'], ["wl_title = '" . self::$DB->strencode($revision->getTitle()->mDbkeyform) . "'"], __METHOD__); if (!$results) { return true; } $grl = globalRevisionList::newFromSite(self::$siteKey); if ($grl !== false) { $grl->addRevision($revision, true); $grl->queueUpdate(); } } return true; }
/** * Achievement List * * @access public * @param array Array of watch lists for the user. * @param string Cut Off Link HTML * @param array Array of HTML links to hide or show items. * @param array Filter options in reverse boolean. True or 1 = Hide. * @param integer Seconds into the past to allow. * @param array Information of visible site keys. * @param object SpecialGobalWatchList special page. * @return string Built HTML */ public function globalWatchlist($globalWatchlist, $pageCount, $cutOffLinks, $showHideLinks, $filterOptions, $secondsFilter, $visibleSites, $specialPage) { global $wgShowUpdatedMarker; $settingsPage = Title::newFromText('Special:GlobalWatchlist/settings'); $settingsURL = $settingsPage->getFullURL(); if ($secondsFilter == 0) { $timeFilter = 7 * 24; } else { $timeFilter = $secondsFilter / 3600; } $HTML = "\n\t\t\t\t<div id='contentSub'>" . wfMessage('gwl_for')->escaped() . " {$specialPage->wgUser->getName()} <span class='mw-watchlist-toollinks'>(<a title='{$settingsPage->getText()}' href='{$settingsURL}'>" . wfMessage('gwl_edit_settings')->escaped() . "</a>)</span></div>\n\t\t\t\t<p>" . wfMessage('watchlist-details', $pageCount)->parse() . "</p>\n\t\t\t\t<form action='{$this->urlPrefix}/Special:GlobalWatchlist' id='mw-watchlist-form' method='get' name='mw-watchlist-form'>\n\t\t\t\t\t<fieldset id='mw-watchlist-options'>\n\t\t\t\t\t\t<legend>" . wfMessage('globalwatchlist_options')->escaped() . "</legend>\n\t\t\t\t\t\t" . wfMessage($timeFilter >= 24 ? 'below_changes_in_days' : 'below_changes_in', $timeFilter >= 24 ? $timeFilter / 24 : $timeFilter, $specialPage->getLanguage()->userDate(time(), $specialPage->getUser()), $specialPage->getLanguage()->userTime(time(), $specialPage->getUser())) . "<br>\n\t\t\t\t\t\t{$cutOffLinks}<br>\n\t\t\t\t\t\t" . implode(' | ', $showHideLinks) . "\n\t\t\t\t\t\t<hr>\n\n\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t<label for='site'>" . wfMessage('wiki')->escaped() . ":</label> <select class='siteselector' id='site' name='site'>\n\t\t\t\t\t\t\t\t<option value=''" . ($specialPage->wgRequest->getVal('site') == '' ? " selected='selected'" : null) . ">" . wfMessage('gwl_all_sites') . "</option>"; if (is_array($visibleSites) && count($visibleSites)) { //Loop over and output visible sites to choose from. foreach ($visibleSites as $siteKey => $name) { if (empty($siteKey)) { continue; } $HTML .= "\n\t\t\t\t\t\t\t\t<option value='{$siteKey}'" . ($specialPage->wgRequest->getVal('site') == $siteKey ? " selected='selected'" : null) . ">{$name}</option>"; } } $HTML .= "\n\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t<label for='namespace'>" . wfMessage('namespace')->escaped() . "</label> <select class='namespaceselector' id='namespace' name='namespace'>\n\t\t\t\t\t\t\t\t<option value='all'" . ($specialPage->wgRequest->getVal('namespace') == 'all' ? " selected='selected'" : null) . ">" . wfMessage('gwl_all_ns') . "</option>\n\t\t\t\t\t\t\t\t<option value='custom'" . ($specialPage->wgRequest->getVal('namespace') == 'custom' ? " selected='selected'" : null) . ">" . wfMessage('gwl_custom_ns') . "</option>"; $namespaces = $specialPage->getLanguage()->getFormattedNamespaces(); if (is_array($namespaces) && count($namespaces)) { //Loop over and output namespaces. foreach ($namespaces as $key => $name) { if ($key < 0) { continue; } if ($key === 0) { $name = wfMessage('blanknamespace')->escaped(); } $HTML .= "\n\t\t\t\t\t\t\t\t<option value='{$key}'" . ($specialPage->wgRequest->getVal('namespace') == $key && $specialPage->wgRequest->getVal('namespace') != 'all' && $specialPage->wgRequest->getVal('namespace') != 'custom' ? " selected='selected'" : null) . ">{$name}</option>"; } } $HTML .= "\n\t\t\t\t\t\t\t</select> <input id='nsinvert' name='invert' title='" . wfMessage('tooltip-invert')->escaped() . "' type='checkbox' value='1'" . ($specialPage->wgRequest->getInt('invert') == 1 ? " checked='checked'" : null) . "> " . "<label for='nsinvert' title='" . wfMessage('tooltip-invert')->escaped() . "'>" . wfMessage('invert')->escaped() . "</label> <input id='nsassociated' name='associated' title='" . wfMessage('tooltip-namespace_association')->escaped() . "' type='checkbox' value='1'" . ($specialPage->wgRequest->getInt('associated') == 1 ? " checked='checked'" : null) . "> " . "<label for='nsassociated' title='" . wfMessage('tooltip-namespace_association')->escaped() . "'>" . wfMessage('namespace_association')->escaped() . "</label> <input type='submit' value='" . wfMessage('go')->escaped() . "'>\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<div id='collapse_expand_all'>\n\t\t\t\t\t\t\t<a href=\"#\" id='expand_all'>" . wfMessage('expand_all')->escaped() . "</a> / <a href=\"#\" id='collapse_all'>" . wfMessage('collapse_all')->escaped() . "</a>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</fieldset>\n\t\t\t\t</form>"; if (is_array($globalWatchlist) && count($globalWatchlist)) { $oldTimestamp = null; if ($secondsFilter > 0) { $oldTimestamp = time() - $secondsFilter; } $counter = 0; foreach ($globalWatchlist as $siteKey => $articles) { if (strlen($siteKey) !== 32 || !is_array($articles)) { //This usually indicates a major error. continue; } if (strlen($specialPage->wgRequest->getVal('site')) == 32 && $specialPage->wgRequest->getVal('site') != $siteKey) { //They filtered to view only one site. continue; } $_temp = current($articles); $site = $_temp['site']; $grl = globalRevisionList::newFromSite($siteKey); if (!$grl) { continue; } $revisions = $grl->getList(); $previousDate = null; $innerHTML = false; foreach ($revisions as $revision) { if (!is_object($revision)) { continue; } $foundArticle = $specialPage->searchByKeyValue($articles, ['article'], $revision->getTitle()->getText()); if ($foundArticle === false) { continue; } else { if (count($foundArticle) == 2) { //Both the subject namespace and talk namespace pages were found. Select the correct one per the revision's namespace. if ($revision->getTitle()->getNamespace() == $foundArticle[0]['article']['mNamespace']) { $article = $foundArticle[0]; } else { $article = $foundArticle[1]; } } else { $article = current($foundArticle); } } $title = $revision->getTitle(); $filterNamespaces[] = $specialPage->wgRequest->getVal('namespace'); if ($specialPage->wgRequest->getInt('associated') == 1) { if ($specialPage->wgRequest->getVal('namespace') % 2 > 0) { $extraNamespace = $specialPage->wgRequest->getVal('namespace') - 1; } else { $extraNamespace = $specialPage->wgRequest->getVal('namespace') + 1; } $filterNamespaces[] = $extraNamespace; } //Check filtering. if ($revision->isMinor() && $filterOptions['hideMinor'] || $revision->getRawUser() > 0 && $filterOptions['hideLiu'] || $revision->getRawUser() < 1 && $filterOptions['hideAnons'] || $revision->getRawUserText() == $specialPage->getUser()->getName() && $filterOptions['hideOwn'] || wfTimestamp(TS_UNIX, $revision->getTimestamp()) <= $oldTimestamp && $oldTimestamp !== null || is_numeric($specialPage->wgRequest->getVal('namespace')) && !in_array($title->getNamespace(), $filterNamespaces) || $specialPage->wgRequest->getVal('namespace') == 'custom' && $title->getNamespace() < 100) { continue; } $newDate = $specialPage->getLanguage()->userDate($revision->getTimestamp(), $specialPage->getUser()); if ($newDate != $previousDate) { $previousDate = $specialPage->getLanguage()->userDate($revision->getTimestamp(), $specialPage->getUser()); if ($ulStarted == true) { $innerHTML .= "\n\t\t\t\t\t\t\t</ul>"; $ulStarted = false; } $innerHTML .= "\n\t\t\t\t\t\t\t<h4>" . $previousDate . "</h4>"; $innerHTML .= "\n\t\t\t\t\t\t\t<ul class='special'>"; $ulStarted = true; } //This should include any trailing slashes. $siteUrlPrefix = $site['url_prefix'] . '/'; // Sort of hacky way of getting prefixed URL from a title serialized from another wiki. // Prefixed text is cached but prefixed URL is not, so we jump through a few hoops to convert one to another. // TODO someday write a RemoteTitle superclass to handle some of this stuff automatically? $titlePrefixedUrl = str_replace($title->getText(), $title->getDBkey(), $title->getPrefixedText()); $titlePrefixedUrl = wfUrlencode(str_replace(' ', '_', $titlePrefixedUrl)); $innerHTML .= "\n\t\t\t\t\t\t\t\t<li class='" . ($counter % 2 == 0 ? "mw-line-even" : "mw-line-odd") . "'>\n\t\t\t\t\t\t\t\t\t(<a href='{$siteUrlPrefix}{$titlePrefixedUrl}?diff={$revision->getId()}&oldid={$revision->getParentId()}' tabindex='{$counter}' title='{$title->getPrefixedText()}' target='_blank'>diff</a> | <a href='{$siteUrlPrefix}{$titlePrefixedUrl}?curid={$revision->getPage()}&action=history' title='{$title->getPrefixedText()}' target='_blank'>hist</a>) <span class='mw-changeslist-separator'>. .</span> " . ($revision->isMinor() ? ChangesList::flag('minor') : null) . ($revision->getParentId() === 0 ? ChangesList::flag('newpage') : null) . " <span class='mw-title'><a class='mw-changeslist-title' href='{$siteUrlPrefix}{$titlePrefixedUrl}' title='{$title->getPrefixedText()}' target='_blank'>{$title->getPrefixedText()}</a></span>‎;" . " <span class='mw-changeslist-date'>" . $specialPage->getLanguage()->userTime($revision->getTimestamp(), $specialPage->getUser()) . "</span> <span class='mw-changeslist-separator'>. .</span>" . " <span class='mw-plusminus-pos' dir='ltr' title='{$bytes} bytes after change'>(+/-{$bytesChange})</span>‎ <span class='mw-changeslist-separator'>. .</span> ‎" . "<a class='mw-userlink' href='{$siteUrlPrefix}User:{$revision->getUserText(Revision::RAW)}' title='User:{$revision->getUserText(Revision::RAW)}' target='_blank'>{$revision->getUserText(Revision::RAW)}</a>" . "<span class='mw-usertoollinks'>(<a href='{$siteUrlPrefix}User_talk:{$revision->getUserText(Revision::RAW)}' title='User talk:{$revision->getUserText(Revision::RAW)}' target='_blank'>talk</a> | <a href='{$siteUrlPrefix}Special:Contributions/{$revision->getUserText(Revision::RAW)}' title='Special:Contributions/{$revision->getUserText(Revision::RAW)}' target='_blank'>contribs</a>)</span>‎ " . ($revision->getRawComment() != '' ? "<span class='comment'>({$revision->getRawComment()})</span>" : null) . "\n\t\t\t\t\t\t\t\t</li>"; $counter++; } if ($ulStarted == true) { $innerHTML .= "\n\t\t\t\t\t\t\t</ul>"; $ulStarted = false; } $HTML .= "\n\t\t\t\t<form class='mw-changeslist-site site-{$siteKey}'>\n\t\t\t\t\t<fieldset>\n\t\t\t\t\t\t<legend>{$site['wiki_name']}<span class='view_all'>[<a href='{$siteUrlPrefix}Special:Watchlist' target='_blank'>" . wfMessage('view_all')->escaped() . "</a>]</span><span class='site_expand_collapse'>[ <span title='" . wfMessage('collapse_expand')->escaped() . "'>-</span> ]</span><span>(" . wfMessage('gwl_total_items', $counter)->escaped() . ")</span></legend>"; if ($innerHTML !== false) { $HTML .= "\n\t\t\t\t\t\t<div class='mw-changeslist'>\n\t\t\t\t\t\t\t{$innerHTML}\n\t\t\t\t\t\t</div>"; } else { $HTML .= "\n\t\t\t\t\t\t<div class='mw-changeslist-empty'>\n\t\t\t\t\t\t\t<p>" . wfMessage('gwl_noresult')->escaped() . "</p>\n\t\t\t\t\t\t</div>"; } $HTML .= "\n\t\t\t\t\t</fieldset>\n\t\t\t\t</form>"; } } else { $HTML .= "\n\t\t\t<div class='mw-changeslist'>\n\t\t\t\t<div class='mw-changeslist-empty'>\n\t\t\t\t\t<p>" . wfMessage('gwl_noresult')->escaped() . "</p>\n\t\t\t\t</div>\n\t\t\t</div>"; } return $HTML; }