/** * Set the log reader to return only entries of the given type. * Type restrictions enforced here * * @param string|array $types Log types ('upload', 'delete', etc); * empty string means no restriction */ private function limitType($types) { global $wgLogRestrictions; $user = $this->getUser(); // If $types is not an array, make it an array $types = $types === '' ? array() : (array) $types; // Don't even show header for private logs; don't recognize it... $needReindex = false; foreach ($types as $type) { if (isset($wgLogRestrictions[$type]) && !$user->isAllowed($wgLogRestrictions[$type])) { $needReindex = true; $types = array_diff($types, array($type)); } } if ($needReindex) { // Lots of this code makes assumptions that // the first entry in the array is $types[0]. $types = array_values($types); } $this->types = $types; // Don't show private logs to unprivileged users. // Also, only show them upon specific request to avoid suprises. $audience = $types ? 'user' : 'public'; $hideLogs = LogEventsList::getExcludeClause($this->mDb, $audience, $user); if ($hideLogs !== false) { $this->mConds[] = $hideLogs; } if (count($types)) { $this->mConds['log_type'] = $types; // Set typeCGI; used in url param for paging if (count($types) == 1) { $this->typeCGI = $types[0]; } } }
public function execute() { $params = $this->extractRequestParams(); $db = $this->getDB(); $prop = array_flip($params['prop']); $this->fld_ids = isset($prop['ids']); $this->fld_title = isset($prop['title']); $this->fld_type = isset($prop['type']); $this->fld_action = isset($prop['action']); $this->fld_user = isset($prop['user']); $this->fld_userid = isset($prop['userid']); $this->fld_timestamp = isset($prop['timestamp']); $this->fld_comment = isset($prop['comment']); $this->fld_parsedcomment = isset($prop['parsedcomment']); $this->fld_details = isset($prop['details']); $this->fld_tags = isset($prop['tags']); $hideLogs = LogEventsList::getExcludeClause($db); if ($hideLogs !== false) { $this->addWhere($hideLogs); } // Order is significant here $this->addTables(array('logging', 'user', 'page')); $this->addOption('STRAIGHT_JOIN'); $this->addJoinConds(array('user' => array('JOIN', 'user_id=log_user'), 'page' => array('LEFT JOIN', array('log_namespace=page_namespace', 'log_title=page_title')))); $index = array('logging' => 'times'); // default, may change $this->addFields(array('log_type', 'log_action', 'log_timestamp', 'log_deleted')); $this->addFieldsIf(array('log_id', 'page_id'), $this->fld_ids); $this->addFieldsIf(array('log_user', 'user_name'), $this->fld_user); $this->addFieldsIf('user_id', $this->fld_userid); $this->addFieldsIf(array('log_namespace', 'log_title'), $this->fld_title || $this->fld_parsedcomment); $this->addFieldsIf('log_comment', $this->fld_comment || $this->fld_parsedcomment); $this->addFieldsIf('log_params', $this->fld_details); if ($this->fld_tags) { $this->addTables('tag_summary'); $this->addJoinConds(array('tag_summary' => array('LEFT JOIN', 'log_id=ts_log_id'))); $this->addFields('ts_tags'); } if (!is_null($params['tag'])) { $this->addTables('change_tag'); $this->addJoinConds(array('change_tag' => array('INNER JOIN', array('log_id=ct_log_id')))); $this->addWhereFld('ct_tag', $params['tag']); global $wgOldChangeTagsIndex; $index['change_tag'] = $wgOldChangeTagsIndex ? 'ct_tag' : 'change_tag_tag_id'; } if (!is_null($params['action'])) { list($type, $action) = explode('/', $params['action']); $this->addWhereFld('log_type', $type); $this->addWhereFld('log_action', $action); } elseif (!is_null($params['type'])) { $this->addWhereFld('log_type', $params['type']); $index['logging'] = 'type_time'; } $this->addTimestampWhereRange('log_timestamp', $params['dir'], $params['start'], $params['end']); $limit = $params['limit']; $this->addOption('LIMIT', $limit + 1); $user = $params['user']; if (!is_null($user)) { $userid = User::idFromName($user); if (!$userid) { $this->dieUsage("User name {$user} not found", 'param_user'); } $this->addWhereFld('log_user', $userid); $index['logging'] = 'user_time'; } $title = $params['title']; if (!is_null($title)) { $titleObj = Title::newFromText($title); if (is_null($titleObj)) { $this->dieUsage("Bad title value '{$title}'", 'param_title'); } $this->addWhereFld('log_namespace', $titleObj->getNamespace()); $this->addWhereFld('log_title', $titleObj->getDBkey()); // Use the title index in preference to the user index if there is a conflict $index['logging'] = is_null($user) ? 'page_time' : array('page_time', 'user_time'); } $prefix = $params['prefix']; if (!is_null($prefix)) { global $wgMiserMode; if ($wgMiserMode) { $this->dieUsage('Prefix search disabled in Miser Mode', 'prefixsearchdisabled'); } $title = Title::newFromText($prefix); if (is_null($title)) { $this->dieUsage("Bad title value '{$prefix}'", 'param_prefix'); } $this->addWhereFld('log_namespace', $title->getNamespace()); $this->addWhere('log_title ' . $db->buildLike($title->getDBkey(), $db->anyString())); } $this->addOption('USE INDEX', $index); // Paranoia: avoid brute force searches (bug 17342) if (!is_null($title)) { $this->addWhere($db->bitAnd('log_deleted', LogPage::DELETED_ACTION) . ' = 0'); } if (!is_null($user)) { $this->addWhere($db->bitAnd('log_deleted', LogPage::DELETED_USER) . ' = 0'); } $count = 0; $res = $this->select(__METHOD__); $result = $this->getResult(); foreach ($res as $row) { if (++$count > $limit) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->log_timestamp)); break; } $vals = $this->extractRowInfo($row); if (!$vals) { continue; } $fit = $result->addValue(array('query', $this->getModuleName()), null, $vals); if (!$fit) { $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->log_timestamp)); break; } } $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'item'); }
/** * @param string $cond * @throws MWException * @throws Exception */ protected function dumpFrom($cond = '') { wfProfileIn(__METHOD__); # For logging dumps... if ($this->history & self::LOGS) { $where = array('user_id = log_user'); # Hide private logs $hideLogs = LogEventsList::getExcludeClause($this->db); if ($hideLogs) { $where[] = $hideLogs; } # Add on any caller specified conditions if ($cond) { $where[] = $cond; } # Get logging table name for logging.* clause $logging = $this->db->tableName('logging'); if ($this->buffer == WikiExporter::STREAM) { $prev = $this->db->bufferResults(false); } $wrapper = null; // Assuring $wrapper is not undefined, if exception occurs early try { $result = $this->db->select(array('logging', 'user'), array("{$logging}.*", 'user_name'), $where, __METHOD__, array('ORDER BY' => 'log_id', 'USE INDEX' => array('logging' => 'PRIMARY'))); $wrapper = $this->db->resultObject($result); $this->outputLogStream($wrapper); if ($this->buffer == WikiExporter::STREAM) { $this->db->bufferResults($prev); } } catch (Exception $e) { // Throwing the exception does not reliably free the resultset, and // would also leave the connection in unbuffered mode. // Freeing result try { if ($wrapper) { $wrapper->free(); } } catch (Exception $e2) { // Already in panic mode -> ignoring $e2 as $e has // higher priority } // Putting database back in previous buffer mode try { if ($this->buffer == WikiExporter::STREAM) { $this->db->bufferResults($prev); } } catch (Exception $e2) { // Already in panic mode -> ignoring $e2 as $e has // higher priority } // Inform caller about problem wfProfileOut(__METHOD__); throw $e; } # For page dumps... } else { $tables = array('page', 'revision'); $opts = array('ORDER BY' => 'page_id ASC'); $opts['USE INDEX'] = array(); $join = array(); if (is_array($this->history)) { # Time offset/limit for all pages/history... $revJoin = 'page_id=rev_page'; # Set time order if ($this->history['dir'] == 'asc') { $op = '>'; $opts['ORDER BY'] = 'rev_timestamp ASC'; } else { $op = '<'; $opts['ORDER BY'] = 'rev_timestamp DESC'; } # Set offset if (!empty($this->history['offset'])) { $revJoin .= " AND rev_timestamp {$op} " . $this->db->addQuotes($this->db->timestamp($this->history['offset'])); } $join['revision'] = array('INNER JOIN', $revJoin); # Set query limit if (!empty($this->history['limit'])) { $opts['LIMIT'] = intval($this->history['limit']); } } elseif ($this->history & WikiExporter::FULL) { # Full history dumps... $join['revision'] = array('INNER JOIN', 'page_id=rev_page'); } elseif ($this->history & WikiExporter::CURRENT) { # Latest revision dumps... if ($this->list_authors && $cond != '') { // List authors, if so desired $this->do_list_authors($cond); } $join['revision'] = array('INNER JOIN', 'page_id=rev_page AND page_latest=rev_id'); } elseif ($this->history & WikiExporter::STABLE) { # "Stable" revision dumps... # Default JOIN, to be overridden... $join['revision'] = array('INNER JOIN', 'page_id=rev_page AND page_latest=rev_id'); # One, and only one hook should set this, and return false if (wfRunHooks('WikiExporter::dumpStableQuery', array(&$tables, &$opts, &$join))) { wfProfileOut(__METHOD__); throw new MWException(__METHOD__ . " given invalid history dump type."); } } elseif ($this->history & WikiExporter::RANGE) { # Dump of revisions within a specified range $join['revision'] = array('INNER JOIN', 'page_id=rev_page'); $opts['ORDER BY'] = array('rev_page ASC', 'rev_id ASC'); } else { # Unknown history specification parameter? wfProfileOut(__METHOD__); throw new MWException(__METHOD__ . " given invalid history dump type."); } # Query optimization hacks if ($cond == '') { $opts[] = 'STRAIGHT_JOIN'; $opts['USE INDEX']['page'] = 'PRIMARY'; } # Build text join options if ($this->text != WikiExporter::STUB) { // 1-pass $tables[] = 'text'; $join['text'] = array('INNER JOIN', 'rev_text_id=old_id'); } if ($this->buffer == WikiExporter::STREAM) { $prev = $this->db->bufferResults(false); } $wrapper = null; // Assuring $wrapper is not undefined, if exception occurs early try { wfRunHooks('ModifyExportQuery', array($this->db, &$tables, &$cond, &$opts, &$join)); # Do the query! $result = $this->db->select($tables, '*', $cond, __METHOD__, $opts, $join); $wrapper = $this->db->resultObject($result); # Output dump results $this->outputPageStream($wrapper); if ($this->buffer == WikiExporter::STREAM) { $this->db->bufferResults($prev); } } catch (Exception $e) { // Throwing the exception does not reliably free the resultset, and // would also leave the connection in unbuffered mode. // Freeing result try { if ($wrapper) { $wrapper->free(); } } catch (Exception $e2) { // Already in panic mode -> ignoring $e2 as $e has // higher priority } // Putting database back in previous buffer mode try { if ($this->buffer == WikiExporter::STREAM) { $this->db->bufferResults($prev); } } catch (Exception $e2) { // Already in panic mode -> ignoring $e2 as $e has // higher priority } // Inform caller about problem throw $e; } } wfProfileOut(__METHOD__); }
protected function dumpFrom($cond = '') { wfProfileIn(__METHOD__); # For logging dumps... if ($this->history & self::LOGS) { if ($this->buffer == WikiExporter::STREAM) { $prev = $this->db->bufferResults(false); } $where = array('user_id = log_user'); # Hide private logs $hideLogs = LogEventsList::getExcludeClause($this->db); if ($hideLogs) { $where[] = $hideLogs; } # Add on any caller specified conditions if ($cond) { $where[] = $cond; } # Get logging table name for logging.* clause $logging = $this->db->tableName('logging'); $result = $this->db->select(array('logging', 'user'), array("{$logging}.*", 'user_name'), $where, __METHOD__, array('ORDER BY' => 'log_id', 'USE INDEX' => array('logging' => 'PRIMARY'))); $wrapper = $this->db->resultObject($result); if ($this->buffer == WikiExporter::STREAM) { $this->db->bufferResults($prev); } $this->outputLogStream($wrapper); # For page dumps... } else { $tables = array('page', 'revision'); $opts = array('ORDER BY' => 'page_id ASC'); $opts['USE INDEX'] = array(); $join = array(); # Full history dumps... if ($this->history & WikiExporter::FULL) { $join['revision'] = array('INNER JOIN', 'page_id=rev_page'); # Latest revision dumps... } elseif ($this->history & WikiExporter::CURRENT) { if ($this->list_authors && $cond != '') { // List authors, if so desired list($page, $revision) = $this->db->tableNamesN('page', 'revision'); $this->do_list_authors($page, $revision, $cond); } $join['revision'] = array('INNER JOIN', 'page_id=rev_page AND page_latest=rev_id'); # "Stable" revision dumps... } elseif ($this->history & WikiExporter::STABLE) { # Default JOIN, to be overridden... $join['revision'] = array('INNER JOIN', 'page_id=rev_page AND page_latest=rev_id'); # One, and only one hook should set this, and return false if (wfRunHooks('WikiExporter::dumpStableQuery', array(&$tables, &$opts, &$join))) { wfProfileOut(__METHOD__); return new WikiError(__METHOD__ . " given invalid history dump type."); } # Time offset/limit for all pages/history... } elseif (is_array($this->history)) { $revJoin = 'page_id=rev_page'; # Set time order if ($this->history['dir'] == 'asc') { $op = '>'; $opts['ORDER BY'] = 'rev_timestamp ASC'; } else { $op = '<'; $opts['ORDER BY'] = 'rev_timestamp DESC'; } # Set offset if (!empty($this->history['offset'])) { $revJoin .= " AND rev_timestamp {$op} " . $this->db->addQuotes($this->db->timestamp($this->history['offset'])); } $join['revision'] = array('INNER JOIN', $revJoin); # Set query limit if (!empty($this->history['limit'])) { $opts['LIMIT'] = intval($this->history['limit']); } # Uknown history specification parameter? } else { wfProfileOut(__METHOD__); return new WikiError(__METHOD__ . " given invalid history dump type."); } # Query optimization hacks if ($cond == '') { $opts[] = 'STRAIGHT_JOIN'; $opts['USE INDEX']['page'] = 'PRIMARY'; } # Build text join options if ($this->text != WikiExporter::STUB) { // 1-pass $tables[] = 'text'; $join['text'] = array('INNER JOIN', 'rev_text_id=old_id'); } if ($this->buffer == WikiExporter::STREAM) { $prev = $this->db->bufferResults(false); } # Do the query! $result = $this->db->select($tables, '*', $cond, __METHOD__, $opts, $join); $wrapper = $this->db->resultObject($result); # Output dump results $this->outputPageStream($wrapper); if ($this->list_authors) { $this->outputPageStream($wrapper); } if ($this->buffer == WikiExporter::STREAM) { $this->db->bufferResults($prev); } } wfProfileOut(__METHOD__); }
public function getCacheMode($params) { if ($this->userCanSeeRevDel()) { return 'private'; } if (!is_null($params['prop']) && in_array('parsedcomment', $params['prop'])) { // formatComment() calls wfMessage() among other things return 'anon-public-user-private'; } elseif (LogEventsList::getExcludeClause($this->getDB(), 'user', $this->getUser()) === LogEventsList::getExcludeClause($this->getDB(), 'public')) { // Output can only contain public data. return 'public'; } else { return 'anon-public-user-private'; } }
public function execute() { $params = $this->extractRequestParams(); $db = $this->getDB(); $prop = $params['prop']; $this->fld_ids = in_array('ids', $prop); $this->fld_title = in_array('title', $prop); $this->fld_type = in_array('type', $prop); $this->fld_user = in_array('user', $prop); $this->fld_timestamp = in_array('timestamp', $prop); $this->fld_comment = in_array('comment', $prop); $this->fld_details = in_array('details', $prop); list($tbl_logging, $tbl_page, $tbl_user) = $db->tableNamesN('logging', 'page', 'user'); $hideLogs = LogEventsList::getExcludeClause($db); if ($hideLogs !== false) { $this->addWhere($hideLogs); } // Order is significant here $this->addTables(array('logging', 'user', 'page')); $this->addOption('STRAIGHT_JOIN'); $this->addJoinConds(array('user' => array('JOIN', 'user_id=log_user'), 'page' => array('LEFT JOIN', array('log_namespace=page_namespace', 'log_title=page_title')))); $index = 'times'; // default, may change $this->addFields(array('log_type', 'log_action', 'log_timestamp', 'log_deleted')); $this->addFieldsIf('log_id', $this->fld_ids); $this->addFieldsIf('page_id', $this->fld_ids); $this->addFieldsIf('log_user', $this->fld_user); $this->addFieldsIf('user_name', $this->fld_user); $this->addFieldsIf('log_namespace', $this->fld_title); $this->addFieldsIf('log_title', $this->fld_title); $this->addFieldsIf('log_comment', $this->fld_comment); $this->addFieldsIf('log_params', $this->fld_details); if (!is_null($params['type'])) { $this->addWhereFld('log_type', $params['type']); $index = 'type_time'; } $this->addWhereRange('log_timestamp', $params['dir'], $params['start'], $params['end']); $limit = $params['limit']; $this->addOption('LIMIT', $limit + 1); $user = $params['user']; if (!is_null($user)) { $userid = User::idFromName($user); if (!$userid) { $this->dieUsage("User name {$user} not found", 'param_user'); } $this->addWhereFld('log_user', $userid); $index = 'user_time'; } $title = $params['title']; if (!is_null($title)) { $titleObj = Title::newFromText($title); if (is_null($titleObj)) { $this->dieUsage("Bad title value '{$title}'", 'param_title'); } $this->addWhereFld('log_namespace', $titleObj->getNamespace()); $this->addWhereFld('log_title', $titleObj->getDBkey()); // Use the title index in preference to the user index if there is a conflict $index = is_null($user) ? 'page_time' : array('page_time', 'user_time'); } $this->addOption('USE INDEX', array('logging' => $index)); // Paranoia: avoid brute force searches (bug 17342) if (!is_null($title)) { $this->addWhere('log_deleted & ' . LogPage::DELETED_ACTION . ' = 0'); } if (!is_null($user)) { $this->addWhere('log_deleted & ' . LogPage::DELETED_USER . ' = 0'); } $count = 0; $res = $this->select(__METHOD__); while ($row = $db->fetchObject($res)) { if (++$count > $limit) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->log_timestamp)); break; } $vals = $this->extractRowInfo($row); if (!$vals) { continue; } $fit = $this->getResult()->addValue(array('query', $this->getModuleName()), null, $vals); if (!$fit) { $this->setContinueEnumParameter('start', wfTimestamp(TS_ISO_8601, $row->log_timestamp)); break; } } $db->freeResult($res); $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), 'item'); }
/** * Set the log reader to return only entries of the given type. * Type restrictions enforced here * * @param $types String or array: Log types ('upload', 'delete', etc); * empty string means no restriction */ private function limitType($types) { global $wgLogRestrictions, $wgUser; // If $types is not an array, make it an array $types = $types === '' ? array() : (array) $types; // Don't even show header for private logs; don't recognize it... foreach ($types as $type) { if (isset($wgLogRestrictions[$type]) && !$wgUser->isAllowed($wgLogRestrictions[$type])) { $types = array_diff($types, array($type)); } } $this->types = $types; // Don't show private logs to unprivileged users. // Also, only show them upon specific request to avoid suprises. $audience = $types ? 'user' : 'public'; $hideLogs = LogEventsList::getExcludeClause($this->mDb, $audience); if ($hideLogs !== false) { $this->mConds[] = $hideLogs; } if (count($types)) { $this->mConds['log_type'] = $types; // Set typeCGI; used in url param for paging if (count($types) == 1) { $this->typeCGI = $types[0]; } } }
/** * Set the log reader to return only entries of the given type. * Type restrictions enforced here * @param $type String: A log type ('upload', 'delete', etc) */ private function limitType($type) { global $wgLogRestrictions, $wgUser; // Don't even show header for private logs; don't recognize it... if (isset($wgLogRestrictions[$type]) && !$wgUser->isAllowed($wgLogRestrictions[$type])) { $type = ''; } // Don't show private logs to unpriviledged users $hideLogs = LogEventsList::getExcludeClause($this->mDb); if ($hideLogs !== false) { $this->mConds[] = $hideLogs; } if (!$type) { return false; } $this->type = $type; $this->mConds['log_type'] = $type; }
/** * Set the log reader to return only entries of the given type. * Type restrictions enforced here * @param $type String: A log type ('upload', 'delete', etc) */ private function limitType($type) { global $wgLogRestrictions, $wgUser; // Don't even show header for private logs; don't recognize it... if (isset($wgLogRestrictions[$type]) && !$wgUser->isAllowed($wgLogRestrictions[$type])) { $type = ''; } // Don't show private logs to unpriviledged users. // Also, only show them upon specific request to avoid suprises. $audience = $type ? 'user' : 'public'; $hideLogs = LogEventsList::getExcludeClause($this->mDb, $audience); if ($hideLogs !== false) { $this->mConds[] = $hideLogs; } if ($type) { $this->type = $type; $this->mConds['log_type'] = $type; } }
protected function dumpFrom($cond = '') { $fname = 'WikiExporter::dumpFrom'; wfProfileIn($fname); # For logs dumps... if ($this->history & self::LOGS) { $where = array('user_id = log_user'); # Hide private logs $where[] = LogEventsList::getExcludeClause($this->db); if ($cond) { $where[] = $cond; } $result = $this->db->select(array('logging', 'user'), '*', $where, $fname, array('ORDER BY' => 'log_id', 'USE INDEX' => array('logging' => 'PRIMARY'))); $wrapper = $this->db->resultObject($result); $this->outputLogStream($wrapper); # For page dumps... } else { list($page, $revision, $text) = $this->db->tableNamesN('page', 'revision', 'text'); $order = 'ORDER BY page_id'; $limit = ''; if ($this->history == WikiExporter::FULL) { $join = 'page_id=rev_page'; } elseif ($this->history == WikiExporter::CURRENT) { if ($this->list_authors && $cond != '') { // List authors, if so desired $this->do_list_authors($page, $revision, $cond); } $join = 'page_id=rev_page AND page_latest=rev_id'; } elseif (is_array($this->history)) { $join = 'page_id=rev_page'; if ($this->history['dir'] == 'asc') { $op = '>'; $order .= ', rev_timestamp'; } else { $op = '<'; $order .= ', rev_timestamp DESC'; } if (!empty($this->history['offset'])) { $join .= " AND rev_timestamp {$op} " . $this->db->addQuotes($this->db->timestamp($this->history['offset'])); } if (!empty($this->history['limit'])) { $limitNum = intval($this->history['limit']); if ($limitNum > 0) { $limit = "LIMIT {$limitNum}"; } } } else { wfProfileOut($fname); return new WikiError("{$fname} given invalid history dump type."); } $where = $cond == '' ? '' : "{$cond} AND"; if ($this->buffer == WikiExporter::STREAM) { $prev = $this->db->bufferResults(false); } if ($cond == '') { // Optimization hack for full-database dump $revindex = $pageindex = $this->db->useIndexClause("PRIMARY"); $straight = ' /*! STRAIGHT_JOIN */ '; } else { $pageindex = ''; $revindex = ''; $straight = ''; } if ($this->text == WikiExporter::STUB) { $sql = "SELECT {$straight} * FROM\n\t\t\t\t\t{$page} {$pageindex},\n\t\t\t\t\t{$revision} {$revindex}\n\t\t\t\t\tWHERE {$where} {$join}\n\t\t\t\t\t{$order} {$limit}"; } else { $sql = "SELECT {$straight} * FROM\n\t\t\t\t\t{$page} {$pageindex},\n\t\t\t\t\t{$revision} {$revindex},\n\t\t\t\t\t{$text}\n\t\t\t\t\tWHERE {$where} {$join} AND rev_text_id=old_id\n\t\t\t\t\t{$order} {$limit}"; } $result = $this->db->query($sql, $fname); $wrapper = $this->db->resultObject($result); $this->outputPageStream($wrapper); if ($this->list_authors) { $this->outputPageStream($wrapper); } if ($this->buffer == WikiExporter::STREAM) { $this->db->bufferResults($prev); } } wfProfileOut($fname); }