/** * Insert the tags of the given change */ private function formatReviewSummaryRow($rc, $page) { global $wgRequest; $s = ''; if (!$rc) { return $s; } $attr = $rc->mAttribs; $tagRows = $attr['collabwatchlist_tags']; $classes = array(); $displayTags = array(); foreach ($tagRows as $tagRow) { $tag = $tagRow['ct_tag']; $collabwatchlistTag = Xml::tags('span', array('class' => 'mw-collabwatchlist-tag-marker ' . Sanitizer::escapeClass("mw-collabwatchlist-tag-marker-{$tag}"), 'title' => $tagRow['rrt_comment']), ChangeTags::tagDescription($tag)); $classes[] = Sanitizer::escapeClass("mw-collabwatchlist-tag-{$tag}"); /** Insert links to user page, user talk page and eventually a blocking link */ $userLink = $this->skin->userLink($tagRow['user_id'], $tagRow['user_name']); $delTagTarget = CollabWatchlistEditor::getUnsetTagUrl($wgRequest->getFullRequestURL(), $attr['rc_title'], $tagRow['cw_id'], $tag, $attr['rc_id']); $delTagLink = Xml::element('a', array('href' => $delTagTarget, 'class' => 'mw-collabwatchlist-unsettag-' . $tag), wfMsg('collabwatchlist-unset-tag')); $displayTags[] = $collabwatchlistTag . ' ' . $delTagLink . ' ' . $userLink; } $markers = '(' . implode(', ', $displayTags) . ')'; $markers = Xml::tags('span', array('class' => 'mw-collabwatchlist-tag-markers'), $markers); return array($markers, $classes); }
/** * Main execution point * * @param $par Parameter passed to the page */ function execute($par) { global $wgUser, $wgOut, $wgLang, $wgRequest; global $wgRCShowWatchingUsers; global $wgEnotifWatchlist; // Add feed links $wlToken = $wgUser->getOption('watchlisttoken'); if (!$wlToken) { $wlToken = sha1(mt_rand() . microtime(true)); $wgUser->setOption('watchlisttoken', $wlToken); $wgUser->saveSettings(); } global $wgFeedClasses; $apiParams = array('action' => 'feedwatchlist', 'allrev' => 'allrev', 'wlowner' => $wgUser->getName(), 'wltoken' => $wlToken); $feedTemplate = wfScript('api') . '?'; foreach ($wgFeedClasses as $format => $class) { $theseParams = $apiParams + array('feedformat' => $format); $url = $feedTemplate . wfArrayToCGI($theseParams); $wgOut->addFeedLink($format, $url); } $skin = $wgUser->getSkin(); $specialTitle = SpecialPage::getTitleFor('CollabWatchlist'); $wgOut->setRobotPolicy('noindex,nofollow'); # Anons don't get a watchlist if ($wgUser->isAnon()) { $wgOut->setPageTitle(wfMsg('watchnologin')); $llink = $skin->linkKnown(SpecialPage::getTitleFor('Userlogin'), wfMsgHtml('loginreqlink'), array(), array('returnto' => $specialTitle->getPrefixedText())); $wgOut->addHTML(wfMsgWikiHtml('watchlistanontext', $llink)); return; } $wgOut->setPageTitle(wfMsg('collabwatchlist')); $listIdsAndNames = CollabWatchlistChangesList::getCollabWatchlistIdAndName($wgUser->getId()); $sub = wfMsgExt('watchlistfor2', array('parseinline', 'replaceafter'), $wgUser->getName(), ''); $sub .= '<br />' . CollabWatchlistEditor::buildTools($listIdsAndNames, $wgUser->getSkin()); $wgOut->setSubtitle($sub); $uid = $wgUser->getId(); // The filter form has one checkbox for each tag, build an array $postValues = $wgRequest->getValues(); $tagFilter = array(); foreach ($postValues as $key => $value) { if (stripos($key, 'collaborative-watchlist-filtertag-') === 0) { $tagFilter[] = $postValues[$key]; } } // Alternative syntax for requests from links (show / hide ...) if (empty($tagFilter)) { $tagFilter = explode('|', $wgRequest->getVal('filterTags')); } $defaults = array('days' => floatval($wgUser->getOption('watchlistdays')), 'hideMinor' => (int) $wgUser->getBoolOption('watchlisthideminor'), 'hideBots' => (int) $wgUser->getBoolOption('watchlisthidebots'), 'hideAnons' => (int) $wgUser->getBoolOption('watchlisthideanons'), 'hideLiu' => (int) $wgUser->getBoolOption('watchlisthideliu'), 'hideListUser' => (int) $wgUser->getBoolOption('collabwatchlisthidelistuser'), 'hidePatrolled' => (int) $wgUser->getBoolOption('watchlisthidepatrolled'), 'hideOwn' => (int) $wgUser->getBoolOption('watchlisthideown'), 'collabwatchlist' => 0, 'globalwatch' => 'all', 'invert' => false, 'invertTags' => false, 'filterTags' => ''); extract($defaults); # Extract variables from the request, falling back to user preferences or # other default values if these don't exist $prefs['days'] = floatval($wgUser->getOption('watchlistdays')); $prefs['hideminor'] = $wgUser->getBoolOption('watchlisthideminor'); $prefs['hidebots'] = $wgUser->getBoolOption('watchlisthidebots'); $prefs['hideanons'] = $wgUser->getBoolOption('watchlisthideanon'); $prefs['hideliu'] = $wgUser->getBoolOption('watchlisthideliu'); $prefs['hideown'] = $wgUser->getBoolOption('watchlisthideown'); $prefs['hidelistuser'] = $wgUser->getBoolOption('collabwatchlisthidelistuser'); $prefs['hidepatrolled'] = $wgUser->getBoolOption('watchlisthidepatrolled'); $prefs['invertTags'] = $wgUser->getBoolOption('collabwatchlistinverttags'); $prefs['filterTags'] = $wgUser->getOption('collabwatchlistfiltertags'); # Get query variables $days = $wgRequest->getVal('days', $prefs['days']); $hideMinor = $wgRequest->getBool('hideMinor', $prefs['hideminor']); $hideBots = $wgRequest->getBool('hideBots', $prefs['hidebots']); $hideAnons = $wgRequest->getBool('hideAnons', $prefs['hideanons']); $hideLiu = $wgRequest->getBool('hideLiu', $prefs['hideliu']); $hideOwn = $wgRequest->getBool('hideOwn', $prefs['hideown']); $hideListUser = $wgRequest->getBool('hideListUser', $prefs['hidelistuser']); $hidePatrolled = $wgRequest->getBool('hidePatrolled', $prefs['hidepatrolled']); $filterTags = implode('|', $tagFilter); $invertTags = $wgRequest->getBool('invertTags', $prefs['invertTags']); # Get collabwatchlist value, if supplied, and prepare a WHERE fragment $collabWatchlist = $wgRequest->getIntOrNull('collabwatchlist'); if (!is_null($collabWatchlist) && $collabWatchlist !== 'all') { $collabWatchlist = intval($collabWatchlist); } if (array_key_exists($collabWatchlist, $listIdsAndNames)) { $wgOut->addHTML(Xml::element('h2', null, $listIdsAndNames[$collabWatchlist])); } if (($mode = CollabWatchlistEditor::getMode($wgRequest, $par)) !== false) { $editor = new CollabWatchlistEditor(); $editor->execute($collabWatchlist, $listIdsAndNames, $wgOut, $wgRequest, $mode); return; } if (!$collabWatchlist) { return; } $dbr = wfGetDB(DB_SLAVE, 'watchlist'); $recentchanges = $dbr->tableName('recentchanges'); $nitems = $dbr->selectField('collabwatchlistcategory', 'COUNT(*)', $collabWatchlist == 0 ? array() : array('cw_id' => $collabWatchlist), __METHOD__); if ($nitems == 0) { $wgOut->addWikiMsg('nowatchlist'); return; } // Dump everything here $nondefaults = array(); wfAppendToArrayIfNotDefault('days', $days, $defaults, $nondefaults); wfAppendToArrayIfNotDefault('hideMinor', (int) $hideMinor, $defaults, $nondefaults); wfAppendToArrayIfNotDefault('hideBots', (int) $hideBots, $defaults, $nondefaults); wfAppendToArrayIfNotDefault('hideAnons', (int) $hideAnons, $defaults, $nondefaults); wfAppendToArrayIfNotDefault('hideLiu', (int) $hideLiu, $defaults, $nondefaults); wfAppendToArrayIfNotDefault('hideOwn', (int) $hideOwn, $defaults, $nondefaults); wfAppendToArrayIfNotDefault('hideListUser', (int) $hideListUser, $defaults, $nondefaults); wfAppendToArrayIfNotDefault('collabwatchlist', $collabWatchlist, $defaults, $nondefaults); wfAppendToArrayIfNotDefault('hidePatrolled', (int) $hidePatrolled, $defaults, $nondefaults); wfAppendToArrayIfNotDefault('filterTags', $filterTags, $defaults, $nondefaults); wfAppendToArrayIfNotDefault('invertTags', $invertTags, $defaults, $nondefaults); if ($days <= 0) { $andcutoff = ''; } else { $andcutoff = "rc_timestamp > '" . $dbr->timestamp(time() - intval($days * 86400)) . "'"; } # If the watchlist is relatively short, it's simplest to zip # down its entirety and then sort the results. # If it's relatively long, it may be worth our while to zip # through the time-sorted page list checking for watched items. # Up estimate of watched items by 15% to compensate for talk pages... # Toggles $andHideOwn = $hideOwn ? "rc_user != {$uid}" : ''; $andHideBots = $hideBots ? "rc_bot = 0" : ''; $andHideMinor = $hideMinor ? "rc_minor = 0" : ''; $andHideLiu = $hideLiu ? "rc_user = 0" : ''; $andHideAnons = $hideAnons ? "rc_user != 0" : ''; $andHideListUser = $hideListUser ? $this->wlGetFilterClauseListUser($collabWatchlist) : ''; $andHidePatrolled = $wgUser->useRCPatrol() && $hidePatrolled ? "rc_patrolled != 1" : ''; # Toggle watchlist content (all recent edits or just the latest) if ($wgUser->getOption('extendwatchlist')) { $andLatest = ''; $limitWatchlist = intval($wgUser->getOption('wllimit')); $usePage = false; } else { # Top log Ids for a page are not stored $andLatest = 'rc_this_oldid=page_latest OR rc_type=' . RC_LOG; $limitWatchlist = 0; $usePage = true; } # Show a message about slave lag, if applicable $lag = wfGetLB()->safeGetLag($dbr); if ($lag > 0) { $wgOut->showLagWarning($lag); } # Create output form $form = Xml::fieldset(wfMsg('watchlist-options'), false, array('id' => 'mw-watchlist-options')); # Show watchlist header $form .= wfMsgExt('collabwatchlist-details', array('parseinline'), $wgLang->formatNum($nitems)); if ($wgUser->getOption('enotifwatchlistpages') && $wgEnotifWatchlist) { $form .= wfMsgExt('wlheader-enotif', 'parse') . "\n"; } $form .= '<hr />'; $tables = array('recentchanges', 'categorylinks'); $fields = array("{$recentchanges}.*"); $categoryClause = $this->wlGetFilterClauseForCollabWatchlistIds($collabWatchlist, 'cl_to', 'rc_cur_id'); // If this collaborative watchlist does not contain any categories, add a clause which gives // us an empty result $conds = isset($categoryClause) ? array($categoryClause) : array('false'); $join_conds = array('categorylinks' => array('LEFT OUTER JOIN', "rc_cur_id=cl_from")); if (!empty($tagFilter)) { // The tag filter causes a query runtime of O(MxN), where M is relative to the number // of recentchanges we select (from a table which is purged periodically, limited to 250) // and N is relative the number of change_tag entries for a recentchange. Doing it // the other way around (selecting from change_tag first, is probably slower, as the // change_tag table is never purged. // Using the tag_summary table for filtering is difficult, at least I have been unable to // find a common SQL compliant way for using regular expressions which works across Postgre / Mysql // Furthermore, ChangeTags does not seem to prevent tags containing ',' from being set, // which renders tag_summary quite unusable if ($invertTags) { $filter = 'EXISTS '; } else { $filter = 'NOT EXISTS '; } $filter .= '(SELECT cwlrt.ct_rc_id FROM collabwatchlistrevisiontag cwlrt WHERE cwlrt.ct_rc_id = recentchanges.rc_id AND cwlrt.ct_tag '; if (count($tagFilter) > 1) { $filter .= 'IN (' . $dbr->makeList($tagFilter) . '))'; } else { $filter .= ' = ' . $dbr->addQuotes(current($tagFilter)) . ')'; } $conds[] = $filter; } $options = array('ORDER BY' => 'rc_timestamp DESC'); if ($limitWatchlist) { $options['LIMIT'] = $limitWatchlist; } if ($andcutoff) { $conds[] = $andcutoff; } if ($andLatest) { $conds[] = $andLatest; } if ($andHideOwn) { $conds[] = $andHideOwn; } if ($andHideBots) { $conds[] = $andHideBots; } if ($andHideMinor) { $conds[] = $andHideMinor; } if ($andHideLiu) { $conds[] = $andHideLiu; } if ($andHideAnons) { $conds[] = $andHideAnons; } if ($andHideListUser) { $conds[] = $andHideListUser; } if ($andHidePatrolled) { $conds[] = $andHidePatrolled; } $rollbacker = $wgUser->isAllowed('rollback'); if ($usePage || $rollbacker) { $tables[] = 'page'; $join_conds['page'] = array('LEFT JOIN', 'rc_cur_id=page.page_id'); if ($rollbacker) { $fields[] = 'page_latest'; } } ChangeTags::modifyDisplayQuery($tables, $fields, $conds, $join_conds, $options, ''); wfRunHooks('SpecialCollabWatchlistQuery', array(&$conds, &$tables, &$join_conds, &$fields)); $res = $dbr->select($tables, $fields, $conds, __METHOD__, $options, $join_conds); $numRows = $dbr->numRows($res); /* Start bottom header */ $wlInfo = ''; if ($days >= 1) { $wlInfo = wfMsgExt('rcnote', 'parseinline', $wgLang->formatNum($numRows), $wgLang->formatNum($days), $wgLang->timeAndDate(wfTimestampNow(), true), $wgLang->date(wfTimestampNow(), true), $wgLang->time(wfTimestampNow(), true)) . '<br />'; } elseif ($days > 0) { $wlInfo = wfMsgExt('wlnote', 'parseinline', $wgLang->formatNum($numRows), $wgLang->formatNum(round($days * 24))) . '<br />'; } $cutofflinks = "\n" . $this->cutoffLinks($days, 'CollabWatchlist', $nondefaults) . "<br />\n"; $thisTitle = SpecialPage::getTitleFor('CollabWatchlist'); # Spit out some control panel links $links[] = $this->showHideLink($nondefaults, 'rcshowhideminor', 'hideMinor', $hideMinor); $links[] = $this->showHideLink($nondefaults, 'rcshowhidebots', 'hideBots', $hideBots); $links[] = $this->showHideLink($nondefaults, 'rcshowhideanons', 'hideAnons', $hideAnons); $links[] = $this->showHideLink($nondefaults, 'rcshowhideliu', 'hideLiu', $hideLiu); $links[] = $this->showHideLink($nondefaults, 'rcshowhidemine', 'hideOwn', $hideOwn); $links[] = $this->showHideLink($nondefaults, 'collabwatchlistshowhidelistusers', 'hideListUser', $hideListUser); if ($wgUser->useRCPatrol()) { $links[] = $this->showHideLink($nondefaults, 'rcshowhidepatr', 'hidePatrolled', $hidePatrolled); } # Namespace filter and put the whole form together. $form .= $wlInfo; $form .= $cutofflinks; $form .= $wgLang->pipeList($links); $form .= Xml::openElement('form', array('method' => 'get', 'action' => $thisTitle->getLocalUrl())); $form .= '<hr /><p>'; $tagsAndInfo = CollabWatchlistChangesList::getValidTagsAndInfo(array_keys($listIdsAndNames)); if (count($tagsAndInfo) > 0) { $form .= wfMsg('collabwatchlistfiltertags') . ': '; } foreach ($tagsAndInfo as $tag => $tagInfo) { $tagAttr = array('name' => 'collaborative-watchlist-filtertag-' . $tag, 'type' => 'checkbox', 'value' => $tag); if (in_array($tag, $tagFilter)) { $tagAttr['checked'] = 'checked'; } $form .= Xml::element('input', $tagAttr) . ' ' . Xml::label($tag, 'collaborative-watchlist-filtertag-' . $tag) . ' '; } if (count($tagsAndInfo) > 0) { $form .= '<br />'; } $form .= Xml::checkLabel(wfMsg('collabwatchlistinverttags'), 'invertTags', 'nsinvertTags', $invertTags) . '<br />'; $form .= CollabWatchlistChangesList::collabWatchlistSelector($listIdsAndNames, $collabWatchlist, '', 'collabwatchlist', wfMsg('collabwatchlist')) . ' '; $form .= Xml::submitButton(wfMsg('allpagessubmit')) . '</p>'; $form .= Html::hidden('days', $days); if ($hideMinor) { $form .= Html::hidden('hideMinor', 1); } if ($hideBots) { $form .= Html::hidden('hideBots', 1); } if ($hideAnons) { $form .= Html::hidden('hideAnons', 1); } if ($hideLiu) { $form .= Html::hidden('hideLiu', 1); } if ($hideOwn) { $form .= Html::hidden('hideOwn', 1); } if ($hideListUser) { $form .= Html::hidden('hideListUser', 1); } if ($wgUser->useRCPatrol()) { if ($hidePatrolled) { $form .= Html::hidden('hidePatrolled', 1); } } $form .= Xml::closeElement('form'); $form .= Xml::closeElement('fieldset'); $wgOut->addHTML($form); # If there's nothing to show, stop here if ($numRows == 0) { $wgOut->addWikiMsg('watchnochange'); return; } /* End bottom header */ /* Do link batch query */ $linkBatch = new LinkBatch(); foreach ($res as $row) { $userNameUnderscored = str_replace(' ', '_', $row->rc_user_text); if ($row->rc_user != 0) { $linkBatch->add(NS_USER, $userNameUnderscored); } $linkBatch->add(NS_USER_TALK, $userNameUnderscored); $linkBatch->add($row->rc_namespace, $row->rc_title); } $linkBatch->execute(); $dbr->dataSeek($res, 0); $list = CollabWatchlistChangesList::newFromUser($wgUser); $list->setWatchlistDivs(); $s = $list->beginRecentChangesList(); $counter = 1; foreach ($res as $obj) { # Make RC entry $rc = RecentChange::newFromRow($obj); $rc->counter = $counter++; if ($wgRCShowWatchingUsers && $wgUser->getOption('shownumberswatching')) { $rc->numberofWatchingusers = $dbr->selectField('watchlist', 'COUNT(*)', array('wl_namespace' => $obj->rc_namespace, 'wl_title' => $obj->rc_title), __METHOD__); } else { $rc->numberofWatchingusers = 0; } $tags = $this->wlTagsForRevision($obj->rc_this_oldid, array($collabWatchlist)); // if( isset($tags) ) { // // Filter recentchanges which contain unwanted tags // $tagNames = array(); // foreach($tags as $tagInfo) { // $tagNames[] = $tagInfo['ct_tag']; // } // $unwantedTagsFound = array_intersect($tagFilter, $tagNames); // if( !empty($unwantedTagsFound) ) // continue; // } $attrs = $rc->getAttributes(); $attrs['collabwatchlist_tags'] = $tags; $rc->setAttribs($attrs); $s .= $list->recentChangesLine($rc, false, $counter); } $s .= $list->endRecentChangesList(); $dbr->freeResult($res); $wgOut->addHTML($s); }