/**
  * @covers RecentChange::newFromRow
  * @covers RecentChange::loadFromRow
  */
 public function testNewFromRow()
 {
     $row = new stdClass();
     $row->rc_foo = 'AAA';
     $row->rc_timestamp = '20150921134808';
     $row->rc_deleted = 'bar';
     $rc = RecentChange::newFromRow($row);
     $expected = array('rc_foo' => 'AAA', 'rc_timestamp' => '20150921134808', 'rc_deleted' => 'bar');
     $this->assertEquals($expected, $rc->getAttributes());
 }
 function doTest()
 {
     // Quick syntax check.
     $out = $this->getOutput();
     $result = AbuseFilter::checkSyntax($this->mFilter);
     if ($result !== true) {
         $out->addWikiMsg('abusefilter-test-syntaxerr');
         return;
     }
     $dbr = wfGetDB(DB_SLAVE);
     $conds = array('rc_user_text' => $this->mTestUser, 'rc_type != ' . RC_EXTERNAL);
     if ($this->mTestPeriodStart) {
         $conds[] = 'rc_timestamp >= ' . $dbr->addQuotes($dbr->timestamp(strtotime($this->mTestPeriodStart)));
     }
     if ($this->mTestPeriodEnd) {
         $conds[] = 'rc_timestamp <= ' . $dbr->addQuotes($dbr->timestamp(strtotime($this->mTestPeriodEnd)));
     }
     if ($this->mTestPage) {
         $title = Title::newFromText($this->mTestPage);
         if ($title instanceof Title) {
             $conds['rc_namespace'] = $title->getNamespace();
             $conds['rc_title'] = $title->getDBkey();
         } else {
             $out->addWikiMsg('abusefilter-test-badtitle');
             return;
         }
     }
     // Get our ChangesList
     $changesList = new AbuseFilterChangesList($this->getSkin());
     $output = $changesList->beginRecentChangesList();
     $res = $dbr->select('recentchanges', '*', array_filter($conds), __METHOD__, array('LIMIT' => self::$mChangeLimit, 'ORDER BY' => 'rc_timestamp desc'));
     $counter = 1;
     foreach ($res as $row) {
         $vars = AbuseFilter::getVarsFromRCRow($row);
         if (!$vars) {
             continue;
         }
         $result = AbuseFilter::checkConditions($this->mFilter, $vars);
         if ($result || $this->mShowNegative) {
             // Stash result in RC item
             $rc = RecentChange::newFromRow($row);
             $rc->examineParams['testfilter'] = $this->mFilter;
             $rc->filterResult = $result;
             $rc->counter = $counter++;
             $output .= $changesList->recentChangesLine($rc, false);
         }
     }
     $output .= $changesList->endRecentChangesList();
     $out->addHTML($output);
 }
 private function buildQuickWatchlist()
 {
     global $wgShowUpdatedMarker, $wgRCShowWatchingUsers;
     $user = $this->getUser();
     // Building the request
     $dbr = wfGetDB(DB_SLAVE, 'watchlist');
     $tables = array('recentchanges', 'watchlist');
     $fields = array($dbr->tableName('recentchanges') . '.*');
     if ($wgShowUpdatedMarker) {
         $fields[] = 'wl_notificationtimestamp';
     }
     $conds = array();
     $conds[] = 'rc_this_oldid=page_latest OR rc_type=' . RC_LOG;
     $limitWatchlist = 0;
     $usePage = true;
     $join_conds = array('watchlist' => array('INNER JOIN', "wl_user='******' AND wl_namespace=rc_namespace AND wl_title=rc_title"));
     $options = array('LIMIT' => 5, 'ORDER BY' => 'rc_timestamp DESC');
     $rollbacker = $user->isAllowed('rollback');
     if ($usePage || $rollbacker) {
         $tables[] = 'page';
         $join_conds['page'] = array('LEFT JOIN', 'rc_cur_id=page_id');
         if ($rollbacker) {
             $fields[] = 'page_latest';
         }
     }
     ChangeTags::modifyDisplayQuery($tables, $fields, $conds, $join_conds, $options, '');
     $res = $dbr->select($tables, $fields, $conds, __METHOD__, $options, $join_conds);
     $numRows = $dbr->numRows($res);
     $s = '<div class="ms-info-wl">' . wfMessage('ms-watchlist')->parse() . '</div>';
     if ($numRows == 0) {
         $s .= '<p class="ms-wl-empty">' . wfMessage('watchnochange')->parse() . '</p>';
     } else {
         /* 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 = ChangesList::newFromContext($this->getContext());
         $list->setWatchlistDivs();
         $s .= $list->beginRecentChangesList();
         $counter = 1;
         foreach ($res as $obj) {
             # Make RC entry
             $rc = RecentChange::newFromRow($obj);
             $rc->counter = $counter++;
             // Updated markers are always shown
             $updated = $obj->wl_notificationtimestamp;
             // We don't display the count of watching users
             $rc->numberofWatchingusers = 0;
             $s .= $list->recentChangesLine($rc, $updated, $counter);
         }
         $s .= $list->endRecentChangesList();
     }
     return $s;
 }
 /**
  * Build and output the actual changes list.
  *
  * @param ResultWrapper $rows Database rows
  * @param FormOptions $opts
  */
 public function outputChangesList($rows, $opts)
 {
     $dbr = $this->getDB();
     $user = $this->getUser();
     $output = $this->getOutput();
     # Show a message about slave lag, if applicable
     $lag = wfGetLB()->safeGetLag($dbr);
     if ($lag > 0) {
         $output->showLagWarning($lag);
     }
     # If no rows to display, show message before try to render the list
     if ($rows->numRows() == 0) {
         $output->wrapWikiMsg("<div class='mw-changeslist-empty'>\n\$1\n</div>", 'recentchanges-noresult');
         return;
     }
     $dbr->dataSeek($rows, 0);
     $list = ChangesList::newFromContext($this->getContext());
     $list->setWatchlistDivs();
     $list->initChangesListRows($rows);
     $dbr->dataSeek($rows, 0);
     $s = $list->beginRecentChangesList();
     $counter = 1;
     foreach ($rows as $obj) {
         # Make RC entry
         $rc = RecentChange::newFromRow($obj);
         $rc->counter = $counter++;
         if ($this->getConfig()->get('ShowUpdatedMarker')) {
             $updated = $obj->wl_notificationtimestamp;
         } else {
             $updated = false;
         }
         if ($this->getConfig()->get('RCShowWatchingUsers') && $user->getOption('shownumberswatching')) {
             $rc->numberofWatchingusers = $dbr->selectField('watchlist', 'COUNT(*)', array('wl_namespace' => $obj->rc_namespace, 'wl_title' => $obj->rc_title), __METHOD__);
         } else {
             $rc->numberofWatchingusers = 0;
         }
         $changeLine = $list->recentChangesLine($rc, $updated, $counter);
         if ($changeLine !== false) {
             $s .= $changeLine;
         }
     }
     $s .= $list->endRecentChangesList();
     $output->addHTML($s);
 }
 /**
  * Extracts from a single sql row the data needed to describe one recent change.
  *
  * @param $row The row from which to extract the data.
  * @return An array mapping strings (descriptors) to their respective string values.
  * @access public
  */
 public function extractRowInfo($row)
 {
     /* If page was moved somewhere, get the title of the move target. */
     $movedToTitle = false;
     if (isset($row->rc_moved_to_title) && $row->rc_moved_to_title !== '') {
         $movedToTitle = Title::makeTitle($row->rc_moved_to_ns, $row->rc_moved_to_title);
     }
     /* Determine the title of the page that has been changed. */
     $title = Title::makeTitle($row->rc_namespace, $row->rc_title);
     /* Our output data. */
     $vals = array();
     $type = intval($row->rc_type);
     /* Determine what kind of change this was. */
     switch ($type) {
         case RC_EDIT:
             $vals['type'] = 'edit';
             break;
         case RC_NEW:
             $vals['type'] = 'new';
             break;
         case RC_MOVE:
             $vals['type'] = 'move';
             break;
         case RC_LOG:
             $vals['type'] = 'log';
             break;
         case RC_MOVE_OVER_REDIRECT:
             $vals['type'] = 'move over redirect';
             break;
         default:
             $vals['type'] = $type;
     }
     /* Create a new entry in the result for the title. */
     if ($this->fld_title) {
         ApiQueryBase::addTitleInfo($vals, $title);
         if ($movedToTitle) {
             ApiQueryBase::addTitleInfo($vals, $movedToTitle, 'new_');
         }
     }
     /* Add ids, such as rcid, pageid, revid, and oldid to the change's info. */
     if ($this->fld_ids) {
         $vals['rcid'] = intval($row->rc_id);
         $vals['pageid'] = intval($row->rc_cur_id);
         $vals['revid'] = intval($row->rc_this_oldid);
         $vals['old_revid'] = intval($row->rc_last_oldid);
     }
     /* Add user data and 'anon' flag, if use is anonymous. */
     if ($this->fld_user || $this->fld_userid) {
         if ($this->fld_user) {
             $vals['user'] = $row->rc_user_text;
         }
         if ($this->fld_userid) {
             $vals['userid'] = $row->rc_user;
         }
         if (!$row->rc_user) {
             $vals['anon'] = '';
         }
     }
     /* Add flags, such as new, minor, bot. */
     if ($this->fld_flags) {
         if ($row->rc_bot) {
             $vals['bot'] = '';
         }
         if ($row->rc_new) {
             $vals['new'] = '';
         }
         if ($row->rc_minor) {
             $vals['minor'] = '';
         }
     }
     /* Add sizes of each revision. (Only available on 1.10+) */
     if ($this->fld_sizes) {
         $vals['oldlen'] = intval($row->rc_old_len);
         $vals['newlen'] = intval($row->rc_new_len);
     }
     /* Add the timestamp. */
     if ($this->fld_timestamp) {
         $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->rc_timestamp);
     }
     /* Add edit summary / log summary. */
     if ($this->fld_comment && isset($row->rc_comment)) {
         $vals['comment'] = $row->rc_comment;
     }
     if ($this->fld_parsedcomment && isset($row->rc_comment)) {
         $vals['parsedcomment'] = Linker::formatComment($row->rc_comment, $title);
     }
     if ($this->fld_redirect) {
         if ($row->page_is_redirect) {
             $vals['redirect'] = '';
         }
     }
     /* Add the patrolled flag */
     if ($this->fld_patrolled && $row->rc_patrolled == 1) {
         $vals['patrolled'] = '';
     }
     if ($this->fld_loginfo && $row->rc_type == RC_LOG) {
         $vals['logid'] = intval($row->rc_logid);
         $vals['logtype'] = $row->rc_log_type;
         $vals['logaction'] = $row->rc_log_action;
         ApiQueryLogEvents::addLogParams($this->getResult(), $vals, $row->rc_params, $row->rc_log_action, $row->rc_log_type, $row->rc_timestamp);
     }
     if ($this->fld_tags) {
         if ($row->ts_tags) {
             $tags = explode(',', $row->ts_tags);
             $this->getResult()->setIndexedTagName($tags, 'tag');
             $vals['tags'] = $tags;
         } else {
             $vals['tags'] = array();
         }
     }
     if (!is_null($this->token)) {
         $tokenFunctions = $this->getTokenFunctions();
         foreach ($this->token as $t) {
             $val = call_user_func($tokenFunctions[$t], $row->rc_cur_id, $title, RecentChange::newFromRow($row));
             if ($val === false) {
                 $this->setWarning("Action '{$t}' is not allowed for the current user");
             } else {
                 $vals[$t . 'token'] = $val;
             }
         }
     }
     if ($this->fld_wikiamode) {
         $vals['rc_params'] = $row->rc_params;
     }
     return $vals;
 }
 /**
  * Send output to the OutputPage object, only called if not used feeds
  *
  * @param array $rows Database rows
  * @param FormOptions $opts
  */
 public function webOutput($rows, $opts)
 {
     global $wgRCShowWatchingUsers, $wgShowUpdatedMarker, $wgAllowCategorizedRecentChanges;
     $limit = $opts['limit'];
     if (!$this->including()) {
         // Output options box
         $this->doHeader($opts);
     }
     // And now for the content
     $feedQuery = $this->getFeedQuery();
     if ($feedQuery !== '') {
         $this->getOutput()->setFeedAppendQuery($feedQuery);
     } else {
         $this->getOutput()->setFeedAppendQuery(false);
     }
     if ($wgAllowCategorizedRecentChanges) {
         $this->filterByCategories($rows, $opts);
     }
     $showNumsWachting = $this->getUser()->getOption('shownumberswatching');
     $showWatcherCount = $wgRCShowWatchingUsers && $showNumsWachting;
     $watcherCache = array();
     $dbr = wfGetDB(DB_SLAVE);
     $counter = 1;
     $list = ChangesList::newFromContext($this->getContext());
     $s = $list->beginRecentChangesList();
     foreach ($rows as $obj) {
         if ($limit == 0) {
             break;
         }
         $rc = RecentChange::newFromRow($obj);
         $rc->counter = $counter++;
         # Check if the page has been updated since the last visit
         if ($wgShowUpdatedMarker && !empty($obj->wl_notificationtimestamp)) {
             $rc->notificationtimestamp = $obj->rc_timestamp >= $obj->wl_notificationtimestamp;
         } else {
             $rc->notificationtimestamp = false;
             // Default
         }
         # Check the number of users watching the page
         $rc->numberofWatchingusers = 0;
         // Default
         if ($showWatcherCount && $obj->rc_namespace >= 0) {
             if (!isset($watcherCache[$obj->rc_namespace][$obj->rc_title])) {
                 $watcherCache[$obj->rc_namespace][$obj->rc_title] = $dbr->selectField('watchlist', 'COUNT(*)', array('wl_namespace' => $obj->rc_namespace, 'wl_title' => $obj->rc_title), __METHOD__ . '-watchers');
             }
             $rc->numberofWatchingusers = $watcherCache[$obj->rc_namespace][$obj->rc_title];
         }
         $changeLine = $list->recentChangesLine($rc, !empty($obj->wl_user), $counter);
         if ($changeLine !== false) {
             $s .= $changeLine;
             --$limit;
         }
     }
     $s .= $list->endRecentChangesList();
     $this->getOutput()->addHTML($s);
 }
	/**
	 * Execute
	 * @param $par Parameter passed to the page
	 */
	function execute( $par ) {
		global $wgRCShowWatchingUsers, $wgEnotifWatchlist, $wgShowUpdatedMarker;

		$user = $this->getUser();
		$output = $this->getOutput();

		# Anons don't get a watchlist
		if ( $user->isAnon() ) {
			$output->setPageTitle( $this->msg( 'watchnologin' ) );
			$output->setRobotPolicy( 'noindex,nofollow' );
			$llink = Linker::linkKnown(
				SpecialPage::getTitleFor( 'Userlogin' ),
				$this->msg( 'loginreqlink' )->escaped(),
				array(),
				array( 'returnto' => $this->getTitle()->getPrefixedText() )
			);
			$output->addHTML( $this->msg( 'watchlistanontext' )->rawParams( $llink )->parse() );
			return;
		}

		// Check permissions
		$this->checkPermissions();

		// Add feed links
		$wlToken = $user->getTokenFromOption( 'watchlisttoken' );
		if ( $wlToken ) {
			$this->addFeedLinks( array( 'action' => 'feedwatchlist', 'allrev' => 'allrev',
								'wlowner' => $user->getName(), 'wltoken' => $wlToken ) );
		}

		$this->setHeaders();
		$this->outputHeader();

		$output->addSubtitle( $this->msg( 'watchlistfor2', $user->getName()
			)->rawParams( SpecialEditWatchlist::buildTools( null ) ) );

		$request = $this->getRequest();

		$mode = SpecialEditWatchlist::getMode( $request, $par );
		if ( $mode !== false ) {
			# TODO: localise?
			switch ( $mode ) {
				case SpecialEditWatchlist::EDIT_CLEAR:
					$mode = 'clear';
					break;
				case SpecialEditWatchlist::EDIT_RAW:
					$mode = 'raw';
					break;
				default:
					$mode = null;
			}
			$title = SpecialPage::getTitleFor( 'EditWatchlist', $mode );
			$output->redirect( $title->getLocalURL() );
			return;
		}

		$dbr = wfGetDB( DB_SLAVE, 'watchlist' );

		$nitems = $this->countItems( $dbr );
		if ( $nitems == 0 ) {
			$output->addWikiMsg( 'nowatchlist' );
			return;
		}

		// @todo use FormOptions!
		$defaults = array(
		/* float */ 'days' => floatval( $user->getOption( 'watchlistdays' ) ),
		/* bool  */ 'hideMinor' => (int)$user->getBoolOption( 'watchlisthideminor' ),
		/* bool  */ 'hideBots' => (int)$user->getBoolOption( 'watchlisthidebots' ),
		/* bool  */ 'hideAnons' => (int)$user->getBoolOption( 'watchlisthideanons' ),
		/* bool  */ 'hideLiu' => (int)$user->getBoolOption( 'watchlisthideliu' ),
		/* bool  */ 'hidePatrolled' => (int)$user->getBoolOption( 'watchlisthidepatrolled' ),
		/* bool  */ 'hideOwn' => (int)$user->getBoolOption( 'watchlisthideown' ),
		/* bool  */ 'extended' => (int)$user->getBoolOption( 'extendwatchlist' ),
		/* ?     */ 'namespace' => '', //means all
		/* ?     */ 'invert' => false,
		/* bool  */ 'associated' => false,
		);
		$this->customFilters = array();
		wfRunHooks( 'SpecialWatchlistFilters', array( $this, &$this->customFilters ) );
		foreach ( $this->customFilters as $key => $params ) {
			$defaults[$key] = $params['default'];
		}

		# Extract variables from the request, falling back to user preferences or
		# other default values if these don't exist
		$values = array();
		$values['days'] = floatval( $request->getVal( 'days', $defaults['days'] ) );
		$values['hideMinor'] = (int)$request->getBool( 'hideMinor', $defaults['hideMinor'] );
		$values['hideBots'] = (int)$request->getBool( 'hideBots', $defaults['hideBots'] );
		$values['hideAnons'] = (int)$request->getBool( 'hideAnons', $defaults['hideAnons'] );
		$values['hideLiu'] = (int)$request->getBool( 'hideLiu', $defaults['hideLiu'] );
		$values['hideOwn'] = (int)$request->getBool( 'hideOwn', $defaults['hideOwn'] );
		$values['hidePatrolled'] = (int)$request->getBool( 'hidePatrolled', $defaults['hidePatrolled'] );
		$values['extended'] = (int)$request->getBool( 'extended', $defaults['extended'] );
		foreach ( $this->customFilters as $key => $params ) {
			$values[$key] = (int)$request->getBool( $key, $defaults[$key] );
		}

		# Get namespace value, if supplied, and prepare a WHERE fragment
		$nameSpace = $request->getIntOrNull( 'namespace' );
		$invert = $request->getBool( 'invert' );
		$associated = $request->getBool( 'associated' );
		if ( !is_null( $nameSpace ) ) {
			$eq_op = $invert ? '!=' : '=';
			$bool_op = $invert ? 'AND' : 'OR';
			$nameSpace = intval( $nameSpace ); // paranioa
			if ( !$associated ) {
				$nameSpaceClause = "rc_namespace $eq_op $nameSpace";
			} else {
				$associatedNS = MWNamespace::getAssociated( $nameSpace );
				$nameSpaceClause =
					"rc_namespace $eq_op $nameSpace " .
					$bool_op .
					" rc_namespace $eq_op $associatedNS";
			}
		} else {
			$nameSpace = '';
			$nameSpaceClause = '';
		}
		$values['namespace'] = $nameSpace;
		$values['invert'] = $invert;
		$values['associated'] = $associated;

		// Dump everything here
		$nondefaults = array();
		foreach ( $defaults as $name => $defValue ) {
			wfAppendToArrayIfNotDefault( $name, $values[$name], $defaults, $nondefaults );
		}

		if ( ( $wgEnotifWatchlist || $wgShowUpdatedMarker ) && $request->getVal( 'reset' ) &&
			$request->wasPosted() )
		{
			$user->clearAllNotifications();
			$output->redirect( $this->getTitle()->getFullURL( $nondefaults ) );
			return;
		}

		# Possible where conditions
		$conds = array();

		if ( $values['days'] > 0 ) {
			$conds[] = 'rc_timestamp > ' . $dbr->addQuotes( $dbr->timestamp( time() - intval( $values['days'] * 86400 ) ) );
		}

		# Toggles
		if ( $values['hideOwn'] ) {
			$conds[] = 'rc_user != ' . $user->getId();
		}
		if ( $values['hideBots'] ) {
			$conds[] = 'rc_bot = 0';
		}
		if ( $values['hideMinor'] ) {
			$conds[] = 'rc_minor = 0';
		}
		if ( $values['hideLiu'] ) {
			$conds[] = 'rc_user = 0';
		}
		if ( $values['hideAnons'] ) {
			$conds[] = 'rc_user != 0';
		}
		if ( $user->useRCPatrol() && $values['hidePatrolled'] ) {
			$conds[] = 'rc_patrolled != 1';
		}
		if ( $nameSpaceClause ) {
			$conds[] = $nameSpaceClause;
		}

		# Toggle watchlist content (all recent edits or just the latest)
		if ( $values['extended'] ) {
			$limitWatchlist = $user->getIntOption( 'wllimit' );
			$usePage = false;
		} else {
			# Top log Ids for a page are not stored
			$nonRevisionTypes = array( RC_LOG );
			wfRunHooks( 'SpecialWatchlistGetNonRevisionTypes', array( &$nonRevisionTypes ) );
			if ( $nonRevisionTypes ) {
				if ( count( $nonRevisionTypes ) === 1 ) {
					// if only one use an equality instead of IN condition
					$nonRevisionTypes = reset( $nonRevisionTypes );
				}
				$conds[] = $dbr->makeList(
					array(
						'rc_this_oldid=page_latest',
						'rc_type' => $nonRevisionTypes,
					),
					LIST_OR
				);
			}
			$limitWatchlist = 0;
			$usePage = true;
		}

		# Show a message about slave lag, if applicable
		$lag = wfGetLB()->safeGetLag( $dbr );
		if ( $lag > 0 ) {
			$output->showLagWarning( $lag );
		}

		# Create output
		$form = '';

		# Show watchlist header
		$form .= "<p>";
		$form .= $this->msg( 'watchlist-details' )->numParams( $nitems )->parse() . "\n";
		if ( $wgEnotifWatchlist && $user->getOption( 'enotifwatchlistpages' ) ) {
			$form .= $this->msg( 'wlheader-enotif' )->parse() . "\n";
		}
		if ( $wgShowUpdatedMarker ) {
			$form .= $this->msg( 'wlheader-showupdated' )->parse() . "\n";
		}
		$form .= "</p>";

		if ( $wgShowUpdatedMarker ) {
			$form .= Xml::openElement( 'form', array( 'method' => 'post',
				'action' => $this->getTitle()->getLocalURL(),
				'id' => 'mw-watchlist-resetbutton' ) ) . "\n" .
			Xml::submitButton( $this->msg( 'enotif_reset' )->text(), array( 'name' => 'dummy' ) ) . "\n" .
			Html::hidden( 'reset', 'all' ) . "\n";
			foreach ( $nondefaults as $key => $value ) {
				$form .= Html::hidden( $key, $value ) . "\n";
			}
			$form .= Xml::closeElement( 'form' ) . "\n";
		}

		$form .= Xml::openElement( 'form', array(
			'method' => 'post',
			'action' => $this->getTitle()->getLocalURL(),
			'id' => 'mw-watchlist-form'
		) );
		$form .= Xml::fieldset(
			$this->msg( 'watchlist-options' )->text(),
			false,
			array( 'id' => 'mw-watchlist-options' )
		);

		$tables = array( 'recentchanges', 'watchlist' );
		$fields = RecentChange::selectFields();
		$join_conds = array(
			'watchlist' => array(
				'INNER JOIN',
				array(
					'wl_user' => $user->getId(),
					'wl_namespace=rc_namespace',
					'wl_title=rc_title'
				),
			),
		);
		$options = array( 'ORDER BY' => 'rc_timestamp DESC' );
		if ( $wgShowUpdatedMarker ) {
			$fields[] = 'wl_notificationtimestamp';
		}
		if ( $limitWatchlist ) {
			$options['LIMIT'] = $limitWatchlist;
		}

		$rollbacker = $user->isAllowed( 'rollback' );
		if ( $usePage || $rollbacker ) {
			$tables[] = 'page';
			$join_conds['page'] = array( 'LEFT JOIN', 'rc_cur_id=page_id' );
			if ( $rollbacker ) {
				$fields[] = 'page_latest';
			}
		}

		// Log entries with DELETED_ACTION must not show up unless the user has
		// the necessary rights.
		if ( !$user->isAllowed( 'deletedhistory' ) ) {
			$bitmask = LogPage::DELETED_ACTION;
		} elseif ( !$user->isAllowed( 'suppressrevision' ) ) {
			$bitmask = LogPage::DELETED_ACTION | LogPage::DELETED_RESTRICTED;
		} else {
			$bitmask = 0;
		}
		if ( $bitmask ) {
			$conds[] = $dbr->makeList( array(
				'rc_type != ' . RC_LOG,
				$dbr->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask",
			), LIST_OR );
		}

		ChangeTags::modifyDisplayQuery( $tables, $fields, $conds, $join_conds, $options, '' );
		wfRunHooks( 'SpecialWatchlistQuery', array( &$conds, &$tables, &$join_conds, &$fields, $values ) );

		$res = $dbr->select( $tables, $fields, $conds, __METHOD__, $options, $join_conds );
		$numRows = $res->numRows();

		/* Start bottom header */

		$lang = $this->getLanguage();
		$wlInfo = '';
		if ( $values['days'] > 0 ) {
			$timestamp = wfTimestampNow();
			$wlInfo = $this->msg( 'wlnote' )->numParams( $numRows, round( $values['days'] * 24 ) )->params(
				$lang->userDate( $timestamp, $user ), $lang->userTime( $timestamp, $user ) )->parse() . "<br />\n";
		}

		$cutofflinks = $this->cutoffLinks( $values['days'], $nondefaults ) . "<br />\n";

		# Spit out some control panel links
		$filters = array(
			'hideMinor' => 'rcshowhideminor',
			'hideBots' => 'rcshowhidebots',
			'hideAnons' => 'rcshowhideanons',
			'hideLiu' => 'rcshowhideliu',
			'hideOwn' => 'rcshowhidemine',
			'hidePatrolled' => 'rcshowhidepatr'
		);
		foreach ( $this->customFilters as $key => $params ) {
			$filters[$key] = $params['msg'];
		}
		// Disable some if needed
		if ( !$user->useNPPatrol() ) {
			unset( $filters['hidePatrolled'] );
		}

		$links = array();
		foreach ( $filters as $name => $msg ) {
			$links[] = $this->showHideLink( $nondefaults, $msg, $name, $values[$name] );
		}

		$hiddenFields = $nondefaults;
		unset( $hiddenFields['namespace'] );
		unset( $hiddenFields['invert'] );
		unset( $hiddenFields['associated'] );

		# Namespace filter and put the whole form together.
		$form .= $wlInfo;
		$form .= $cutofflinks;
		$form .= $lang->pipeList( $links ) . "\n";
		$form .= "<hr />\n<p>";
		$form .= Html::namespaceSelector(
			array(
				'selected' => $nameSpace,
				'all' => '',
				'label' => $this->msg( 'namespace' )->text()
			), array(
				'name' => 'namespace',
				'id' => 'namespace',
				'class' => 'namespaceselector',
			)
		) . '&#160;';
		$form .= Xml::checkLabel(
			$this->msg( 'invert' )->text(),
			'invert',
			'nsinvert',
			$invert,
			array( 'title' => $this->msg( 'tooltip-invert' )->text() )
		) . '&#160;';
		$form .= Xml::checkLabel(
			$this->msg( 'namespace_association' )->text(),
			'associated',
			'associated',
			$associated,
			array( 'title' => $this->msg( 'tooltip-namespace_association' )->text() )
		) . '&#160;';
		$form .= Xml::submitButton( $this->msg( 'allpagessubmit' )->text() ) . "</p>\n";
		foreach ( $hiddenFields as $key => $value ) {
			$form .= Html::hidden( $key, $value ) . "\n";
		}
		$form .= Xml::closeElement( 'fieldset' ) . "\n";
		$form .= Xml::closeElement( 'form' ) . "\n";
		$output->addHTML( $form );

		# If there's nothing to show, stop here
		if ( $numRows == 0 ) {
			$output->wrapWikiMsg(
				"<div class='mw-changeslist-empty'>\n$1\n</div>", 'recentchanges-noresult'
			);
			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 = ChangesList::newFromContext( $this->getContext() );
		$list->setWatchlistDivs();

		$s = $list->beginRecentChangesList();
		$counter = 1;
		foreach ( $res as $obj ) {
			# Make RC entry
			$rc = RecentChange::newFromRow( $obj );
			$rc->counter = $counter++;

			if ( $wgShowUpdatedMarker ) {
				$updated = $obj->wl_notificationtimestamp;
			} else {
				$updated = false;
			}

			if ( $wgRCShowWatchingUsers && $user->getOption( 'shownumberswatching' ) ) {
				$rc->numberofWatchingusers = $dbr->selectField( 'watchlist',
					'COUNT(*)',
					array(
						'wl_namespace' => $obj->rc_namespace,
						'wl_title' => $obj->rc_title,
					),
					__METHOD__ );
			} else {
				$rc->numberofWatchingusers = 0;
			}

			$changeLine = $list->recentChangesLine( $rc, $updated, $counter );
			if ( $changeLine !== false ) {
				$s .= $changeLine;
			}
		}
		$s .= $list->endRecentChangesList();

		$output->addHTML( $s );
	}
/**
 * Constructor
 *
 * @param $par Parameter passed to the page
 */
function wfSpecialWatchlist($par)
{
    global $wgUser, $wgOut, $wgLang, $wgRequest;
    global $wgRCShowWatchingUsers, $wgEnotifWatchlist, $wgShowUpdatedMarker;
    global $wgEnotifWatchlist;
    $skin = $wgUser->getSkin();
    $specialTitle = SpecialPage::getTitleFor('Watchlist');
    $wgOut->setRobotPolicy('noindex,nofollow');
    # Anons don't get a watchlist
    if ($wgUser->isAnon()) {
        $wgOut->setPageTitle(wfMsg('watchnologin'));
        $llink = $skin->makeKnownLinkObj(SpecialPage::getTitleFor('Userlogin'), wfMsgHtml('loginreqlink'), 'returnto=' . $specialTitle->getPrefixedUrl());
        $wgOut->addHTML(wfMsgWikiHtml('watchlistanontext', $llink));
        return;
    }
    $wgOut->setPageTitle(wfMsg('watchlist'));
    $sub = wfMsgExt('watchlistfor', 'parseinline', $wgUser->getName());
    $sub .= '<br />' . WatchlistEditor::buildTools($wgUser->getSkin());
    $wgOut->setSubtitle($sub);
    if (($mode = WatchlistEditor::getMode($wgRequest, $par)) !== false) {
        $editor = new WatchlistEditor();
        $editor->execute($wgUser, $wgOut, $wgRequest, $mode);
        return;
    }
    $uid = $wgUser->getId();
    if (($wgEnotifWatchlist || $wgShowUpdatedMarker) && $wgRequest->getVal('reset') && $wgRequest->wasPosted()) {
        $wgUser->clearAllNotifications($uid);
        $wgOut->redirect($specialTitle->getFullUrl());
        return;
    }
    $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'), 'hidePatrolled' => (int) $wgUser->getBoolOption('watchlisthidepatrolled'), 'hideOwn' => (int) $wgUser->getBoolOption('watchlisthideown'), 'namespace' => 'all', 'invert' => false);
    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['hidepatrolled'] = $wgUser->getBoolOption('watchlisthidepatrolled');
    # 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']);
    $hidePatrolled = $wgRequest->getBool('hidePatrolled', $prefs['hidepatrolled']);
    # Get namespace value, if supplied, and prepare a WHERE fragment
    $nameSpace = $wgRequest->getIntOrNull('namespace');
    $invert = $wgRequest->getIntOrNull('invert');
    if (!is_null($nameSpace)) {
        $nameSpace = intval($nameSpace);
        if ($invert && $nameSpace !== 'all') {
            $nameSpaceClause = "rc_namespace != {$nameSpace}";
        } else {
            $nameSpaceClause = "rc_namespace = {$nameSpace}";
        }
    } else {
        $nameSpace = '';
        $nameSpaceClause = '';
    }
    $dbr = wfGetDB(DB_SLAVE, 'watchlist');
    list($page, $watchlist, $recentchanges) = $dbr->tableNamesN('page', 'watchlist', 'recentchanges');
    $watchlistCount = $dbr->selectField('watchlist', 'COUNT(*)', array('wl_user' => $uid), __METHOD__);
    // Adjust for page X, talk:page X, which are both stored separately,
    // but treated together
    $nitems = floor($watchlistCount / 2);
    if (is_null($days) || !is_numeric($days)) {
        $big = 1000;
        /* The magical big */
        if ($nitems > $big) {
            # Set default cutoff shorter
            $days = $defaults['days'] = 12.0 / 24.0;
            # 12 hours...
        } else {
            $days = $defaults['days'];
            # default cutoff for shortlisters
        }
    } else {
        $days = floatval($days);
    }
    // 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('namespace', $nameSpace, $defaults, $nondefaults);
    wfAppendToArrayIfNotDefault('hidePatrolled', (int) $hidePatrolled, $defaults, $nondefaults);
    if ($nitems == 0) {
        $wgOut->addWikiMsg('nowatchlist');
        return;
    }
    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" : '';
    $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'));
    } else {
        # Top log Ids for a page are not stored
        $andLatest = 'rc_this_oldid=page_latest OR rc_type=' . RC_LOG;
        $limitWatchlist = 0;
    }
    # Show a message about slave lag, if applicable
    if (($lag = $dbr->getLag()) > 0) {
        $wgOut->showLagWarning($lag);
    }
    # Create output form
    $form = Xml::fieldset(wfMsg('watchlist-options'), false, array('id' => 'mw-watchlist-options'));
    # Show watchlist header
    $form .= wfMsgExt('watchlist-details', array('parseinline'), $wgLang->formatNum($nitems));
    if ($wgUser->getOption('enotifwatchlistpages') && $wgEnotifWatchlist) {
        $form .= wfMsgExt('wlheader-enotif', 'parse') . "\n";
    }
    if ($wgShowUpdatedMarker) {
        $form .= Xml::openElement('form', array('method' => 'post', 'action' => $specialTitle->getLocalUrl(), 'id' => 'mw-watchlist-resetbutton')) . wfMsgExt('wlheader-showupdated', array('parseinline')) . ' ' . Xml::submitButton(wfMsg('enotif_reset'), array('name' => 'dummy')) . Xml::hidden('reset', 'all') . Xml::closeElement('form');
    }
    $form .= '<hr />';
    $tables = array('recentchanges', 'watchlist', 'page');
    $fields = array("{$recentchanges}.*");
    $conds = array();
    $join_conds = array('watchlist' => array('INNER JOIN', "wl_user='******' AND wl_namespace=rc_namespace AND wl_title=rc_title"), 'page' => array('LEFT JOIN', 'rc_cur_id=page_id'));
    $options = array('ORDER BY' => 'rc_timestamp DESC');
    if ($wgShowUpdatedMarker) {
        $fields[] = 'wl_notificationtimestamp';
    }
    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 ($andHidePatrolled) {
        $conds[] = $andHidePatrolled;
    }
    if ($nameSpaceClause) {
        $conds[] = $nameSpaceClause;
    }
    wfRunHooks('SpecialWatchlistQuery', 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" . wlCutoffLinks($days, 'Watchlist', $nondefaults) . "<br />\n";
    # Spit out some control panel links
    $thisTitle = SpecialPage::getTitleFor('Watchlist');
    $skin = $wgUser->getSkin();
    $showLinktext = wfMsgHtml('show');
    $hideLinktext = wfMsgHtml('hide');
    # Hide/show minor edits
    $label = $hideMinor ? $showLinktext : $hideLinktext;
    $linkBits = wfArrayToCGI(array('hideMinor' => 1 - (int) $hideMinor), $nondefaults);
    $links[] = wfMsgHtml('rcshowhideminor', $skin->makeKnownLinkObj($thisTitle, $label, $linkBits));
    # Hide/show bot edits
    $label = $hideBots ? $showLinktext : $hideLinktext;
    $linkBits = wfArrayToCGI(array('hideBots' => 1 - (int) $hideBots), $nondefaults);
    $links[] = wfMsgHtml('rcshowhidebots', $skin->makeKnownLinkObj($thisTitle, $label, $linkBits));
    # Hide/show anonymous edits
    $label = $hideAnons ? $showLinktext : $hideLinktext;
    $linkBits = wfArrayToCGI(array('hideAnons' => 1 - (int) $hideAnons), $nondefaults);
    $links[] = wfMsgHtml('rcshowhideanons', $skin->makeKnownLinkObj($thisTitle, $label, $linkBits));
    # Hide/show logged in edits
    $label = $hideLiu ? $showLinktext : $hideLinktext;
    $linkBits = wfArrayToCGI(array('hideLiu' => 1 - (int) $hideLiu), $nondefaults);
    $links[] = wfMsgHtml('rcshowhideliu', $skin->makeKnownLinkObj($thisTitle, $label, $linkBits));
    # Hide/show own edits
    $label = $hideOwn ? $showLinktext : $hideLinktext;
    $linkBits = wfArrayToCGI(array('hideOwn' => 1 - (int) $hideOwn), $nondefaults);
    $links[] = wfMsgHtml('rcshowhidemine', $skin->makeKnownLinkObj($thisTitle, $label, $linkBits));
    # Hide/show patrolled edits
    if ($wgUser->useRCPatrol()) {
        $label = $hidePatrolled ? $showLinktext : $hideLinktext;
        $linkBits = wfArrayToCGI(array('hidePatrolled' => 1 - (int) $hidePatrolled), $nondefaults);
        $links[] = wfMsgHtml('rcshowhidepatr', $skin->makeKnownLinkObj($thisTitle, $label, $linkBits));
    }
    # Namespace filter and put the whole form together.
    $form .= $wlInfo;
    $form .= $cutofflinks;
    $form .= implode(' | ', $links);
    $form .= Xml::openElement('form', array('method' => 'post', 'action' => $thisTitle->getLocalUrl()));
    $form .= '<hr /><p>';
    $form .= Xml::label(wfMsg('namespace'), 'namespace') . '&nbsp;';
    $form .= Xml::namespaceSelector($nameSpace, '') . '&nbsp;';
    $form .= Xml::checkLabel(wfMsg('invert'), 'invert', 'nsinvert', $invert) . '&nbsp;';
    $form .= Xml::submitButton(wfMsg('allpagessubmit')) . '</p>';
    $form .= Xml::hidden('days', $days);
    if ($hideMinor) {
        $form .= Xml::hidden('hideMinor', 1);
    }
    if ($hideBots) {
        $form .= Xml::hidden('hideBots', 1);
    }
    if ($hideAnons) {
        $form .= Xml::hidden('hideAnons', 1);
    }
    if ($hideLiu) {
        $form .= Xml::hidden('hideLiu', 1);
    }
    if ($hideOwn) {
        $form .= Xml::hidden('hideOwn', 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();
    while ($row = $dbr->fetchObject($res)) {
        $userNameUnderscored = str_replace(' ', '_', $row->rc_user_text);
        if ($row->rc_user != 0) {
            $linkBatch->add(NS_USER, $userNameUnderscored);
        }
        $linkBatch->add(NS_USER_TALK, $userNameUnderscored);
    }
    $linkBatch->execute();
    $dbr->dataSeek($res, 0);
    $list = ChangesList::newFromUser($wgUser);
    $s = $list->beginRecentChangesList();
    $counter = 1;
    while ($obj = $dbr->fetchObject($res)) {
        # Make RC entry
        $rc = RecentChange::newFromRow($obj);
        $rc->counter = $counter++;
        if ($wgShowUpdatedMarker) {
            $updated = $obj->wl_notificationtimestamp;
        } else {
            $updated = false;
        }
        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;
        }
        $s .= $list->recentChangesLine($rc, $updated);
    }
    $s .= $list->endRecentChangesList();
    $dbr->freeResult($res);
    $wgOut->addHTML($s);
}
/**
 * Constructor
 *
 * @param $par Parameter passed to the page
 */
function wfSpecialWatchlist($par)
{
    global $wgUser, $wgOut, $wgLang, $wgRequest;
    global $wgRCShowWatchingUsers, $wgEnotifWatchlist, $wgShowUpdatedMarker;
    global $wgEnotifWatchlist;
    $fname = 'wfSpecialWatchlist';
    $skin = $wgUser->getSkin();
    $specialTitle = SpecialPage::getTitleFor('Watchlist');
    $wgOut->setRobotPolicy('noindex,nofollow');
    # Anons don't get a watchlist
    if ($wgUser->isAnon()) {
        $wgOut->setPageTitle(wfMsg('watchnologin'));
        $llink = $skin->makeKnownLinkObj(SpecialPage::getTitleFor('Userlogin'), wfMsgHtml('loginreqlink'), 'returnto=' . $specialTitle->getPrefixedUrl());
        $wgOut->addHtml(wfMsgWikiHtml('watchlistanontext', $llink));
        return;
    }
    $wgOut->setPageTitle(wfMsg('watchlist'));
    $sub = wfMsgExt('watchlistfor', 'parseinline', $wgUser->getName());
    $sub .= '<br />' . WatchlistEditor::buildTools($wgUser->getSkin());
    $wgOut->setSubtitle($sub);
    if (($mode = WatchlistEditor::getMode($wgRequest, $par)) !== false) {
        $editor = new WatchlistEditor();
        $editor->execute($wgUser, $wgOut, $wgRequest, $mode);
        return;
    }
    $uid = $wgUser->getId();
    if ($wgEnotifWatchlist && $wgRequest->getVal('reset') && $wgRequest->wasPosted()) {
        $wgUser->clearAllNotifications($uid);
        $wgOut->redirect($specialTitle->getFullUrl());
        return;
    }
    $defaults = array('days' => floatval($wgUser->getOption('watchlistdays')), 'hideOwn' => (int) $wgUser->getBoolOption('watchlisthideown'), 'hideBots' => (int) $wgUser->getBoolOption('watchlisthidebots'), 'hideMinor' => (int) $wgUser->getBoolOption('watchlisthideminor'), 'namespace' => 'all');
    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['hideown'] = $wgUser->getBoolOption('watchlisthideown');
    $prefs['hidebots'] = $wgUser->getBoolOption('watchlisthidebots');
    $prefs['hideminor'] = $wgUser->getBoolOption('watchlisthideminor');
    # Get query variables
    $days = $wgRequest->getVal('days', $prefs['days']);
    $hideOwn = $wgRequest->getBool('hideOwn', $prefs['hideown']);
    $hideBots = $wgRequest->getBool('hideBots', $prefs['hidebots']);
    $hideMinor = $wgRequest->getBool('hideMinor', $prefs['hideminor']);
    # Get namespace value, if supplied, and prepare a WHERE fragment
    $nameSpace = $wgRequest->getIntOrNull('namespace');
    if (!is_null($nameSpace)) {
        $nameSpace = intval($nameSpace);
        $nameSpaceClause = " AND rc_namespace = {$nameSpace}";
    } else {
        $nameSpace = '';
        $nameSpaceClause = '';
    }
    $dbr = wfGetDB(DB_SLAVE, 'watchlist');
    list($page, $watchlist, $recentchanges) = $dbr->tableNamesN('page', 'watchlist', 'recentchanges');
    $sql = "SELECT COUNT(*) AS n FROM {$watchlist} WHERE wl_user={$uid}";
    $res = $dbr->query($sql, $fname);
    $s = $dbr->fetchObject($res);
    #	Patch *** A1 *** (see A2 below)
    #	adjust for page X, talk:page X, which are both stored separately, but treated together
    $nitems = floor($s->n / 2);
    #	$nitems = $s->n;
    if ($nitems == 0) {
        $wgOut->addWikiText(wfMsg('nowatchlist'));
        return;
    }
    if (is_null($days) || !is_numeric($days)) {
        $big = 1000;
        /* The magical big */
        if ($nitems > $big) {
            # Set default cutoff shorter
            $days = $defaults['days'] = 12.0 / 24.0;
            # 12 hours...
        } else {
            $days = $defaults['days'];
            # default cutoff for shortlisters
        }
    } else {
        $days = floatval($days);
    }
    // Dump everything here
    $nondefaults = array();
    wfAppendToArrayIfNotDefault('days', $days, $defaults, $nondefaults);
    wfAppendToArrayIfNotDefault('hideOwn', (int) $hideOwn, $defaults, $nondefaults);
    wfAppendToArrayIfNotDefault('hideBots', (int) $hideBots, $defaults, $nondefaults);
    wfAppendToArrayIfNotDefault('hideMinor', (int) $hideMinor, $defaults, $nondefaults);
    wfAppendToArrayIfNotDefault('namespace', $nameSpace, $defaults, $nondefaults);
    if ($days <= 0) {
        $andcutoff = '';
    } else {
        $andcutoff = "AND rc_timestamp > '" . $dbr->timestamp(time() - intval($days * 86400)) . "'";
        /*
        $sql = "SELECT COUNT(*) AS n FROM $page, $revision  WHERE rev_timestamp>'$cutoff' AND page_id=rev_page";
        $res = $dbr->query( $sql, $fname );
        $s = $dbr->fetchObject( $res );
        $npages = $s->n;
        */
    }
    # 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 ? "AND (rc_user <> {$uid})" : '';
    $andHideBots = $hideBots ? "AND (rc_bot = 0)" : '';
    $andHideMinor = $hideMinor ? 'AND rc_minor = 0' : '';
    # Show watchlist header
    $header = '';
    if ($wgUser->getOption('enotifwatchlistpages') && $wgEnotifWatchlist) {
        $header .= wfMsg('wlheader-enotif') . "\n";
    }
    if ($wgEnotifWatchlist && $wgShowUpdatedMarker) {
        $header .= wfMsg('wlheader-showupdated') . "\n";
    }
    # Toggle watchlist content (all recent edits or just the latest)
    if ($wgUser->getOption('extendwatchlist')) {
        $andLatest = '';
        $limitWatchlist = 'LIMIT ' . intval($wgUser->getOption('wllimit'));
    } else {
        $andLatest = 'AND rc_this_oldid=page_latest';
        $limitWatchlist = '';
    }
    $header .= wfMsgExt('watchlist-details', array('parsemag'), $wgLang->formatNum($nitems));
    $wgOut->addWikiText($header);
    # Show a message about slave lag, if applicable
    if (($lag = $dbr->getLag()) > 0) {
        $wgOut->showLagWarning($lag);
    }
    if ($wgEnotifWatchlist && $wgShowUpdatedMarker) {
        $wgOut->addHTML('<form action="' . $specialTitle->escapeLocalUrl() . '" method="post"><input type="submit" name="dummy" value="' . htmlspecialchars(wfMsg('enotif_reset')) . '" /><input type="hidden" name="reset" value="all" /></form>' . "\n\n");
    }
    $sql = "SELECT *\r\n\t  FROM {$watchlist},{$recentchanges},{$page}\r\n\t  WHERE wl_user={$uid}\r\n\t  AND wl_namespace=rc_namespace\r\n\t  AND wl_title=rc_title\r\n\t  AND rc_cur_id=page_id\r\n\t  {$andcutoff}\r\n\t  {$andLatest}\r\n\t  {$andHideOwn}\r\n\t  {$andHideBots}\r\n\t  {$andHideMinor}\r\n\t  {$nameSpaceClause}\r\n\t  ORDER BY rc_timestamp DESC\r\n\t  {$limitWatchlist}";
    $res = $dbr->query($sql, $fname);
    $numRows = $dbr->numRows($res);
    /* Start bottom header */
    $wgOut->addHTML("<hr />\n");
    if ($days >= 1) {
        $wgOut->addWikiText(wfMsgExt('rcnote', array('parseinline'), $wgLang->formatNum($numRows), $wgLang->formatNum($days), $wgLang->timeAndDate(wfTimestampNow(), true)) . '<br />', false);
    } elseif ($days > 0) {
        $wgOut->addWikiText(wfMsgExt('wlnote', array('parseinline'), $wgLang->formatNum($numRows), $wgLang->formatNum(round($days * 24))) . '<br />', false);
    }
    $wgOut->addHTML("\n" . wlCutoffLinks($days, 'Watchlist', $nondefaults) . "<br />\n");
    # Spit out some control panel links
    $thisTitle = SpecialPage::getTitleFor('Watchlist');
    $skin = $wgUser->getSkin();
    # Hide/show bot edits
    $label = $hideBots ? wfMsgHtml('watchlist-show-bots') : wfMsgHtml('watchlist-hide-bots');
    $linkBits = wfArrayToCGI(array('hideBots' => 1 - (int) $hideBots), $nondefaults);
    $links[] = $skin->makeKnownLinkObj($thisTitle, $label, $linkBits);
    # Hide/show own edits
    $label = $hideOwn ? wfMsgHtml('watchlist-show-own') : wfMsgHtml('watchlist-hide-own');
    $linkBits = wfArrayToCGI(array('hideOwn' => 1 - (int) $hideOwn), $nondefaults);
    $links[] = $skin->makeKnownLinkObj($thisTitle, $label, $linkBits);
    # Hide/show minor edits
    $label = $hideMinor ? wfMsgHtml('watchlist-show-minor') : wfMsgHtml('watchlist-hide-minor');
    $linkBits = wfArrayToCGI(array('hideMinor' => 1 - (int) $hideMinor), $nondefaults);
    $links[] = $skin->makeKnownLinkObj($thisTitle, $label, $linkBits);
    $wgOut->addHTML(implode(' | ', $links));
    # Form for namespace filtering
    $form = Xml::openElement('form', array('method' => 'post', 'action' => $thisTitle->getLocalUrl()));
    $form .= '<p>';
    $form .= Xml::label(wfMsg('namespace'), 'namespace') . '&nbsp;';
    $form .= Xml::namespaceSelector($nameSpace, '') . '&nbsp;';
    $form .= Xml::submitButton(wfMsg('allpagessubmit')) . '</p>';
    $form .= Xml::hidden('days', $days);
    if ($hideOwn) {
        $form .= Xml::hidden('hideOwn', 1);
    }
    if ($hideBots) {
        $form .= Xml::hidden('hideBots', 1);
    }
    if ($hideMinor) {
        $form .= Xml::hidden('hideMinor', 1);
    }
    $form .= Xml::closeElement('form');
    $wgOut->addHtml($form);
    # If there's nothing to show, stop here
    if ($numRows == 0) {
        $wgOut->addWikiText(wfMsgNoTrans('watchnochange'));
        return;
    }
    /* End bottom header */
    /* Do link batch query */
    $linkBatch = new LinkBatch();
    while ($row = $dbr->fetchObject($res)) {
        $userNameUnderscored = str_replace(' ', '_', $row->rc_user_text);
        if ($row->rc_user != 0) {
            $linkBatch->add(NS_USER, $userNameUnderscored);
        }
        $linkBatch->add(NS_USER_TALK, $userNameUnderscored);
    }
    $linkBatch->execute();
    $dbr->dataSeek($res, 0);
    $list = ChangesList::newFromUser($wgUser);
    $s = $list->beginRecentChangesList();
    $counter = 1;
    while ($obj = $dbr->fetchObject($res)) {
        # Make RC entry
        $rc = RecentChange::newFromRow($obj);
        $rc->counter = $counter++;
        if ($wgShowUpdatedMarker) {
            $updated = $obj->wl_notificationtimestamp;
        } else {
            // Same visual appearance as MW 1.4
            $updated = true;
        }
        if ($wgRCShowWatchingUsers && $wgUser->getOption('shownumberswatching')) {
            $sql3 = "SELECT COUNT(*) AS n FROM {$watchlist} WHERE wl_title='" . $dbr->strencode($obj->page_title) . "' AND wl_namespace='{$obj->page_namespace}'";
            $res3 = $dbr->query($sql3, $fname);
            $x = $dbr->fetchObject($res3);
            $rc->numberofWatchingusers = $x->n;
        } else {
            $rc->numberofWatchingusers = 0;
        }
        $s .= $list->recentChangesLine($rc, $updated);
    }
    $s .= $list->endRecentChangesList();
    $dbr->freeResult($res);
    $wgOut->addHTML($s);
}
 /**
  * Execute
  * @param $par Parameter passed to the page
  */
 function execute($par)
 {
     global $wgRCShowWatchingUsers, $wgEnotifWatchlist, $wgShowUpdatedMarker;
     $user = $this->getUser();
     $output = $this->getOutput();
     # Anons don't get a watchlist
     if ($user->isAnon()) {
         $output->setPageTitle($this->msg('watchnologin'));
         $output->setRobotPolicy('noindex,nofollow');
         $llink = Linker::linkKnown(SpecialPage::getTitleFor('Userlogin'), $this->msg('loginreqlink')->escaped(), array(), array('returnto' => $this->getTitle()->getPrefixedText()));
         $output->addHTML($this->msg('watchlistanontext')->rawParams($llink)->parse());
         return;
     }
     // Add feed links
     $wlToken = $user->getOption('watchlisttoken');
     if (!$wlToken) {
         $wlToken = MWCryptRand::generateHex(40);
         $user->setOption('watchlisttoken', $wlToken);
         $user->saveSettings();
     }
     $this->addFeedLinks(array('action' => 'feedwatchlist', 'allrev' => 'allrev', 'wlowner' => $user->getName(), 'wltoken' => $wlToken));
     $this->setHeaders();
     $this->outputHeader();
     $output->addSubtitle($this->msg('watchlistfor2', $user->getName())->rawParams(SpecialEditWatchlist::buildTools(null)));
     $request = $this->getRequest();
     $mode = SpecialEditWatchlist::getMode($request, $par);
     if ($mode !== false) {
         # TODO: localise?
         switch ($mode) {
             case SpecialEditWatchlist::EDIT_CLEAR:
                 $mode = 'clear';
                 break;
             case SpecialEditWatchlist::EDIT_RAW:
                 $mode = 'raw';
                 break;
             default:
                 $mode = null;
         }
         $title = SpecialPage::getTitleFor('EditWatchlist', $mode);
         $output->redirect($title->getLocalUrl());
         return;
     }
     $nitems = $this->countItems();
     if ($nitems == 0) {
         $output->addWikiMsg('nowatchlist');
         return;
     }
     // @TODO: use FormOptions!
     $defaults = array('days' => floatval($user->getOption('watchlistdays')), 'hideMinor' => (int) $user->getBoolOption('watchlisthideminor'), 'hideBots' => (int) $user->getBoolOption('watchlisthidebots'), 'hideAnons' => (int) $user->getBoolOption('watchlisthideanons'), 'hideLiu' => (int) $user->getBoolOption('watchlisthideliu'), 'hidePatrolled' => (int) $user->getBoolOption('watchlisthidepatrolled'), 'hideOwn' => (int) $user->getBoolOption('watchlisthideown'), 'namespace' => 'all', 'invert' => false, 'associated' => false);
     $this->customFilters = array();
     wfRunHooks('SpecialWatchlistFilters', array($this, &$this->customFilters));
     foreach ($this->customFilters as $key => $params) {
         $defaults[$key] = $params['msg'];
     }
     # Extract variables from the request, falling back to user preferences or
     # other default values if these don't exist
     $prefs['days'] = floatval($user->getOption('watchlistdays'));
     $prefs['hideminor'] = $user->getBoolOption('watchlisthideminor');
     $prefs['hidebots'] = $user->getBoolOption('watchlisthidebots');
     $prefs['hideanons'] = $user->getBoolOption('watchlisthideanons');
     $prefs['hideliu'] = $user->getBoolOption('watchlisthideliu');
     $prefs['hideown'] = $user->getBoolOption('watchlisthideown');
     $prefs['hidepatrolled'] = $user->getBoolOption('watchlisthidepatrolled');
     # Get query variables
     $values = array();
     $values['days'] = $request->getVal('days', $prefs['days']);
     $values['hideMinor'] = (int) $request->getBool('hideMinor', $prefs['hideminor']);
     $values['hideBots'] = (int) $request->getBool('hideBots', $prefs['hidebots']);
     $values['hideAnons'] = (int) $request->getBool('hideAnons', $prefs['hideanons']);
     $values['hideLiu'] = (int) $request->getBool('hideLiu', $prefs['hideliu']);
     $values['hideOwn'] = (int) $request->getBool('hideOwn', $prefs['hideown']);
     $values['hidePatrolled'] = (int) $request->getBool('hidePatrolled', $prefs['hidepatrolled']);
     foreach ($this->customFilters as $key => $params) {
         $values[$key] = (int) $request->getBool($key);
     }
     # Get namespace value, if supplied, and prepare a WHERE fragment
     $nameSpace = $request->getIntOrNull('namespace');
     $invert = $request->getBool('invert');
     $associated = $request->getBool('associated');
     if (!is_null($nameSpace)) {
         $eq_op = $invert ? '!=' : '=';
         $bool_op = $invert ? 'AND' : 'OR';
         $nameSpace = intval($nameSpace);
         // paranioa
         if (!$associated) {
             $nameSpaceClause = "rc_namespace {$eq_op} {$nameSpace}";
         } else {
             $associatedNS = MWNamespace::getAssociated($nameSpace);
             $nameSpaceClause = "rc_namespace {$eq_op} {$nameSpace} " . $bool_op . " rc_namespace {$eq_op} {$associatedNS}";
         }
     } else {
         $nameSpace = '';
         $nameSpaceClause = '';
     }
     $values['namespace'] = $nameSpace;
     $values['invert'] = $invert;
     $values['associated'] = $associated;
     if (is_null($values['days']) || !is_numeric($values['days'])) {
         $big = 1000;
         /* The magical big */
         if ($nitems > $big) {
             # Set default cutoff shorter
             $values['days'] = $defaults['days'] = 12.0 / 24.0;
             # 12 hours...
         } else {
             $values['days'] = $defaults['days'];
             # default cutoff for shortlisters
         }
     } else {
         $values['days'] = floatval($values['days']);
     }
     // Dump everything here
     $nondefaults = array();
     foreach ($defaults as $name => $defValue) {
         wfAppendToArrayIfNotDefault($name, $values[$name], $defaults, $nondefaults);
     }
     if (($wgEnotifWatchlist || $wgShowUpdatedMarker) && $request->getVal('reset') && $request->wasPosted()) {
         $user->clearAllNotifications();
         $output->redirect($this->getTitle()->getFullUrl($nondefaults));
         return;
     }
     $dbr = wfGetDB(DB_SLAVE, 'watchlist');
     # Possible where conditions
     $conds = array();
     if ($values['days'] > 0) {
         $conds[] = "rc_timestamp > '" . $dbr->timestamp(time() - intval($values['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
     if ($values['hideOwn']) {
         $conds[] = 'rc_user != ' . $user->getId();
     }
     if ($values['hideBots']) {
         $conds[] = 'rc_bot = 0';
     }
     if ($values['hideMinor']) {
         $conds[] = 'rc_minor = 0';
     }
     if ($values['hideLiu']) {
         $conds[] = 'rc_user = 0';
     }
     if ($values['hideAnons']) {
         $conds[] = 'rc_user != 0';
     }
     if ($user->useRCPatrol() && $values['hidePatrolled']) {
         $conds[] = 'rc_patrolled != 1';
     }
     if ($nameSpaceClause) {
         $conds[] = $nameSpaceClause;
     }
     # Toggle watchlist content (all recent edits or just the latest)
     if ($user->getOption('extendwatchlist')) {
         $limitWatchlist = intval($user->getOption('wllimit'));
         $usePage = false;
     } else {
         # Top log Ids for a page are not stored
         $conds[] = '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) {
         $output->showLagWarning($lag);
     }
     # Create output form
     $form = Xml::fieldset($this->msg('watchlist-options')->text(), false, array('id' => 'mw-watchlist-options'));
     # Show watchlist header
     $form .= $this->msg('watchlist-details')->numParams($nitems)->parse();
     if ($user->getOption('enotifwatchlistpages') && $wgEnotifWatchlist) {
         $form .= $this->msg('wlheader-enotif')->parseAsBlock() . "\n";
     }
     if ($wgShowUpdatedMarker) {
         $form .= Xml::openElement('form', array('method' => 'post', 'action' => $this->getTitle()->getLocalUrl(), 'id' => 'mw-watchlist-resetbutton')) . $this->msg('wlheader-showupdated')->parse() . ' ' . Xml::submitButton($this->msg('enotif_reset')->text(), array('name' => 'dummy')) . Html::hidden('reset', 'all');
         foreach ($nondefaults as $key => $value) {
             $form .= Html::hidden($key, $value);
         }
         $form .= Xml::closeElement('form');
     }
     $form .= '<hr />';
     $tables = array('recentchanges', 'watchlist');
     $fields = RecentChange::selectFields();
     $join_conds = array('watchlist' => array('INNER JOIN', array('wl_user' => $user->getId(), 'wl_namespace=rc_namespace', 'wl_title=rc_title')));
     $options = array('ORDER BY' => 'rc_timestamp DESC');
     if ($wgShowUpdatedMarker) {
         $fields[] = 'wl_notificationtimestamp';
     }
     if ($limitWatchlist) {
         $options['LIMIT'] = $limitWatchlist;
     }
     $rollbacker = $user->isAllowed('rollback');
     if ($usePage || $rollbacker) {
         $tables[] = 'page';
         $join_conds['page'] = array('LEFT JOIN', 'rc_cur_id=page_id');
         if ($rollbacker) {
             $fields[] = 'page_latest';
         }
     }
     ChangeTags::modifyDisplayQuery($tables, $fields, $conds, $join_conds, $options, '');
     wfRunHooks('SpecialWatchlistQuery', array(&$conds, &$tables, &$join_conds, &$fields));
     $res = $dbr->select($tables, $fields, $conds, __METHOD__, $options, $join_conds);
     $numRows = $res->numRows();
     /* Start bottom header */
     $lang = $this->getLanguage();
     $wlInfo = '';
     if ($values['days'] > 0) {
         $timestamp = wfTimestampNow();
         $wlInfo = $this->msg('wlnote')->numParams($numRows, round($values['days'] * 24))->params($lang->userDate($timestamp, $user), $lang->userTime($timestamp, $user))->parse() . '<br />';
     }
     $cutofflinks = "\n" . $this->cutoffLinks($values['days'], $nondefaults) . "<br />\n";
     # Spit out some control panel links
     $filters = array('hideMinor' => 'rcshowhideminor', 'hideBots' => 'rcshowhidebots', 'hideAnons' => 'rcshowhideanons', 'hideLiu' => 'rcshowhideliu', 'hideOwn' => 'rcshowhidemine', 'hidePatrolled' => 'rcshowhidepatr');
     foreach ($this->customFilters as $key => $params) {
         $filters[$key] = $params['msg'];
     }
     // Disable some if needed
     if (!$user->useNPPatrol()) {
         unset($filters['hidePatrolled']);
     }
     $links = array();
     foreach ($filters as $name => $msg) {
         $links[] = $this->showHideLink($nondefaults, $msg, $name, $values[$name]);
     }
     # Namespace filter and put the whole form together.
     $form .= $wlInfo;
     $form .= $cutofflinks;
     $form .= $lang->pipeList($links);
     $form .= Xml::openElement('form', array('method' => 'post', 'action' => $this->getTitle()->getLocalUrl(), 'id' => 'mw-watchlist-form-namespaceselector'));
     $form .= '<hr /><p>';
     $form .= Html::namespaceSelector(array('selected' => $nameSpace, 'all' => '', 'label' => $this->msg('namespace')->text()), array('name' => 'namespace', 'id' => 'namespace', 'class' => 'namespaceselector')) . '&#160;';
     $form .= Xml::checkLabel($this->msg('invert')->text(), 'invert', 'nsinvert', $invert, array('title' => $this->msg('tooltip-invert')->text())) . '&#160;';
     $form .= Xml::checkLabel($this->msg('namespace_association')->text(), 'associated', 'associated', $associated, array('title' => $this->msg('tooltip-namespace_association')->text())) . '&#160;';
     $form .= Xml::submitButton($this->msg('allpagessubmit')->text()) . '</p>';
     $form .= Html::hidden('days', $values['days']);
     foreach ($filters as $key => $msg) {
         if ($values[$key]) {
             $form .= Html::hidden($key, 1);
         }
     }
     $form .= Xml::closeElement('form');
     $form .= Xml::closeElement('fieldset');
     $output->addHTML($form);
     # If there's nothing to show, stop here
     if ($numRows == 0) {
         $output->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 = ChangesList::newFromContext($this->getContext());
     $list->setWatchlistDivs();
     $s = $list->beginRecentChangesList();
     $counter = 1;
     foreach ($res as $obj) {
         # Make RC entry
         $rc = RecentChange::newFromRow($obj);
         $rc->counter = $counter++;
         if ($wgShowUpdatedMarker) {
             $updated = $obj->wl_notificationtimestamp;
         } else {
             $updated = false;
         }
         if ($wgRCShowWatchingUsers && $user->getOption('shownumberswatching')) {
             $rc->numberofWatchingusers = $dbr->selectField('watchlist', 'COUNT(*)', array('wl_namespace' => $obj->rc_namespace, 'wl_title' => $obj->rc_title), __METHOD__);
         } else {
             $rc->numberofWatchingusers = 0;
         }
         $s .= $list->recentChangesLine($rc, $updated, $counter);
     }
     $s .= $list->endRecentChangesList();
     $output->addHTML($s);
 }
 public function buildRssWatch($par)
 {
     // TODO SU (04.07.11 10:35): Globals
     global $wgUser, $wgOut, $wgRequest, $wgRCShowWatchingUsers, $wgEnotifWatchlist, $wgShowUpdatedMarker, $wgEnotifWatchlist, $wgSitename;
     $skin = RequestContext::getMain()->getSkin();
     $specialTitle = SpecialPage::getTitleFor('Watchlist');
     $wgOut->setRobotPolicy('noindex,nofollow');
     # Anons don't get a watchlist
     if ($wgUser->isAnon()) {
         $_user = $wgRequest->getVal('u', '');
         $user = User::newFromName($_user);
         $_hash = $wgRequest->getVal('h', '');
         if (!($user && $_hash == md5($_user . $user->getToken() . $user->getId())) || $user->isAnon()) {
             $oTitle = SpecialPage::getTitleFor('Userlogin');
             $sLink = Linker::link($oTitle, wfMessage('loginreqlink')->plain(), array(), array('returnto' => $specialTitle->getLocalUrl()));
             throw new ErrorPageError('bs-rssstandards-watchnologin', 'watchlistanontext', array($sLink));
         }
     } else {
         $user = $wgUser;
     }
     $wgOut->setPageTitle(wfMessage('bs-rssstandards-watchlist')->plain());
     $sub = wfMessage('watchlistfor', $user->getName())->parse();
     $sub .= '<br />' . WatchlistEditor::buildTools($user->getSkin());
     $wgOut->setSubtitle($sub);
     if (($mode = WatchlistEditor::getMode($wgRequest, $par)) !== false) {
         $editor = new WatchlistEditor();
         $editor->execute($user, $wgOut, $wgRequest, $mode);
         return;
     }
     $uid = $user->getId();
     if (($wgEnotifWatchlist || $wgShowUpdatedMarker) && $wgRequest->getVal('reset') && $wgRequest->wasPosted()) {
         $user->clearAllNotifications($uid);
         $wgOut->redirect($specialTitle->getFullUrl());
         return;
     }
     $defaults = array('days' => floatval($user->getOption('watchlistdays')), 'hideOwn' => (int) $user->getBoolOption('watchlisthideown'), 'hideBots' => (int) $user->getBoolOption('watchlisthidebots'), 'hideMinor' => (int) $user->getBoolOption('watchlisthideminor'), 'namespace' => 'all');
     extract($defaults);
     # Extract variables from the request, falling back to user preferences or
     # other default values if these don't exist
     $prefs['days'] = floatval($user->getOption('watchlistdays'));
     $prefs['hideown'] = $user->getBoolOption('watchlisthideown');
     $prefs['hidebots'] = $user->getBoolOption('watchlisthidebots');
     $prefs['hideminor'] = $user->getBoolOption('watchlisthideminor');
     # Get query variables
     $days = $wgRequest->getVal('days', $prefs['days']);
     $hideOwn = $wgRequest->getBool('hideOwn', $prefs['hideown']);
     $hideBots = $wgRequest->getBool('hideBots', $prefs['hidebots']);
     $hideMinor = $wgRequest->getBool('hideMinor', $prefs['hideminor']);
     # Get namespace value, if supplied, and prepare a WHERE fragment
     $nameSpace = $wgRequest->getIntOrNull('namespace');
     if (!is_null($nameSpace)) {
         $nameSpace = intval($nameSpace);
         $nameSpaceClause = " AND rc_namespace = {$nameSpace}";
     } else {
         $nameSpace = '';
         $nameSpaceClause = '';
     }
     $dbr = wfGetDB(DB_SLAVE, 'watchlist');
     list($page, $watchlist, $recentchanges) = $dbr->tableNamesN('page', 'watchlist', 'recentchanges');
     $watchlistCount = $dbr->selectField('watchlist', 'COUNT(*)', array('wl_user' => $uid), __METHOD__);
     // Adjust for page X, talk:page X, which are both stored separately,
     // but treated together
     $nitems = floor($watchlistCount / 2);
     if (is_null($days) || !is_numeric($days)) {
         $big = 1000;
         /* The magical big */
         if ($nitems > $big) {
             # Set default cutoff shorter
             $days = $defaults['days'] = 12.0 / 24.0;
             # 12 hours...
         } else {
             $days = $defaults['days'];
             # default cutoff for shortlisters
         }
     } else {
         $days = floatval($days);
     }
     // Dump everything here
     $nondefaults = array();
     wfAppendToArrayIfNotDefault('days', $days, $defaults, $nondefaults);
     wfAppendToArrayIfNotDefault('hideOwn', (int) $hideOwn, $defaults, $nondefaults);
     wfAppendToArrayIfNotDefault('hideBots', (int) $hideBots, $defaults, $nondefaults);
     wfAppendToArrayIfNotDefault('hideMinor', (int) $hideMinor, $defaults, $nondefaults);
     wfAppendToArrayIfNotDefault('namespace', $nameSpace, $defaults, $nondefaults);
     $hookSql = "";
     if (!wfRunHooks('BeforeWatchlist', array($nondefaults, $user, &$hookSql))) {
         return;
     }
     /*if ( $nitems == 0 ) {
     			$wgOut->addWikiMsg( 'nowatchlist' );
     			return;
     		}*/
     if ($days <= 0) {
         $andcutoff = '';
     } else {
         $andcutoff = "AND 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 ? "AND (rc_user <> {$uid})" : '';
     $andHideBots = $hideBots ? "AND (rc_bot = 0)" : '';
     $andHideMinor = $hideMinor ? 'AND rc_minor = 0' : '';
     # Toggle watchlist content (all recent edits or just the latest)
     if ($user->getOption('extendwatchlist')) {
         $andLatest = '';
         $limitWatchlist = 'LIMIT ' . intval($user->getOption('wllimit'));
     } else {
         # Top log Ids for a page are not stored
         $andLatest = 'AND (rc_this_oldid=page_latest OR rc_type=' . RC_LOG . ') ';
         $limitWatchlist = '';
     }
     if ($wgShowUpdatedMarker) {
         $wltsfield = ", {$watchlist}.wl_notificationtimestamp ";
     } else {
         $wltsfield = '';
     }
     $sql = "SELECT {$recentchanges}.* {$wltsfield}\n\t  FROM {$watchlist},{$recentchanges}\n\t  LEFT JOIN {$page} ON rc_cur_id=page_id\n\t  WHERE wl_user={$uid}\n\t  AND wl_namespace=rc_namespace\n\t  AND wl_title=rc_title\n\t\t\t{$andcutoff}\n\t\t\t{$andLatest}\n\t\t\t{$andHideOwn}\n\t\t\t{$andHideBots}\n\t\t\t{$andHideMinor}\n\t\t\t{$nameSpaceClause}\n\t\t\t{$hookSql}\n\t  ORDER BY rc_timestamp DESC\n\t\t\t{$limitWatchlist}";
     $res = $dbr->query($sql, __METHOD__);
     $numRows = $dbr->numRows($res);
     /*# If there's nothing to show, stop here
     		if( $numRows == 0 ) {
     			$wgOut->addWikiMsg( 'watchnochange' );
     			return;
     		}*/
     /* End bottom header */
     if ($numRows > 0) {
         /* Do link batch query */
         $linkBatch = new LinkBatch();
         while ($row = $dbr->fetchObject($res)) {
             $userNameUnderscored = str_replace(' ', '_', $row->rc_user_text);
             if ($row->rc_user != 0) {
                 $linkBatch->add(NS_USER, $userNameUnderscored);
             }
             $linkBatch->add(NS_USER_TALK, $userNameUnderscored);
         }
         $linkBatch->execute();
         $dbr->dataSeek($res, 0);
     }
     $list = ChangesList::newFromContext($skin->getContext());
     //Thanks to Bartosz Dziewoński (https://gerrit.wikimedia.org/r/#/c/94082/)
     $channel = RSSCreator::createChannel(SpecialPage::getTitleFor('Watchlist') . ' (' . $user->getName() . ')', 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'], wfMessage('bs-rssstandards-desc-watch')->plain());
     $html = $list->beginRecentChangesList();
     $counter = 1;
     $items = array();
     while ($obj = $dbr->fetchObject($res)) {
         $title = Title::newFromText($obj->rc_title, $obj->rc_namespace);
         $items[] = array('title' => $title->getPrefixedText(), 'link' => $title->getFullURL(), 'date' => wfTimestamp(TS_UNIX, $obj->rc_timestamp), 'comments' => $title->getTalkPage()->getFullURL());
         # Make RC entry
         $rc = RecentChange::newFromRow($obj);
         $rc->counter = $counter++;
         if ($wgShowUpdatedMarker) {
             $updated = $obj->wl_notificationtimestamp;
         } else {
             $updated = false;
         }
         if ($wgRCShowWatchingUsers && $user->getOption('shownumberswatching')) {
             $rc->numberofWatchingusers = $dbr->selectField('watchlist', 'COUNT(*)', array('wl_namespace' => $obj->rc_namespace, 'wl_title' => $obj->rc_title), __METHOD__);
         } else {
             $rc->numberofWatchingusers = 0;
         }
         $rc->mAttribs['rc_timestamp'] = 0;
         $html .= $list->recentChangesLine($rc, false);
     }
     $html .= $list->endRecentChangesList();
     $lines = array();
     preg_match_all('%<li.*?>(.*?)</li>%', $html, $lines, PREG_SET_ORDER);
     foreach ($lines as $key => $line) {
         $item = $items[$key];
         $entry = RSSItemCreator::createItem($item['title'], $item['link'], RSSCreator::xmlEncode($line[1]));
         if ($entry == false) {
             wfDebugLog('BS::RSSStandards::buildRssWatch', 'Invalid item: ' . var_export($item, true));
             continue;
         }
         $entry->setPubDate($item['date']);
         $entry->setComments($item['comments']);
         $channel->addItem($entry);
     }
     $dbr->freeResult($res);
     return $channel->buildOutput();
 }
	/**
	 * Extracts from a single sql row the data needed to describe one recent change.
	 *
	 * @param mixed $row The row from which to extract the data.
	 * @return array An array mapping strings (descriptors) to their respective string values.
	 * @access public
	 */
	public function extractRowInfo( $row ) {
		/* Determine the title of the page that has been changed. */
		$title = Title::makeTitle( $row->rc_namespace, $row->rc_title );

		/* Our output data. */
		$vals = array();

		$type = intval( $row->rc_type );

		/* Determine what kind of change this was. */
		switch ( $type ) {
			case RC_EDIT:
				$vals['type'] = 'edit';
				break;
			case RC_NEW:
				$vals['type'] = 'new';
				break;
			case RC_MOVE:
				$vals['type'] = 'move';
				break;
			case RC_LOG:
				$vals['type'] = 'log';
				break;
			case RC_EXTERNAL:
				$vals['type'] = 'external';
				break;
			case RC_MOVE_OVER_REDIRECT:
				$vals['type'] = 'move over redirect';
				break;
			default:
				$vals['type'] = $type;
		}

		/* Create a new entry in the result for the title. */
		if ( $this->fld_title ) {
			ApiQueryBase::addTitleInfo( $vals, $title );
		}

		/* Add ids, such as rcid, pageid, revid, and oldid to the change's info. */
		if ( $this->fld_ids ) {
			$vals['rcid'] = intval( $row->rc_id );
			$vals['pageid'] = intval( $row->rc_cur_id );
			$vals['revid'] = intval( $row->rc_this_oldid );
			$vals['old_revid'] = intval( $row->rc_last_oldid );
		}

		/* Add user data and 'anon' flag, if use is anonymous. */
		if ( $this->fld_user || $this->fld_userid ) {

			if ( $this->fld_user ) {
				$vals['user'] = $row->rc_user_text;
			}

			if ( $this->fld_userid ) {
				$vals['userid'] = $row->rc_user;
			}

			if ( !$row->rc_user ) {
				$vals['anon'] = '';
			}
		}

		/* Add flags, such as new, minor, bot. */
		if ( $this->fld_flags ) {
			if ( $row->rc_bot ) {
				$vals['bot'] = '';
			}
			if ( $row->rc_type == RC_NEW ) {
				$vals['new'] = '';
			}
			if ( $row->rc_minor ) {
				$vals['minor'] = '';
			}
		}

		/* Add sizes of each revision. (Only available on 1.10+) */
		if ( $this->fld_sizes ) {
			$vals['oldlen'] = intval( $row->rc_old_len );
			$vals['newlen'] = intval( $row->rc_new_len );
		}

		/* Add the timestamp. */
		if ( $this->fld_timestamp ) {
			$vals['timestamp'] = wfTimestamp( TS_ISO_8601, $row->rc_timestamp );
		}

		/* Add edit summary / log summary. */
		if ( $this->fld_comment && isset( $row->rc_comment ) ) {
			$vals['comment'] = $row->rc_comment;
		}

		if ( $this->fld_parsedcomment && isset( $row->rc_comment ) ) {
			$vals['parsedcomment'] = Linker::formatComment( $row->rc_comment, $title );
		}

		if ( $this->fld_redirect ) {
			if ( $row->page_is_redirect ) {
				$vals['redirect'] = '';
			}
		}

		/* Add the patrolled flag */
		if ( $this->fld_patrolled && $row->rc_patrolled == 1 ) {
			$vals['patrolled'] = '';
		}

		if ( $this->fld_loginfo && $row->rc_type == RC_LOG ) {
			$vals['logid'] = intval( $row->rc_logid );
			$vals['logtype'] = $row->rc_log_type;
			$vals['logaction'] = $row->rc_log_action;
			$logEntry = DatabaseLogEntry::newFromRow( (array)$row );
			ApiQueryLogEvents::addLogParams(
				$this->getResult(),
				$vals,
				$logEntry->getParameters(),
				$logEntry->getType(),
				$logEntry->getSubtype(),
				$logEntry->getTimestamp()
			);
		}

		if ( $this->fld_tags ) {
			if ( $row->ts_tags ) {
				$tags = explode( ',', $row->ts_tags );
				$this->getResult()->setIndexedTagName( $tags, 'tag' );
				$vals['tags'] = $tags;
			} else {
				$vals['tags'] = array();
			}
		}

		if ( $this->fld_sha1 && $row->rev_sha1 !== null ) {
			// The RevDel check should currently never pass due to the
			// rc_deleted = 0 condition in the WHERE clause, but in case that
			// ever changes we check it here too.
			if ( $row->rev_deleted & Revision::DELETED_TEXT ) {
				$vals['sha1hidden'] = '';
			} elseif ( $row->rev_sha1 !== '' ) {
				$vals['sha1'] = wfBaseConvert( $row->rev_sha1, 36, 16, 40 );
			} else {
				$vals['sha1'] = '';
			}
		}

		if ( !is_null( $this->token ) ) {
			$tokenFunctions = $this->getTokenFunctions();
			foreach ( $this->token as $t ) {
				$val = call_user_func( $tokenFunctions[$t], $row->rc_cur_id,
					$title, RecentChange::newFromRow( $row ) );
				if ( $val === false ) {
					$this->setWarning( "Action '$t' is not allowed for the current user" );
				} else {
					$vals[$t . 'token'] = $val;
				}
			}
		}

		return $vals;
	}
/**
 * Entrypoint
 * @param string $par parent page we will look at
 */
function wfSpecialRecentchangeslinked($par = NULL)
{
    global $wgUser, $wgOut, $wgLang, $wgContLang, $wgRequest;
    $fname = 'wfSpecialRecentchangeslinked';
    $days = $wgRequest->getInt('days');
    $target = isset($par) ? $par : $wgRequest->getText('target');
    $hideminor = $wgRequest->getBool('hideminor') ? 1 : 0;
    $wgOut->setPagetitle(wfMsg('recentchangeslinked'));
    $sk = $wgUser->getSkin();
    # Validate the title
    $nt = Title::newFromURL($target);
    if (!is_object($nt)) {
        $wgOut->errorPage('notargettitle', 'notargettext');
        return;
    }
    # Check for existence
    # Do a quiet redirect back to the page itself if it doesn't
    if (!$nt->exists()) {
        $wgOut->redirect($nt->getLocalUrl());
        return;
    }
    $id = $nt->getArticleId();
    $wgOut->setSubtitle(htmlspecialchars(wfMsg('rclsub', $nt->getPrefixedText())));
    if (!$days) {
        $days = $wgUser->getOption('rcdays');
        if (!$days) {
            $days = 7;
        }
    }
    $days = (int) $days;
    list($limit, $offset) = wfCheckLimits(100, 'rclimit');
    $dbr =& wfGetDB(DB_SLAVE);
    $cutoff = $dbr->timestamp(time() - $days * 86400);
    $hideminor = $hideminor ? 1 : 0;
    if ($hideminor) {
        $mlink = $sk->makeKnownLink($wgContLang->specialPage('Recentchangeslinked'), wfMsg('show'), 'target=' . htmlspecialchars($nt->getPrefixedURL()) . "&days={$days}&limit={$limit}&hideminor=0");
    } else {
        $mlink = $sk->makeKnownLink($wgContLang->specialPage("Recentchangeslinked"), wfMsg("hide"), "target=" . htmlspecialchars($nt->getPrefixedURL()) . "&days={$days}&limit={$limit}&hideminor=1");
    }
    if ($hideminor) {
        $cmq = 'AND rc_minor=0';
    } else {
        $cmq = '';
    }
    extract($dbr->tableNames('recentchanges', 'categorylinks', 'pagelinks', 'revision', 'page', "watchlist"));
    $uid = $wgUser->getID();
    // If target is a Category, use categorylinks and invert from and to
    if ($nt->getNamespace() == NS_CATEGORY) {
        $catkey = $dbr->addQuotes($nt->getDBKey());
        $sql = "SELECT /* wfSpecialRecentchangeslinked */\n\t\t\t\trc_id,\n\t\t\t\trc_cur_id,\n\t\t\t\trc_namespace,\n\t\t\t\trc_title,\n\t\t\t\trc_this_oldid,\n\t\t\t\trc_last_oldid,\n\t\t\t\trc_user,\n\t\t\t\trc_comment,\n\t\t\t\trc_user_text,\n\t\t\t\trc_timestamp,\n\t\t\t\trc_minor,\n\t\t\t\trc_bot,\n\t\t\t\trc_new,\n\t\t\t\trc_patrolled,\n\t\t\t\trc_type\n" . ($uid ? ",wl_user" : "") . "\n\t    FROM {$categorylinks}, {$recentchanges}\n" . ($uid ? "LEFT OUTER JOIN {$watchlist} ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : "") . "\n\t   WHERE rc_timestamp > '{$cutoff}'\n\t     {$cmq}\n\t     AND cl_from=rc_cur_id\n\t     AND cl_to={$catkey}\n\tGROUP BY rc_cur_id,rc_namespace,rc_title,\n\t \trc_user,rc_comment,rc_user_text,rc_timestamp,rc_minor,\n\t \trc_new\n\t\tORDER BY rc_timestamp DESC\n\tLIMIT {$limit};\n ";
    } else {
        $sql = "SELECT /* wfSpecialRecentchangeslinked */\n\t\t\trc_id,\n\t\t\trc_cur_id,\n\t\t\trc_namespace,\n\t\t\trc_title,\n\t\t\trc_user,\n\t\t\trc_comment,\n\t\t\trc_user_text,\n\t\t\trc_this_oldid,\n\t\t\trc_last_oldid,\n\t\t\trc_timestamp,\n\t\t\trc_minor,\n\t\t\trc_bot,\n\t\t\trc_new,\n\t\t\trc_patrolled,\n\t\t\trc_type\n" . ($uid ? ",wl_user" : "") . "\n   FROM {$pagelinks}, {$recentchanges}\n" . ($uid ? " LEFT OUTER JOIN {$watchlist} ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : "") . "\n   WHERE rc_timestamp > '{$cutoff}'\n\t{$cmq}\n     AND pl_namespace=rc_namespace\n     AND pl_title=rc_title\n     AND pl_from={$id}\nGROUP BY rc_cur_id,rc_namespace,rc_title,\n\t rc_user,rc_comment,rc_user_text,rc_timestamp,rc_minor,\n\t rc_new\nORDER BY rc_timestamp DESC\n   LIMIT {$limit}";
    }
    $res = $dbr->query($sql, $fname);
    $wgOut->addHTML("&lt; " . $sk->makeKnownLinkObj($nt, "", "redirect=no") . "<br />\n");
    $note = wfMsg("rcnote", $limit, $days, $wgLang->timeAndDate(wfTimestampNow(), true));
    $wgOut->addHTML("<hr />\n{$note}\n<br />");
    $note = rcDayLimitlinks($days, $limit, "Recentchangeslinked", "target=" . $nt->getPrefixedURL() . "&hideminor={$hideminor}", false, $mlink);
    $wgOut->addHTML($note . "\n");
    $list = ChangesList::newFromUser($wgUser);
    $s = $list->beginRecentChangesList();
    $count = $dbr->numRows($res);
    $counter = 1;
    while ($limit) {
        if (0 == $count) {
            break;
        }
        $obj = $dbr->fetchObject($res);
        --$count;
        #		print_r ( $obj ) ;
        #		print "<br/>\n" ;
        $rc = RecentChange::newFromRow($obj);
        $rc->counter = $counter++;
        $s .= $list->recentChangesLine($rc, !empty($obj->wl_user));
        --$limit;
    }
    $s .= $list->endRecentChangesList();
    $dbr->freeResult($res);
    $wgOut->addHTML($s);
}
 private function getRevisionFromLog()
 {
     wfProfileIn(__METHOD__);
     $this->addTables('logging');
     $this->addFields('recentchanges.*');
     $this->addTables('recentchanges');
     $this->addWhere('log_id = rc_logid');
     $this->addWhere('log_title = rc_title');
     $this->addWhere('log_namespace = rc_namespace');
     $this->addWhereFld('log_id', $this->mLogid);
     $res = $this->select(__METHOD__);
     $count = 0;
     $oRC = false;
     $db = $this->getDB();
     if ($row = $db->fetchObject($res)) {
         $oRC = RecentChange::newFromRow($row);
     }
     $db->freeResult($res);
     $res = is_object($oRC) ? $this->getArchivePage($oRC) : false;
     if (empty($res) && is_object($oRC)) {
         $res = $this->getRecentchangePage($oRC);
     }
     if (empty($res) && !is_object($oRC)) {
         $res = $this->getLoggingArchivePage();
     }
     wfProfileOut(__METHOD__);
     return $res;
 }
 public function newFromRow($obj)
 {
     $rc = \RecentChange::newFromRow($obj);
     $rc->setExtra(array('pageStatus' => 'update'));
     return $rc;
 }
 /**
  * Build and output the actual changes list.
  *
  * @param ResultWrapper $rows Database rows
  * @param FormOptions $opts
  */
 public function outputChangesList($rows, $opts)
 {
     $dbr = $this->getDB();
     $user = $this->getUser();
     $output = $this->getOutput();
     # Show a message about replica DB lag, if applicable
     $lag = wfGetLB()->safeGetLag($dbr);
     if ($lag > 0) {
         $output->showLagWarning($lag);
     }
     # If no rows to display, show message before try to render the list
     if ($rows->numRows() == 0) {
         $output->wrapWikiMsg("<div class='mw-changeslist-empty'>\n\$1\n</div>", 'recentchanges-noresult');
         return;
     }
     $dbr->dataSeek($rows, 0);
     $list = ChangesList::newFromContext($this->getContext());
     $list->setWatchlistDivs();
     $list->initChangesListRows($rows);
     $dbr->dataSeek($rows, 0);
     if ($this->getConfig()->get('RCShowWatchingUsers') && $user->getOption('shownumberswatching')) {
         $watchedItemStore = MediaWikiServices::getInstance()->getWatchedItemStore();
     }
     $s = $list->beginRecentChangesList();
     $userShowHiddenCats = $this->getUser()->getBoolOption('showhiddencats');
     $counter = 1;
     foreach ($rows as $obj) {
         # Make RC entry
         $rc = RecentChange::newFromRow($obj);
         # Skip CatWatch entries for hidden cats based on user preference
         if ($rc->getAttribute('rc_type') == RC_CATEGORIZE && !$userShowHiddenCats && $rc->getParam('hidden-cat')) {
             continue;
         }
         $rc->counter = $counter++;
         if ($this->getConfig()->get('ShowUpdatedMarker')) {
             $updated = $obj->wl_notificationtimestamp;
         } else {
             $updated = false;
         }
         if (isset($watchedItemStore)) {
             $rcTitleValue = new TitleValue((int) $obj->rc_namespace, $obj->rc_title);
             $rc->numberofWatchingusers = $watchedItemStore->countWatchers($rcTitleValue);
         } else {
             $rc->numberofWatchingusers = 0;
         }
         $changeLine = $list->recentChangesLine($rc, $updated, $counter);
         if ($changeLine !== false) {
             $s .= $changeLine;
         }
     }
     $s .= $list->endRecentChangesList();
     $output->addHTML($s);
 }
Exemple #17
0
 function renderRow($row, $forFeed = false)
 {
     global $wgUser, $wgLang;
     $change = RecentChange::newFromRow($row);
     $change->counter = 0;
     //hack
     $usetemplate = $this->usetemplate;
     $templatetext = $this->templatetext;
     if (!$templatetext && $forFeed) {
         $templatetext = '{{{head}}}';
         $usetemplate = true;
     }
     if (!$usetemplate) {
         #$pagelink = $this->skin->makeKnownLinkObj( $title );
         $this->changelist->insertDateHeader($dummy, $row->rc_timestamp);
         #dummy call to suppress date headers
         $html = $this->changelist->recentChangesLine($change);
         return $html;
     } else {
         $params = array();
         $params['namespace'] = $row->rc_namespace;
         $params['title'] = $row->rc_title;
         $title = $change->getTitle();
         $params['pagename'] = $title->getPrefixedText();
         $params['minor'] = $row->rc_minor ? 'true' : '';
         $params['bot'] = $row->rc_bot ? 'true' : '';
         $params['patrolled'] = $row->rc_patrolled ? 'true' : '';
         $params['anon'] = $row->rc_user <= 0 ? 'true' : '';
         #XXX: perhaps use (rc_user == rc_ip) instead? That would take care of entries from importing.
         $params['new'] = $row->rc_type == RC_NEW ? 'true' : '';
         $params['type'] = $row->rc_type;
         $params['user'] = $row->rc_user_text;
         $params['rawtime'] = $row->rc_timestamp;
         $params['time'] = $wgLang->time($row->rc_timestamp, true, true);
         $params['date'] = $wgLang->date($row->rc_timestamp, true, true);
         $params['timeanddate'] = $wgLang->timeanddate($row->rc_timestamp, true, true);
         $params['old_len'] = $row->rc_old_len;
         $params['new_len'] = $row->rc_new_len;
         $params['old_rev'] = $row->rc_last_oldid;
         $params['new_rev'] = $row->rc_this_oldid;
         $diffq = $change->diffLinkTrail(false);
         $params['diff'] = $diffq ? $title->getFullURL($diffq) : '';
         $permaq = "oldid=" . $row->rc_this_oldid;
         $params['permalink'] = $permaq ? $title->getFullURL($permaq) : '';
         $params['comment'] = str_replace(array('{{', '}}', '|', '\''), array('&#123;&#123;', '&#125;&#125;', '&#124;', '$#39;'), wfEscapeWikiText($row->rc_comment));
         if (stripos($templatetext, '{{{content}}}') !== false || stripos($templatetext, '{{{head}}}') !== false) {
             $article = new Article($title, $row->rc_this_oldid);
             $t = $article->getContent();
             //TODO: expand variables & templates first, so cut-off applies to effective content,
             //      and extension tags from templates are stripped properly
             //      this doesn't work though: $t = $this->templateparser->preprocess( $t, $this->title, new ParserOptions() );
             //TODO: avoid magic categories, interwiki-links, etc
             $params['content'] = NewsRenderer::sanitizeWikiText($t, $this->templateparser);
             if (stripos($templatetext, '{{{head}}}') !== false) {
                 $params['head'] = NewsRenderer::extractHead($params['content'], $title);
             }
         }
         $text = NewsRenderer::replaceVariables($this->templateparser, $templatetext, $params, $this->title);
         return $text;
     }
 }
/**
 * Constructor
 */
function wfSpecialRecentchanges($par, $specialPage)
{
    global $wgUser, $wgOut, $wgRequest, $wgUseRCPatrol;
    global $wgRCShowWatchingUsers, $wgShowUpdatedMarker;
    global $wgAllowCategorizedRecentChanges;
    $fname = 'wfSpecialRecentchanges';
    $userGroups = $wgUser->getGroups();
    if (!$wgUser->isAllowed('patrol') || in_array('patrolblock', $userGroups)) {
        $wgOut->errorpage('nosuchspecialpage', 'nospecialpagetext');
        return;
    }
    # Get query parameters
    $feedFormat = $wgRequest->getVal('feed');
    /* Checkbox values can't be true by default, because
     * we cannot differentiate between unset and not set at all
     */
    $defaults = array('days' => $wgUser->getDefaultOption('rcdays'), 'limit' => $wgUser->getDefaultOption('rclimit'), 'hideminor' => false, 'hidebots' => true, 'hideanons' => false, 'hideliu' => false, 'hidepatrolled' => false, 'hidemyself' => false, 'from' => '', 'namespace' => null, 'invert' => false, 'categories_any' => false, 'reverse' => false, 'featured' => false, 'categories_any' => false);
    extract($defaults);
    $days = $wgUser->getOption('rcdays', $defaults['days']);
    $days = $wgRequest->getInt('days', $days);
    $limit = $wgUser->getOption('rclimit', $defaults['limit']);
    #	list( $limit, $offset ) = wfCheckLimits( 100, 'rclimit' );
    $limit = $wgRequest->getInt('limit', $limit);
    /* order of selection: url > preferences > default */
    $hideminor = $wgRequest->getBool('hideminor', $wgUser->getOption('hideminor') ? true : $defaults['hideminor']);
    # As a feed, use limited settings only
    if ($feedFormat) {
        global $wgFeedLimit;
        if ($limit > $wgFeedLimit) {
            $limit = $wgFeedLimit;
        }
    } else {
        $namespace = $wgRequest->getIntOrNull('namespace');
        $invert = $wgRequest->getBool('invert', $defaults['invert']);
        //XXADDED
        $reverse = $wgRequest->getBool('reverse', $defaults['reverse']);
        $featured = $wgRequest->getBool('featured', $defaults['featured']);
        $hidebots = $wgRequest->getBool('hidebots', $defaults['hidebots']);
        $hideanons = $wgRequest->getBool('hideanons', $defaults['hideanons']);
        $hideliu = $wgRequest->getBool('hideliu', $defaults['hideliu']);
        $hidepatrolled = $wgRequest->getBool('hidepatrolled', $defaults['hidepatrolled']);
        $hidemyself = $wgRequest->getBool('hidemyself', $defaults['hidemyself']);
        $from = $wgRequest->getVal('from', $defaults['from']);
        # Get query parameters from path
        if ($par) {
            $bits = preg_split('/\\s*,\\s*/', trim($par));
            foreach ($bits as $bit) {
                if ('hidebots' == $bit) {
                    $hidebots = 1;
                }
                if ('bots' == $bit) {
                    $hidebots = 0;
                }
                if ('hideminor' == $bit) {
                    $hideminor = 1;
                }
                if ('minor' == $bit) {
                    $hideminor = 0;
                }
                if ('hideliu' == $bit) {
                    $hideliu = 1;
                }
                if ('hidepatrolled' == $bit) {
                    $hidepatrolled = 1;
                }
                if ('hideanons' == $bit) {
                    $hideanons = 1;
                }
                if ('hidemyself' == $bit) {
                    $hidemyself = 1;
                }
                if (is_numeric($bit)) {
                    $limit = $bit;
                }
                $m = array();
                if (preg_match('/^limit=(\\d+)$/', $bit, $m)) {
                    $limit = $m[1];
                }
                if (preg_match('/^days=(\\d+)$/', $bit, $m)) {
                    $days = $m[1];
                }
            }
        }
    }
    if ($limit < 0 || $limit > 5000) {
        $limit = $defaults['limit'];
    }
    # Database connection and caching
    $dbr = wfGetDB(DB_SLAVE);
    list($recentchanges, $watchlist) = $dbr->tableNamesN('recentchanges', 'watchlist');
    $cutoff_unixtime = time() - $days * 86400;
    $cutoff_unixtime = $cutoff_unixtime - $cutoff_unixtime % 86400;
    $cutoff = $dbr->timestamp($cutoff_unixtime);
    if (preg_match('/^[0-9]{14}$/', $from) and $from > wfTimestamp(TS_MW, $cutoff)) {
        $cutoff = $dbr->timestamp($from);
    } else {
        $from = $defaults['from'];
    }
    # 10 seconds server-side caching max
    $wgOut->setSquidMaxage(0);
    # Get last modified date, for client caching
    # Don't use this if we are using the patrol feature, patrol changes don't update the timestamp
    $lastmod = $dbr->selectField('recentchanges', 'MAX(rc_timestamp)', false, $fname);
    if ($feedFormat || !$wgUseRCPatrol) {
        if ($lastmod && $wgOut->checkLastModified($lastmod)) {
            # Client cache fresh and headers sent, nothing more to do.
            return;
        }
    }
    # It makes no sense to hide both anons and logged-in users
    # Where this occurs, force anons to be shown
    if ($hideanons && $hideliu) {
        $hideanons = false;
    }
    # Form WHERE fragments for all the options
    $hidem = $hideminor ? 'AND rc_minor = 0' : '';
    $hidem .= $hidebots ? ' AND rc_bot = 0' : '';
    $hidem .= $hideliu ? ' AND rc_user = 0' : '';
    $hidem .= $wgUseRCPatrol && $hidepatrolled ? ' AND rc_patrolled = 0' : '';
    $hidem .= $hideanons ? ' AND rc_user != 0' : '';
    if ($hidemyself) {
        if ($wgUser->getID()) {
            $hidem .= ' AND rc_user != ' . $wgUser->getID();
        } else {
            $hidem .= ' AND rc_user_text != ' . $dbr->addQuotes($wgUser->getName());
        }
    }
    # Namespace filtering
    $hidem .= is_null($namespace) ? '' : ' AND rc_namespace' . ($invert ? '!=' : '=') . $namespace;
    //XXADDED
    $order = $reverse ? " ASC" : "DESC";
    $ft = $featured ? " AND page_is_featured = 1 " : "";
    // This is the big thing!
    $uid = $wgUser->getID();
    //XXCHANGED
    // Perform query
    $forceclause = $dbr->useIndexClause("rc_timestamp");
    $sql2 = "SELECT * FROM {$recentchanges} {$forceclause}" . ($uid ? "LEFT OUTER JOIN {$watchlist} ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : "") . " LEFT OUTER JOIN page ON page_title=rc_title AND page_namespace=rc_namespace " . "WHERE rc_timestamp >= '{$cutoff}' {$hidem} {$ft} " . "ORDER BY rc_timestamp {$order} ";
    $sql2 = $dbr->limitResult($sql2, $limit, 0);
    $res = $dbr->query($sql2, $fname);
    // Fetch results, prepare a batch link existence check query
    $rows = array();
    $batch = new LinkBatch();
    while ($row = $dbr->fetchObject($res)) {
        $rows[] = $row;
        if (!$feedFormat) {
            // User page and talk links
            $batch->add(NS_USER, $row->rc_user_text);
            $batch->add(NS_USER_TALK, $row->rc_user_text);
        }
    }
    $dbr->freeResult($res);
    if ($feedFormat) {
        rcOutputFeed($rows, $feedFormat, $limit, $hideminor, $lastmod);
    } else {
        # Web output...
        // Run existence checks
        $batch->execute();
        $any = $wgRequest->getBool('categories_any', $defaults['categories_any']);
        // Output header
        if (!$specialPage->including()) {
            $wgOut->addWikiText('<div class="minor_text">' . wfMsgForContentNoTrans("recentchangestext") . '<br /></div>');
            // Dump everything here
            $nondefaults = array();
            wfAppendToArrayIfNotDefault('days', $days, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('limit', $limit, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('hideminor', $hideminor, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('hidebots', $hidebots, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('hideanons', $hideanons, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('hideliu', $hideliu, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('hidepatrolled', $hidepatrolled, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('hidemyself', $hidemyself, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('from', $from, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('namespace', $namespace, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('invert', $invert, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('categories_any', $any, $defaults, $nondefaults);
            // Add end of the texts
            $wgOut->addHTML('<div class="rcoptions">' . rcOptionsPanel($defaults, $nondefaults) . "\n");
            //XXCHANGED
            $wgOut->addHTML(rcNamespaceForm($namespace, $invert, $reverse, $featured, $nondefaults, $any) . '</div>' . "\n");
            //XXADDED
            global $wgLanguageCode;
            if ($wgUser->getID() > 0 && $wgLanguageCode == 'en') {
                $sk = $wgUser->getSkin();
                $url = $wgRequest->getRequestURL();
                if ($wgRequest->getVal('refresh', null) != null) {
                    $url = str_replace("&refresh=1", "", $url);
                    $url = str_replace("?refresh=1", "", $url);
                    $wgOut->addHTML("<a href='{$url}' class='button secondary'>" . wfMsg('rc_turn_refresh_off') . "</a>");
                } else {
                    if (strpos($url, "?") !== false) {
                        $url .= "&refresh=1";
                    } else {
                        $url .= "?refresh=1";
                    }
                    $wgOut->addHTML("<a href='{$url}' class='button secondary'>" . wfMsg('rc_turn_refresh_on') . "</a>");
                }
                $wgOut->addHTML("&nbsp;<a class='button secondary' href='#' onclick=\"open('/index.php?title=Special:RCBuddy&hidepatrolled=1&limit=200&featured=1', '', 'scrollbars=no,status=no,width=570,height=200,resizable=yes,titlebar=no');\">RC Buddy</a>");
            }
        }
        // And now for the content
        $wgOut->setSyndicated(true);
        $list = ChangesList::newFromUser($wgUser);
        if ($wgAllowCategorizedRecentChanges) {
            $categories = trim($wgRequest->getVal('categories', ""));
            $categories = str_replace("|", "\n", $categories);
            $categories = explode("\n", $categories);
            rcFilterByCategories($rows, $categories, $any);
        }
        $s = $list->beginRecentChangesList();
        $s .= "<div id='recentchanges'>\n";
        $counter = 1;
        $showWatcherCount = $wgRCShowWatchingUsers && $wgUser->getOption('shownumberswatching');
        $watcherCache = array();
        foreach ($rows as $obj) {
            if ($limit == 0) {
                break;
            }
            if (!($hideminor && $obj->rc_minor) && !($hidepatrolled && $obj->rc_patrolled)) {
                $rc = RecentChange::newFromRow($obj);
                $rc->counter = $counter++;
                if ($wgShowUpdatedMarker && !empty($obj->wl_notificationtimestamp) && $obj->rc_timestamp >= $obj->wl_notificationtimestamp) {
                    $rc->notificationtimestamp = true;
                } else {
                    $rc->notificationtimestamp = false;
                }
                $rc->numberofWatchingusers = 0;
                // Default
                if ($showWatcherCount && $obj->rc_namespace >= 0) {
                    if (!isset($watcherCache[$obj->rc_namespace][$obj->rc_title])) {
                        $watcherCache[$obj->rc_namespace][$obj->rc_title] = $dbr->selectField('watchlist', 'COUNT(*)', array('wl_namespace' => $obj->rc_namespace, 'wl_title' => $obj->rc_title), __METHOD__ . '-watchers');
                    }
                    $rc->numberofWatchingusers = $watcherCache[$obj->rc_namespace][$obj->rc_title];
                }
                $rc->show_namespace = $namespace;
                $rc->invert = $invert;
                $rc->reverse = $reverse;
                $rc->featured = $featured;
                $s .= $list->recentChangesLine($rc, !empty($obj->wl_user));
                --$limit;
            }
        }
        $s .= $list->endRecentChangesList();
        $s .= "</div>\n";
        $wgOut->addHTML($s);
    }
}
 function formatRow($row)
 {
     # Incompatible stuff.
     $rc = RecentChange::newFromRow($row);
     $rc->counter = $this->mPage->mCounter++;
     return $this->mChangesList->recentChangesLine($rc, false);
 }
 /**
  * Build and output the actual changes list.
  *
  * @param ResultWrapper $rows Database rows
  * @param FormOptions $opts
  */
 public function outputChangesList($rows, $opts)
 {
     $limit = $opts['limit'];
     $showWatcherCount = $this->getConfig()->get('RCShowWatchingUsers') && $this->getUser()->getOption('shownumberswatching');
     $watcherCache = [];
     $dbr = $this->getDB();
     $counter = 1;
     $list = ChangesList::newFromContext($this->getContext());
     $list->initChangesListRows($rows);
     $userShowHiddenCats = $this->getUser()->getBoolOption('showhiddencats');
     $rclistOutput = $list->beginRecentChangesList();
     foreach ($rows as $obj) {
         if ($limit == 0) {
             break;
         }
         $rc = RecentChange::newFromRow($obj);
         # Skip CatWatch entries for hidden cats based on user preference
         if ($rc->getAttribute('rc_type') == RC_CATEGORIZE && !$userShowHiddenCats && $rc->getParam('hidden-cat')) {
             continue;
         }
         $rc->counter = $counter++;
         # Check if the page has been updated since the last visit
         if ($this->getConfig()->get('ShowUpdatedMarker') && !empty($obj->wl_notificationtimestamp)) {
             $rc->notificationtimestamp = $obj->rc_timestamp >= $obj->wl_notificationtimestamp;
         } else {
             $rc->notificationtimestamp = false;
             // Default
         }
         # Check the number of users watching the page
         $rc->numberofWatchingusers = 0;
         // Default
         if ($showWatcherCount && $obj->rc_namespace >= 0) {
             if (!isset($watcherCache[$obj->rc_namespace][$obj->rc_title])) {
                 $watcherCache[$obj->rc_namespace][$obj->rc_title] = MediaWikiServices::getInstance()->getWatchedItemStore()->countWatchers(new TitleValue((int) $obj->rc_namespace, $obj->rc_title));
             }
             $rc->numberofWatchingusers = $watcherCache[$obj->rc_namespace][$obj->rc_title];
         }
         $changeLine = $list->recentChangesLine($rc, !empty($obj->wl_user), $counter);
         if ($changeLine !== false) {
             $rclistOutput .= $changeLine;
             --$limit;
         }
     }
     $rclistOutput .= $list->endRecentChangesList();
     if ($rows->numRows() === 0) {
         $this->getOutput()->addHTML('<div class="mw-changeslist-empty">' . $this->msg('recentchanges-noresult')->parse() . '</div>');
         if (!$this->including()) {
             $this->getOutput()->setStatusCode(404);
         }
     } else {
         $this->getOutput()->addHTML($rclistOutput);
     }
 }
	/**
	 * Fetch a particular recent change given the rc_id value
	 *
	 * @param $rcid rc_id value of the row to fetch
	 * @return RecentChange
	 */
	private function loadChange( $rcid ) {
		$dbr = wfGetDB( DB_SLAVE );
		$res = $dbr->select( 'recentchanges', '*', array( 'rc_id' => $rcid ), 'Patroller::loadChange' );
		if( $dbr->numRows( $res ) > 0 ) {
			$row = $dbr->fetchObject( $res );
			return RecentChange::newFromRow( $row );
		} else {
			return false;
		}
	}
/**
 * Constructor
 */
function wfSpecialRecentchanges($par, $specialPage)
{
    global $wgUser, $wgOut, $wgRequest, $wgUseRCPatrol;
    global $wgRCShowWatchingUsers, $wgShowUpdatedMarker;
    global $wgAllowCategorizedRecentChanges;
    $fname = 'wfSpecialRecentchanges';
    # Get query parameters
    $feedFormat = $wgRequest->getVal('feed');
    /* Checkbox values can't be true by default, because
     * we cannot differentiate between unset and not set at all
     */
    $defaults = array('days' => $wgUser->getDefaultOption('rcdays'), 'limit' => $wgUser->getDefaultOption('rclimit'), 'hideminor' => false, 'hidebots' => true, 'hideanons' => false, 'hideliu' => false, 'hidepatrolled' => false, 'hidemyself' => false, 'from' => '', 'namespace' => null, 'invert' => false, 'categories_any' => false);
    extract($defaults);
    $days = $wgUser->getOption('rcdays', $defaults['days']);
    $days = $wgRequest->getInt('days', $days);
    $limit = $wgUser->getOption('rclimit', $defaults['limit']);
    #	list( $limit, $offset ) = wfCheckLimits( 100, 'rclimit' );
    $limit = $wgRequest->getInt('limit', $limit);
    /* order of selection: url > preferences > default */
    $hideminor = $wgRequest->getBool('hideminor', $wgUser->getOption('hideminor') ? true : $defaults['hideminor']);
    # As a feed, use limited settings only
    if ($feedFormat) {
        global $wgFeedLimit;
        if ($limit > $wgFeedLimit) {
            $limit = $wgFeedLimit;
        }
    } else {
        $namespace = $wgRequest->getIntOrNull('namespace');
        $invert = $wgRequest->getBool('invert', $defaults['invert']);
        $hidebots = $wgRequest->getBool('hidebots', $defaults['hidebots']);
        $hideanons = $wgRequest->getBool('hideanons', $defaults['hideanons']);
        $hideliu = $wgRequest->getBool('hideliu', $defaults['hideliu']);
        $hidepatrolled = $wgRequest->getBool('hidepatrolled', $defaults['hidepatrolled']);
        $hidemyself = $wgRequest->getBool('hidemyself', $defaults['hidemyself']);
        $from = $wgRequest->getVal('from', $defaults['from']);
        # Get query parameters from path
        if ($par) {
            $bits = preg_split('/\\s*,\\s*/', trim($par));
            foreach ($bits as $bit) {
                if ('hidebots' == $bit) {
                    $hidebots = 1;
                }
                if ('bots' == $bit) {
                    $hidebots = 0;
                }
                if ('hideminor' == $bit) {
                    $hideminor = 1;
                }
                if ('minor' == $bit) {
                    $hideminor = 0;
                }
                if ('hideliu' == $bit) {
                    $hideliu = 1;
                }
                if ('hidepatrolled' == $bit) {
                    $hidepatrolled = 1;
                }
                if ('hideanons' == $bit) {
                    $hideanons = 1;
                }
                if ('hidemyself' == $bit) {
                    $hidemyself = 1;
                }
                if (is_numeric($bit)) {
                    $limit = $bit;
                }
                $m = array();
                if (preg_match('/^limit=(\\d+)$/', $bit, $m)) {
                    $limit = $m[1];
                }
                if (preg_match('/^days=(\\d+)$/', $bit, $m)) {
                    $days = $m[1];
                }
            }
        }
    }
    if ($limit < 0 || $limit > 5000) {
        $limit = $defaults['limit'];
    }
    # Database connection and caching
    $dbr = wfGetDB(DB_SLAVE);
    list($recentchanges, $watchlist) = $dbr->tableNamesN('recentchanges', 'watchlist');
    $cutoff_unixtime = time() - $days * 86400;
    $cutoff_unixtime = $cutoff_unixtime - $cutoff_unixtime % 86400;
    $cutoff = $dbr->timestamp($cutoff_unixtime);
    if (preg_match('/^[0-9]{14}$/', $from) and $from > wfTimestamp(TS_MW, $cutoff)) {
        $cutoff = $dbr->timestamp($from);
    } else {
        $from = $defaults['from'];
    }
    # 10 seconds server-side caching max
    $wgOut->setSquidMaxage(10);
    # Get last modified date, for client caching
    # Don't use this if we are using the patrol feature, patrol changes don't update the timestamp
    $lastmod = $dbr->selectField('recentchanges', 'MAX(rc_timestamp)', false, $fname);
    if ($feedFormat || !$wgUseRCPatrol) {
        if ($lastmod && $wgOut->checkLastModified($lastmod)) {
            # Client cache fresh and headers sent, nothing more to do.
            return;
        }
    }
    # It makes no sense to hide both anons and logged-in users
    # Where this occurs, force anons to be shown
    if ($hideanons && $hideliu) {
        $hideanons = false;
    }
    # Form WHERE fragments for all the options
    $hidem = $hideminor ? 'AND rc_minor = 0' : '';
    $hidem .= $hidebots ? ' AND rc_bot = 0' : '';
    $hidem .= $hideliu ? ' AND rc_user = 0' : '';
    $hidem .= $wgUseRCPatrol && $hidepatrolled ? ' AND rc_patrolled = 0' : '';
    $hidem .= $hideanons ? ' AND rc_user != 0' : '';
    if ($hidemyself) {
        if ($wgUser->getID()) {
            $hidem .= ' AND rc_user != ' . $wgUser->getID();
        } else {
            $hidem .= ' AND rc_user_text != ' . $dbr->addQuotes($wgUser->getName());
        }
    }
    # Namespace filtering
    $hidem .= is_null($namespace) ? '' : ' AND rc_namespace' . ($invert ? '!=' : '=') . $namespace;
    // This is the big thing!
    $uid = $wgUser->getID();
    // Perform query
    $forceclause = $dbr->useIndexClause("rc_timestamp");
    $sql2 = "SELECT * FROM {$recentchanges} {$forceclause}" . ($uid ? "LEFT OUTER JOIN {$watchlist} ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : "") . "WHERE rc_timestamp >= '{$cutoff}' {$hidem} " . "ORDER BY rc_timestamp DESC";
    $sql2 = $dbr->limitResult($sql2, $limit, 0);
    $res = $dbr->query($sql2, $fname);
    // Fetch results, prepare a batch link existence check query
    $rows = array();
    $batch = new LinkBatch();
    while ($row = $dbr->fetchObject($res)) {
        $rows[] = $row;
        if (!$feedFormat) {
            // User page link
            $title = Title::makeTitleSafe(NS_USER, $row->rc_user_text);
            $batch->addObj($title);
            // User talk
            $title = Title::makeTitleSafe(NS_USER_TALK, $row->rc_user_text);
            $batch->addObj($title);
        }
    }
    $dbr->freeResult($res);
    if ($feedFormat) {
        rcOutputFeed($rows, $feedFormat, $limit, $hideminor, $lastmod);
    } else {
        # Web output...
        // Run existence checks
        $batch->execute();
        $any = $wgRequest->getBool('categories_any', $defaults['categories_any']);
        // Output header
        if (!$specialPage->including()) {
            $wgOut->addWikiText(wfMsgForContentNoTrans("recentchangestext"));
            // Dump everything here
            $nondefaults = array();
            wfAppendToArrayIfNotDefault('days', $days, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('limit', $limit, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('hideminor', $hideminor, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('hidebots', $hidebots, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('hideanons', $hideanons, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('hideliu', $hideliu, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('hidepatrolled', $hidepatrolled, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('hidemyself', $hidemyself, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('from', $from, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('namespace', $namespace, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('invert', $invert, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('categories_any', $any, $defaults, $nondefaults);
            // Add end of the texts
            $wgOut->addHTML('<div class="rcoptions">' . rcOptionsPanel($defaults, $nondefaults) . "\n");
            $wgOut->addHTML(rcNamespaceForm($namespace, $invert, $nondefaults, $any) . '</div>' . "\n");
        }
        // And now for the content
        $list = ChangesList::newFromUser($wgUser);
        if ($wgAllowCategorizedRecentChanges) {
            $categories = trim($wgRequest->getVal('categories', ""));
            $categories = str_replace("|", "\n", $categories);
            $categories = explode("\n", $categories);
            rcFilterByCategories($rows, $categories, $any);
        }
        $s = $list->beginRecentChangesList();
        $counter = 1;
        foreach ($rows as $obj) {
            if ($limit == 0) {
                break;
            }
            if (!($hideminor && $obj->rc_minor) && !($hidepatrolled && $obj->rc_patrolled)) {
                $rc = RecentChange::newFromRow($obj);
                $rc->counter = $counter++;
                if ($wgShowUpdatedMarker && !empty($obj->wl_notificationtimestamp) && $obj->rc_timestamp >= $obj->wl_notificationtimestamp) {
                    $rc->notificationtimestamp = true;
                } else {
                    $rc->notificationtimestamp = false;
                }
                if ($wgRCShowWatchingUsers && $wgUser->getOption('shownumberswatching')) {
                    $sql3 = "SELECT COUNT(*) AS n FROM {$watchlist} WHERE wl_title='" . $dbr->strencode($obj->rc_title) . "' AND wl_namespace={$obj->rc_namespace}";
                    $res3 = $dbr->query($sql3, 'wfSpecialRecentChanges');
                    $x = $dbr->fetchObject($res3);
                    $rc->numberofWatchingusers = $x->n;
                } else {
                    $rc->numberofWatchingusers = 0;
                }
                $s .= $list->recentChangesLine($rc, !empty($obj->wl_user));
                --$limit;
            }
        }
        $s .= $list->endRecentChangesList();
        $wgOut->addHTML($s);
    }
}
/**
 * Constructor
 */
function wfSpecialRecentchanges($par, $specialPage)
{
    global $wgUser, $wgOut, $wgLang, $wgContLang, $wgTitle, $wgMemc, $wgDBname;
    global $wgRequest, $wgSitename, $wgLanguageCode, $wgContLanguageCode;
    global $wgFeedClasses, $wgUseRCPatrol;
    global $wgRCShowCurrentRevisionOnly, $wgRCShowWatchingUsers, $wgShowUpdatedMarker;
    global $wgLinkCache;
    $fname = 'wfSpecialRecentchanges';
    # Get query parameters
    $feedFormat = $wgRequest->getVal('feed');
    $defaults = array('days' => $wgUser->getDefaultOption('rcdays'), 'limit' => $wgUser->getDefaultOption('rclimit'), 'hideminor' => false, 'hidebots' => true, 'hideliu' => false, 'hidepatrolled' => false, 'from' => '', 'namespace' => null, 'invert' => false);
    extract($defaults);
    $days = $wgUser->getOption('rcdays');
    if (!$days) {
        $days = $defaults['days'];
    }
    $days = $wgRequest->getInt('days', $days);
    $limit = $wgUser->getOption('rclimit');
    if (!$limit) {
        $limit = $defaults['limit'];
    }
    #	list( $limit, $offset ) = wfCheckLimits( 100, 'rclimit' );
    $limit = $wgRequest->getInt('limit', $limit);
    /* order of selection: url > preferences > default */
    $hideminor = $wgRequest->getBool('hideminor', $wgUser->getOption('hideminor') ? true : $defaults['hideminor']);
    # As a feed, use limited settings only
    if ($feedFormat) {
        global $wgFeedLimit;
        if ($limit > $wgFeedLimit) {
            $options['limit'] = $wgFeedLimit;
        }
    } else {
        $namespace = $wgRequest->getIntOrNull('namespace');
        $invert = $wgRequest->getBool('invert', $defaults['invert']);
        $hidebots = $wgRequest->getBool('hidebots', $defaults['hidebots']);
        $hideliu = $wgRequest->getBool('hideliu', $defaults['hideliu']);
        $hidepatrolled = $wgRequest->getBool('hidepatrolled', $defaults['hidepatrolled']);
        $from = $wgRequest->getVal('from', $defaults['from']);
        # Get query parameters from path
        if ($par) {
            $bits = preg_split('/\\s*,\\s*/', trim($par));
            foreach ($bits as $bit) {
                if ('hidebots' == $bit) {
                    $hidebots = 1;
                }
                if ('bots' == $bit) {
                    $hidebots = 0;
                }
                if ('hideminor' == $bit) {
                    $hideminor = 1;
                }
                if ('minor' == $bit) {
                    $hideminor = 0;
                }
                if ('hideliu' == $bit) {
                    $hideliu = 1;
                }
                if ('hidepatrolled' == $bit) {
                    $hidepatrolled = 1;
                }
                if (is_numeric($bit)) {
                    $limit = $bit;
                }
                if (preg_match('/^limit=(\\d+)$/', $bit, $m)) {
                    $limit = $m[1];
                }
                if (preg_match('/^days=(\\d+)$/', $bit, $m)) {
                    $days = $m[1];
                }
            }
        }
    }
    if ($limit < 0 || $limit > 5000) {
        $limit = $defaults['limit'];
    }
    # Database connection and caching
    $dbr =& wfGetDB(DB_SLAVE);
    extract($dbr->tableNames('recentchanges', 'watchlist', 'page'));
    $cutoff_unixtime = time() - $days * 86400;
    $cutoff_unixtime = $cutoff_unixtime - $cutoff_unixtime % 86400;
    $cutoff = $dbr->timestamp($cutoff_unixtime);
    if (preg_match('/^[0-9]{14}$/', $from) and $from > wfTimestamp(TS_MW, $cutoff)) {
        $cutoff = $dbr->timestamp($from);
    } else {
        $from = $defaults['from'];
    }
    # 10 seconds server-side caching max
    $wgOut->setSquidMaxage(10);
    # Get last modified date, for client caching
    # Don't use this if we are using the patrol feature, patrol changes don't update the timestamp
    $lastmod = $dbr->selectField('recentchanges', 'MAX(rc_timestamp)', false, $fname);
    if ($feedFormat || !$wgUseRCPatrol) {
        if ($lastmod && $wgOut->checkLastModified($lastmod)) {
            # Client cache fresh and headers sent, nothing more to do.
            return;
        }
    }
    $hidem = $hideminor ? 'AND rc_minor=0' : '';
    $hidem .= $hidebots ? ' AND rc_bot=0' : '';
    $hidem .= $hideliu ? ' AND rc_user=0' : '';
    $hidem .= $hidepatrolled ? ' AND rc_patrolled=0' : '';
    $hidem .= is_null($namespace) ? '' : ' AND rc_namespace' . ($invert ? '!=' : '=') . $namespace;
    // This is the big thing!
    $uid = $wgUser->getID();
    // Perform query
    if ($wgRCShowCurrentRevisionOnly && $wgUser->getOption('rccurrevonly')) {
        $sql2 = "SELECT {$recentchanges}.*" . ($uid ? ",wl_user,wl_notificationtimestamp,wl_lastvisitedrevision" : "") . " FROM {$recentchanges},{$page} " . ($uid ? "LEFT OUTER JOIN {$watchlist} ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : "") . "WHERE rc_timestamp > '{$cutoff}' {$hidem} AND rc_this_oldid=page_latest " . "ORDER BY rc_timestamp DESC LIMIT {$limit}";
    } else {
        $sql2 = "SELECT {$recentchanges}.*" . ($uid ? ",wl_user,wl_notificationtimestamp,wl_lastvisitedrevision" : "") . " FROM {$recentchanges} " . ($uid ? "LEFT OUTER JOIN {$watchlist} ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : "") . "WHERE rc_timestamp > '{$cutoff}' {$hidem} " . "ORDER BY rc_timestamp DESC LIMIT {$limit}";
    }
    $res = $dbr->query($sql2, $fname);
    // Fetch results, prepare a batch link existence check query
    $rows = array();
    $batch = new LinkBatch();
    while ($row = $dbr->fetchObject($res)) {
        $rows[] = $row;
        // User page link
        $title = Title::makeTitleSafe(NS_USER, $row->rc_user_text);
        $batch->addObj($title);
        // User talk
        $title = Title::makeTitleSafe(NS_USER_TALK, $row->rc_user_text);
        $batch->addObj($title);
    }
    $dbr->freeResult($res);
    // Run existence checks
    $batch->execute($wgLinkCache);
    if ($feedFormat) {
        rcOutputFeed($rows, $feedFormat, $limit, $hideminor, $lastmod);
    } else {
        # Web output...
        // Output header
        if (!$specialPage->including()) {
            $wgOut->addWikiText(wfMsgForContent("recentchangestext"));
            // Dump everything here
            $nondefaults = array();
            wfAppendToArrayIfNotDefault('days', $days, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('limit', $limit, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('hideminor', $hideminor, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('hidebots', $hidebots, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('hideliu', $hideliu, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('hidepatrolled', $hidepatrolled, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('from', $from, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('namespace', $namespace, $defaults, $nondefaults);
            wfAppendToArrayIfNotDefault('invert', $invert, $defaults, $nondefaults);
            // Add end of the texts
            $wgOut->addHTML('<div class="rcoptions">' . rcOptionsPanel($defaults, $nondefaults));
            $wgOut->addHTML(rcNamespaceForm($namespace, $invert, $nondefaults) . '</div>');
        }
        // And now for the content
        $sk = $wgUser->getSkin();
        $wgOut->setSyndicated(true);
        $list =& new ChangesList($sk);
        $s = $list->beginRecentChangesList();
        $counter = 1;
        foreach ($rows as $obj) {
            if ($limit == 0) {
                break;
            }
            if (!($hideminor && $obj->rc_minor) && !($hidepatrolled && $obj->rc_patrolled)) {
                $rc = RecentChange::newFromRow($obj);
                $rc->counter = $counter++;
                if ($wgShowUpdatedMarker && $wgUser->getOption('showupdated') && !empty($obj->wl_notificationtimestamp) && $obj->rc_timestamp >= $obj->wl_notificationtimestamp) {
                    $rc->notificationtimestamp = true;
                } else {
                    $rc->notificationtimestamp = false;
                }
                if ($wgRCShowWatchingUsers && $wgUser->getOption('shownumberswatching')) {
                    $sql3 = "SELECT COUNT(*) AS n FROM {$watchlist} WHERE wl_title='" . $dbr->strencode($obj->rc_title) . "' AND wl_namespace={$obj->rc_namespace}";
                    $res3 = $dbr->query($sql3, 'wfSpecialRecentChanges');
                    $x = $dbr->fetchObject($res3);
                    $rc->numberofWatchingusers = $x->n;
                } else {
                    $rc->numberofWatchingusers = 0;
                }
                $rc->lastvisitedrevision = $uid ? $obj->wl_lastvisitedrevision : 0;
                $s .= $list->recentChangesLine($rc, !empty($obj->wl_user));
                --$limit;
            }
        }
        $s .= $list->endRecentChangesList();
        $wgOut->addHTML($s);
    }
}
 /**
  * Build and output the actual changes list.
  *
  * @param array $rows Database rows
  * @param FormOptions $opts
  */
 public function outputChangesList($rows, $opts)
 {
     $limit = $opts['limit'];
     $showWatcherCount = $this->getConfig()->get('RCShowWatchingUsers') && $this->getUser()->getOption('shownumberswatching');
     $watcherCache = array();
     $dbr = $this->getDB();
     $counter = 1;
     $list = ChangesList::newFromContext($this->getContext());
     $list->initChangesListRows($rows);
     $rclistOutput = $list->beginRecentChangesList();
     foreach ($rows as $obj) {
         if ($limit == 0) {
             break;
         }
         $rc = RecentChange::newFromRow($obj);
         $rc->counter = $counter++;
         # Check if the page has been updated since the last visit
         if ($this->getConfig()->get('ShowUpdatedMarker') && !empty($obj->wl_notificationtimestamp)) {
             $rc->notificationtimestamp = $obj->rc_timestamp >= $obj->wl_notificationtimestamp;
         } else {
             $rc->notificationtimestamp = false;
             // Default
         }
         # Check the number of users watching the page
         $rc->numberofWatchingusers = 0;
         // Default
         if ($showWatcherCount && $obj->rc_namespace >= 0) {
             if (!isset($watcherCache[$obj->rc_namespace][$obj->rc_title])) {
                 $watcherCache[$obj->rc_namespace][$obj->rc_title] = $dbr->selectField('watchlist', 'COUNT(*)', array('wl_namespace' => $obj->rc_namespace, 'wl_title' => $obj->rc_title), __METHOD__ . '-watchers');
             }
             $rc->numberofWatchingusers = $watcherCache[$obj->rc_namespace][$obj->rc_title];
         }
         $changeLine = $list->recentChangesLine($rc, !empty($obj->wl_user), $counter);
         if ($changeLine !== false) {
             $rclistOutput .= $changeLine;
             --$limit;
         }
     }
     $rclistOutput .= $list->endRecentChangesList();
     if ($rows->numRows() === 0) {
         $this->getOutput()->addHtml('<div class="mw-changeslist-empty">' . $this->msg('recentchanges-noresult')->parse() . '</div>');
         if (!$this->including()) {
             $this->getOutput()->setStatusCode(404);
         }
     } else {
         $this->getOutput()->addHTML($rclistOutput);
     }
 }
/**
 * Entrypoint
 * @param string $par parent page we will look at
 */
function wfSpecialRecentchangeslinked($par = NULL)
{
    global $wgUser, $wgOut, $wgLang, $wgContLang, $wgRequest, $wgTitle;
    $fname = 'wfSpecialRecentchangeslinked';
    $days = $wgRequest->getInt('days');
    $target = isset($par) ? $par : $wgRequest->getText('target');
    $hideminor = $wgRequest->getBool('hideminor') ? 1 : 0;
    $wgOut->setPagetitle(wfMsg('recentchangeslinked'));
    $sk = $wgUser->getSkin();
    if (is_null($target)) {
        $wgOut->errorpage('notargettitle', 'notargettext');
        return;
    }
    $nt = Title::newFromURL($target);
    if (!$nt) {
        $wgOut->errorpage('notargettitle', 'notargettext');
        return;
    }
    $id = $nt->getArticleId();
    $wgOut->setPageTitle(wfMsg('recentchangeslinked-title', $nt->getPrefixedText()));
    $wgOut->setSyndicated();
    $wgOut->setFeedAppendQuery("target=" . urlencode($target));
    if (!$days) {
        $days = (int) $wgUser->getOption('rcdays', 7);
    }
    list($limit, ) = wfCheckLimits(100, 'rclimit');
    $dbr = wfGetDB(DB_SLAVE, 'recentchangeslinked');
    $cutoff = $dbr->timestamp(time() - $days * 86400);
    $hideminor = $hideminor ? 1 : 0;
    if ($hideminor) {
        $mlink = $sk->makeKnownLink($wgContLang->specialPage('Recentchangeslinked'), wfMsg('show'), 'target=' . htmlspecialchars($nt->getPrefixedURL()) . "&days={$days}&limit={$limit}&hideminor=0");
    } else {
        $mlink = $sk->makeKnownLink($wgContLang->specialPage("Recentchangeslinked"), wfMsg("hide"), "target=" . htmlspecialchars($nt->getPrefixedURL()) . "&days={$days}&limit={$limit}&hideminor=1");
    }
    if ($hideminor) {
        $cmq = 'AND rc_minor=0';
    } else {
        $cmq = '';
    }
    list($recentchanges, $categorylinks, $pagelinks, $watchlist) = $dbr->tableNamesN('recentchanges', 'categorylinks', 'pagelinks', "watchlist");
    $uid = $wgUser->getID();
    $GROUPBY = "\n\tGROUP BY rc_cur_id,rc_namespace,rc_title,\n\t\trc_user,rc_comment,rc_user_text,rc_timestamp,rc_minor,rc_deleted,\n\t\trc_new, rc_id, rc_this_oldid, rc_last_oldid, rc_bot, rc_patrolled, rc_type, rc_old_len, rc_new_len\n" . ($uid ? ",wl_user" : "") . "\n\t\tORDER BY rc_timestamp DESC\n\tLIMIT {$limit}";
    // If target is a Category, use categorylinks and invert from and to
    if ($nt->getNamespace() == NS_CATEGORY) {
        $catkey = $dbr->addQuotes($nt->getDBkey());
        $sql = "SELECT /* wfSpecialRecentchangeslinked */\n\t\t\t\trc_id,\n\t\t\t\trc_cur_id,\n\t\t\t\trc_namespace,\n\t\t\t\trc_title,\n\t\t\t\trc_this_oldid,\n\t\t\t\trc_last_oldid,\n\t\t\t\trc_user,\n\t\t\t\trc_comment,\n\t\t\t\trc_user_text,\n\t\t\t\trc_timestamp,\n\t\t\t\trc_minor,\n\t\t\t\trc_bot,\n\t\t\t\trc_new,\n\t\t\t\trc_patrolled,\n\t\t\t\trc_type,\n\t\t\t\trc_old_len,\n\t\t\t\trc_new_len,\n\t\t\t\trc_deleted\n" . ($uid ? ",wl_user" : "") . "\n\t    FROM {$categorylinks}, {$recentchanges}\n" . ($uid ? "LEFT OUTER JOIN {$watchlist} ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : "") . "\n\t   WHERE rc_timestamp > '{$cutoff}'\n\t     {$cmq}\n\t     AND cl_from=rc_cur_id\n\t     AND cl_to={$catkey}\n{$GROUPBY}\n ";
    } else {
        $sql = "SELECT /* wfSpecialRecentchangeslinked */\n\t\t\trc_id,\n\t\t\trc_cur_id,\n\t\t\trc_namespace,\n\t\t\trc_title,\n\t\t\trc_user,\n\t\t\trc_comment,\n\t\t\trc_user_text,\n\t\t\trc_this_oldid,\n\t\t\trc_last_oldid,\n\t\t\trc_timestamp,\n\t\t\trc_minor,\n\t\t\trc_bot,\n\t\t\trc_new,\n\t\t\trc_patrolled,\n\t\t\trc_type,\n\t\t\trc_old_len,\n\t\t\trc_new_len,\n\t\t\trc_deleted\n" . ($uid ? ",wl_user" : "") . "\n   FROM {$pagelinks}, {$recentchanges}\n" . ($uid ? " LEFT OUTER JOIN {$watchlist} ON wl_user={$uid} AND wl_title=rc_title AND wl_namespace=rc_namespace " : "") . "\n   WHERE rc_timestamp > '{$cutoff}'\n\t{$cmq}\n     AND pl_namespace=rc_namespace\n     AND pl_title=rc_title\n     AND pl_from={$id}\n{$GROUPBY}\n";
    }
    $res = $dbr->query($sql, $fname);
    $wgOut->addHTML("&lt; " . $sk->makeLinkObj($nt, "", "redirect=no") . "<br />\n");
    $note = wfMsgExt("rcnote", array('parseinline'), $limit, $days, $wgLang->timeAndDate(wfTimestampNow(), true));
    $wgOut->addHTML("<hr />\n{$note}\n<br />");
    $note = rcDayLimitlinks($days, $limit, "Recentchangeslinked", "target=" . $nt->getPrefixedURL() . "&hideminor={$hideminor}", false, $mlink);
    $wgOut->addHTML($note . "\n");
    $list = ChangesList::newFromUser($wgUser);
    $s = $list->beginRecentChangesList();
    $count = $dbr->numRows($res);
    $rchanges = array();
    if ($count) {
        $counter = 1;
        while ($limit) {
            if (0 == $count) {
                break;
            }
            $obj = $dbr->fetchObject($res);
            --$count;
            $rc = RecentChange::newFromRow($obj);
            $rc->counter = $counter++;
            $s .= $list->recentChangesLine($rc, !empty($obj->wl_user));
            --$limit;
            $rchanges[] = $obj;
        }
    } else {
        $wgOut->addWikiMsg('recentchangeslinked-noresult');
    }
    $s .= $list->endRecentChangesList();
    $dbr->freeResult($res);
    $wgOut->addHTML($s);
    global $wgSitename, $wgFeedClasses, $wgContLanguageCode;
    $feedFormat = $wgRequest->getVal('feed');
    if ($feedFormat && isset($wgFeedClasses[$feedFormat])) {
        $feedTitle = $wgSitename . ' - ' . wfMsgForContent('recentchangeslinked-title', $nt->getPrefixedText()) . ' [' . $wgContLanguageCode . ']';
        $feed = new $wgFeedClasses[$feedFormat]($feedTitle, htmlspecialchars(wfMsgForContent('recentchangeslinked')), $wgTitle->getFullUrl());
        require_once "SpecialRecentchanges.php";
        $wgOut->disable();
        rcDoOutputFeed($rchanges, $feed);
    }
}
 /**
  * Extracts from a single sql row the data needed to describe one recent change.
  *
  * @param stdClass $row The row from which to extract the data.
  * @return array An array mapping strings (descriptors) to their respective string values.
  * @access public
  */
 public function extractRowInfo($row)
 {
     /* Determine the title of the page that has been changed. */
     $title = Title::makeTitle($row->rc_namespace, $row->rc_title);
     $user = $this->getUser();
     /* Our output data. */
     $vals = array();
     $type = intval($row->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) {
         if ($type === RC_LOG && $row->rc_deleted & LogPage::DELETED_ACTION) {
             $vals['actionhidden'] = true;
             $anyHidden = true;
         }
         if ($type !== RC_LOG || LogEventsList::userCanBitfield($row->rc_deleted, LogPage::DELETED_ACTION, $user)) {
             if ($this->fld_title) {
                 ApiQueryBase::addTitleInfo($vals, $title);
             }
             if ($this->fld_ids) {
                 $vals['pageid'] = intval($row->rc_cur_id);
                 $vals['revid'] = intval($row->rc_this_oldid);
                 $vals['old_revid'] = intval($row->rc_last_oldid);
             }
         }
     }
     if ($this->fld_ids) {
         $vals['rcid'] = intval($row->rc_id);
     }
     /* Add user data and 'anon' flag, if user is anonymous. */
     if ($this->fld_user || $this->fld_userid) {
         if ($row->rc_deleted & Revision::DELETED_USER) {
             $vals['userhidden'] = true;
             $anyHidden = true;
         }
         if (Revision::userCanBitfield($row->rc_deleted, Revision::DELETED_USER, $user)) {
             if ($this->fld_user) {
                 $vals['user'] = $row->rc_user_text;
             }
             if ($this->fld_userid) {
                 $vals['userid'] = $row->rc_user;
             }
             if (!$row->rc_user) {
                 $vals['anon'] = true;
             }
         }
     }
     /* Add flags, such as new, minor, bot. */
     if ($this->fld_flags) {
         $vals['bot'] = (bool) $row->rc_bot;
         $vals['new'] = $row->rc_type == RC_NEW;
         $vals['minor'] = (bool) $row->rc_minor;
     }
     /* Add sizes of each revision. (Only available on 1.10+) */
     if ($this->fld_sizes) {
         $vals['oldlen'] = intval($row->rc_old_len);
         $vals['newlen'] = intval($row->rc_new_len);
     }
     /* Add the timestamp. */
     if ($this->fld_timestamp) {
         $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $row->rc_timestamp);
     }
     /* Add edit summary / log summary. */
     if ($this->fld_comment || $this->fld_parsedcomment) {
         if ($row->rc_deleted & Revision::DELETED_COMMENT) {
             $vals['commenthidden'] = true;
             $anyHidden = true;
         }
         if (Revision::userCanBitfield($row->rc_deleted, Revision::DELETED_COMMENT, $user)) {
             if ($this->fld_comment && isset($row->rc_comment)) {
                 $vals['comment'] = $row->rc_comment;
             }
             if ($this->fld_parsedcomment && isset($row->rc_comment)) {
                 $vals['parsedcomment'] = Linker::formatComment($row->rc_comment, $title);
             }
         }
     }
     if ($this->fld_redirect) {
         $vals['redirect'] = (bool) $row->page_is_redirect;
     }
     /* Add the patrolled flag */
     if ($this->fld_patrolled) {
         $vals['patrolled'] = $row->rc_patrolled == 1;
         $vals['unpatrolled'] = ChangesList::isUnpatrolled($row, $user);
     }
     if ($this->fld_loginfo && $row->rc_type == RC_LOG) {
         if ($row->rc_deleted & LogPage::DELETED_ACTION) {
             $vals['actionhidden'] = true;
             $anyHidden = true;
         }
         if (LogEventsList::userCanBitfield($row->rc_deleted, LogPage::DELETED_ACTION, $user)) {
             $vals['logid'] = intval($row->rc_logid);
             $vals['logtype'] = $row->rc_log_type;
             $vals['logaction'] = $row->rc_log_action;
             $vals['logparams'] = LogFormatter::newFromRow($row)->formatParametersForApi();
         }
     }
     if ($this->fld_tags) {
         if ($row->ts_tags) {
             $tags = explode(',', $row->ts_tags);
             ApiResult::setIndexedTagName($tags, 'tag');
             $vals['tags'] = $tags;
         } else {
             $vals['tags'] = array();
         }
     }
     if ($this->fld_sha1 && $row->rev_sha1 !== null) {
         if ($row->rev_deleted & Revision::DELETED_TEXT) {
             $vals['sha1hidden'] = true;
             $anyHidden = true;
         }
         if (Revision::userCanBitfield($row->rev_deleted, Revision::DELETED_TEXT, $user)) {
             if ($row->rev_sha1 !== '') {
                 $vals['sha1'] = wfBaseConvert($row->rev_sha1, 36, 16, 40);
             } else {
                 $vals['sha1'] = '';
             }
         }
     }
     if (!is_null($this->token)) {
         $tokenFunctions = $this->getTokenFunctions();
         foreach ($this->token as $t) {
             $val = call_user_func($tokenFunctions[$t], $row->rc_cur_id, $title, RecentChange::newFromRow($row));
             if ($val === false) {
                 $this->setWarning("Action '{$t}' is not allowed for the current user");
             } else {
                 $vals[$t . 'token'] = $val;
             }
         }
     }
     if ($anyHidden && $row->rc_deleted & Revision::DELETED_RESTRICTED) {
         $vals['suppressed'] = true;
     }
     return $vals;
 }
    /**
     * 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') . ':&nbsp;&nbsp;';
        }
        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) . '&nbsp;' . Xml::label($tag, 'collaborative-watchlist-filtertag-' . $tag) . '&nbsp;';
        }
        if (count($tagsAndInfo) > 0) {
            $form .= '<br />';
        }
        $form .= Xml::checkLabel(wfMsg('collabwatchlistinverttags'), 'invertTags', 'nsinvertTags', $invertTags) . '<br />';
        $form .= CollabWatchlistChangesList::collabWatchlistSelector($listIdsAndNames, $collabWatchlist, '', 'collabwatchlist', wfMsg('collabwatchlist')) . '&nbsp;';
        $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);
    }