/** * @dataProvider provideGetGroupsWithPermission * @covers User::getGroupsWithPermission */ public function testGetGroupsWithPermission($expected, $right) { $result = User::getGroupsWithPermission($right); sort($result); sort($expected); $this->assertEquals($expected, $result, "Groups with permission {$right}"); }
public function __construct($permission, $errors = array()) { global $wgLang; $this->permission = $permission; if (!count($errors)) { $groups = array_map(array('User', 'makeGroupLinkWiki'), User::getGroupsWithPermission($this->permission)); if ($groups) { $errors[] = array('badaccess-groups', $wgLang->commaList($groups), count($groups)); } else { $errors[] = array('badaccess-group0'); } } $this->errors = $errors; }
/** * @param string|null $permission A permission name or null if unknown * @param array $errors Error message keys or [key, param...] arrays; must not be empty if * $permission is null * @throws \InvalidArgumentException */ public function __construct($permission, $errors = []) { global $wgLang; if ($permission === null && !$errors) { throw new \InvalidArgumentException(__METHOD__ . ': $permission and $errors cannot both be empty'); } $this->permission = $permission; if (!count($errors)) { $groups = array_map(['User', 'makeGroupLinkWiki'], User::getGroupsWithPermission($this->permission)); if ($groups) { $errors[] = ['badaccess-groups', $wgLang->commaList($groups), count($groups)]; } else { $errors[] = ['badaccess-group0']; } } $this->errors = $errors; }
function getQueryInfo() { global $wgMiserMode; $conds = $jconds = array(); $tables = array('image'); if (!$this->showbots) { $tables[] = 'user_groups'; $conds[] = 'ug_group IS NULL'; $jconds['user_groups'] = array('LEFT JOIN', array('ug_group' => User::getGroupsWithPermission('bot'), 'ug_user = img_user')); } if (!$wgMiserMode && $this->like !== null) { $dbr = wfGetDB(DB_SLAVE); $likeObj = Title::newFromURL($this->like); if ($likeObj instanceof Title) { $like = $dbr->buildLike($dbr->anyString(), strtolower($likeObj->getDBkey()), $dbr->anyString()); $conds[] = "LOWER(img_name) {$like}"; } } $query = array('tables' => $tables, 'fields' => '*', 'join_conds' => $jconds, 'conds' => $conds); return $query; }
function getQueryInfo() { $opts = $this->opts; $conds = $jconds = []; $tables = ['image']; $fields = ['img_name', 'img_user', 'img_timestamp']; $options = []; if (!$opts->getValue('showbots')) { $groupsWithBotPermission = User::getGroupsWithPermission('bot'); if (count($groupsWithBotPermission)) { $tables[] = 'user_groups'; $conds[] = 'ug_group IS NULL'; $jconds['user_groups'] = ['LEFT JOIN', ['ug_group' => $groupsWithBotPermission, 'ug_user = img_user']]; } } if ($opts->getValue('hidepatrolled')) { $tables[] = 'recentchanges'; $conds['rc_type'] = RC_LOG; $conds['rc_log_type'] = 'upload'; $conds['rc_patrolled'] = 0; $conds['rc_namespace'] = NS_FILE; $jconds['recentchanges'] = ['INNER JOIN', ['rc_title = img_name', 'rc_user = img_user', 'rc_timestamp = img_timestamp']]; // We're ordering by img_timestamp, so we have to make sure MariaDB queries `image` first. // It sometimes decides to query `recentchanges` first and filesort the result set later // to get the right ordering. T124205 / https://mariadb.atlassian.net/browse/MDEV-8880 $options[] = 'STRAIGHT_JOIN'; } $likeVal = $opts->getValue('like'); if (!$this->getConfig()->get('MiserMode') && $likeVal !== '') { $dbr = wfGetDB(DB_REPLICA); $likeObj = Title::newFromText($likeVal); if ($likeObj instanceof Title) { $like = $dbr->buildLike($dbr->anyString(), strtolower($likeObj->getDBkey()), $dbr->anyString()); $conds[] = "LOWER(img_name) {$like}"; } } $query = ['tables' => $tables, 'fields' => $fields, 'join_conds' => $jconds, 'conds' => $conds, 'options' => $options]; return $query; }
public function execute() { global $wgOut; $repos = CodeRepository::getRepoList(); if (!count($repos)) { global $wgUser; $wgOut->addWikiMsg('code-no-repo'); if ($wgUser->isAllowed('repoadmin')) { $wgOut->addWikiMsg('code-create-repo'); } else { $wgOut->addWikiMsg('code-need-repoadmin-rights'); if (!count(User::getGroupsWithPermission('repoadmin'))) { $wgOut->addWikiMsg('code-need-group-with-rights'); } } return; } $text = ''; foreach ($repos as $repo) { $text .= "* " . self::getNavItem($repo) . "\n"; } $wgOut->addWikiText($text); }
/** * @return UserArray */ public static function getAdminsToNotify() { $groups = User::getGroupsWithPermission('confirmaccount-notify'); if (!count($groups)) { return UserArray::newFromResult(new FakeResultWrapper(array())); } $dbr = wfGetDB(DB_SLAVE); return UserArray::newFromResult($dbr->select(array('user'), array('*'), array('EXISTS (' . $dbr->selectSqlText('user_groups', '1', array('ug_user = user_id', 'ug_group' => $groups)) . ')'), __METHOD__, array('LIMIT' => 200))); }
/** * Get a description array when the user doesn't have the right to perform * $action (i.e. when User::isAllowed() returns false) * * @param string $action The action to check * @param bool $short Short circuit on first error * @return array List of errors */ private function missingPermissionError($action, $short) { // We avoid expensive display logic for quickUserCan's and such if ($short) { return array('badaccess-group0'); } $groups = array_map(array('User', 'makeGroupLinkWiki'), User::getGroupsWithPermission($action)); if (count($groups)) { global $wgLang; return array('badaccess-groups', $wgLang->commaList($groups), count($groups)); } else { return array('badaccess-group0'); } }
/** * @deprecated since 1.25 * @return mixed|string */ public function reallyMakeHelpMsg() { wfDeprecated(__METHOD__, '1.25'); $this->setHelp(); // Use parent to make default message for the main module $msg = parent::makeHelpMsg(); $astriks = str_repeat('*** ', 14); $msg .= "\n\n{$astriks} Modules {$astriks}\n\n"; foreach ($this->mModuleMgr->getNames('action') as $name) { $module = $this->mModuleMgr->getModule($name); $msg .= self::makeHelpMsgHeader($module, 'action'); $msg2 = $module->makeHelpMsg(); if ($msg2 !== false) { $msg .= $msg2; } $msg .= "\n"; } $msg .= "\n{$astriks} Permissions {$astriks}\n\n"; foreach (self::$mRights as $right => $rightMsg) { $rightsMsg = $this->msg($rightMsg['msg'], $rightMsg['params'])->useDatabase(false)->inLanguage('en')->text(); $groups = User::getGroupsWithPermission($right); $msg .= "* " . $right . " *\n {$rightsMsg}" . "\nGranted to:\n " . str_replace('*', 'all', implode(', ', $groups)) . "\n\n"; } $msg .= "\n{$astriks} Formats {$astriks}\n\n"; foreach ($this->mModuleMgr->getNames('format') as $name) { $module = $this->mModuleMgr->getModule($name); $msg .= self::makeHelpMsgHeader($module, 'format'); $msg2 = $module->makeHelpMsg(); if ($msg2 !== false) { $msg .= $msg2; } $msg .= "\n"; } $credits = $this->msg('api-credits')->useDatabase('false')->inLanguage('en')->text(); $credits = str_replace("\n", "\n ", $credits); $msg .= "\n*** Credits: ***\n {$credits}\n"; return $msg; }
/** * @param ApiPageSet $resultPageSet * @return void */ private function run($resultPageSet = null) { $repo = $this->mRepo; if (!$repo instanceof LocalRepo) { $this->dieUsage('Local file repository does not support querying all images', 'unsupportedrepo'); } $prefix = $this->getModulePrefix(); $db = $this->getDB(); $params = $this->extractRequestParams(); // Table and return fields $this->addTables('image'); $prop = array_flip($params['prop']); $this->addFields(LocalFile::selectFields()); $ascendingOrder = true; if ($params['dir'] == 'descending' || $params['dir'] == 'older') { $ascendingOrder = false; } if ($params['sort'] == 'name') { // Check mutually exclusive params $disallowed = ['start', 'end', 'user']; foreach ($disallowed as $pname) { if (isset($params[$pname])) { $this->dieUsage("Parameter '{$prefix}{$pname}' can only be used with {$prefix}sort=timestamp", 'badparams'); } } if ($params['filterbots'] != 'all') { $this->dieUsage("Parameter '{$prefix}filterbots' can only be used with {$prefix}sort=timestamp", 'badparams'); } // Pagination if (!is_null($params['continue'])) { $cont = explode('|', $params['continue']); $this->dieContinueUsageIf(count($cont) != 1); $op = $ascendingOrder ? '>' : '<'; $continueFrom = $db->addQuotes($cont[0]); $this->addWhere("img_name {$op}= {$continueFrom}"); } // Image filters $from = $params['from'] === null ? null : $this->titlePartToKey($params['from'], NS_FILE); $to = $params['to'] === null ? null : $this->titlePartToKey($params['to'], NS_FILE); $this->addWhereRange('img_name', $ascendingOrder ? 'newer' : 'older', $from, $to); if (isset($params['prefix'])) { $this->addWhere('img_name' . $db->buildLike($this->titlePartToKey($params['prefix'], NS_FILE), $db->anyString())); } } else { // Check mutually exclusive params $disallowed = ['from', 'to', 'prefix']; foreach ($disallowed as $pname) { if (isset($params[$pname])) { $this->dieUsage("Parameter '{$prefix}{$pname}' can only be used with {$prefix}sort=name", 'badparams'); } } if (!is_null($params['user']) && $params['filterbots'] != 'all') { // Since filterbots checks if each user has the bot right, it // doesn't make sense to use it with user $this->dieUsage("Parameters '{$prefix}user' and '{$prefix}filterbots' cannot be used together", 'badparams'); } // Pagination $this->addTimestampWhereRange('img_timestamp', $ascendingOrder ? 'newer' : 'older', $params['start'], $params['end']); // Include in ORDER BY for uniqueness $this->addWhereRange('img_name', $ascendingOrder ? 'newer' : 'older', null, null); if (!is_null($params['continue'])) { $cont = explode('|', $params['continue']); $this->dieContinueUsageIf(count($cont) != 2); $op = $ascendingOrder ? '>' : '<'; $continueTimestamp = $db->addQuotes($db->timestamp($cont[0])); $continueName = $db->addQuotes($cont[1]); $this->addWhere("img_timestamp {$op} {$continueTimestamp} OR " . "(img_timestamp = {$continueTimestamp} AND " . "img_name {$op}= {$continueName})"); } // Image filters if (!is_null($params['user'])) { $this->addWhereFld('img_user_text', $params['user']); } if ($params['filterbots'] != 'all') { $this->addTables('user_groups'); $this->addJoinConds(['user_groups' => ['LEFT JOIN', ['ug_group' => User::getGroupsWithPermission('bot'), 'ug_user = img_user']]]); $groupCond = $params['filterbots'] == 'nobots' ? 'NULL' : 'NOT NULL'; $this->addWhere("ug_group IS {$groupCond}"); } } // Filters not depending on sort if (isset($params['minsize'])) { $this->addWhere('img_size>=' . intval($params['minsize'])); } if (isset($params['maxsize'])) { $this->addWhere('img_size<=' . intval($params['maxsize'])); } $sha1 = false; if (isset($params['sha1'])) { $sha1 = strtolower($params['sha1']); if (!$this->validateSha1Hash($sha1)) { $this->dieUsage('The SHA1 hash provided is not valid', 'invalidsha1hash'); } $sha1 = Wikimedia\base_convert($sha1, 16, 36, 31); } elseif (isset($params['sha1base36'])) { $sha1 = strtolower($params['sha1base36']); if (!$this->validateSha1Base36Hash($sha1)) { $this->dieUsage('The SHA1Base36 hash provided is not valid', 'invalidsha1base36hash'); } } if ($sha1) { $this->addWhereFld('img_sha1', $sha1); } if (!is_null($params['mime'])) { if ($this->getConfig()->get('MiserMode')) { $this->dieUsage('MIME search disabled in Miser Mode', 'mimesearchdisabled'); } $mimeConds = []; foreach ($params['mime'] as $mime) { list($major, $minor) = File::splitMime($mime); $mimeConds[] = $db->makeList(['img_major_mime' => $major, 'img_minor_mime' => $minor], LIST_AND); } // safeguard against internal_api_error_DBQueryError if (count($mimeConds) > 0) { $this->addWhere($db->makeList($mimeConds, LIST_OR)); } else { // no MIME types, no files $this->getResult()->addValue('query', $this->getModuleName(), []); return; } } $limit = $params['limit']; $this->addOption('LIMIT', $limit + 1); $sortFlag = ''; if (!$ascendingOrder) { $sortFlag = ' DESC'; } if ($params['sort'] == 'timestamp') { $this->addOption('ORDER BY', 'img_timestamp' . $sortFlag); if (!is_null($params['user'])) { $this->addOption('USE INDEX', ['image' => 'img_usertext_timestamp']); } else { $this->addOption('USE INDEX', ['image' => 'img_timestamp']); } } else { $this->addOption('ORDER BY', 'img_name' . $sortFlag); } $res = $this->select(__METHOD__); $titles = []; $count = 0; $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... if ($params['sort'] == 'name') { $this->setContinueEnumParameter('continue', $row->img_name); } else { $this->setContinueEnumParameter('continue', "{$row->img_timestamp}|{$row->img_name}"); } break; } if (is_null($resultPageSet)) { $file = $repo->newFileFromRow($row); $info = array_merge(['name' => $row->img_name], ApiQueryImageInfo::getInfo($file, $prop, $result)); self::addTitleInfo($info, $file->getTitle()); $fit = $result->addValue(['query', $this->getModuleName()], null, $info); if (!$fit) { if ($params['sort'] == 'name') { $this->setContinueEnumParameter('continue', $row->img_name); } else { $this->setContinueEnumParameter('continue', "{$row->img_timestamp}|{$row->img_name}"); } break; } } else { $titles[] = Title::makeTitle(NS_FILE, $row->img_name); } } if (is_null($resultPageSet)) { $result->addIndexedTagName(['query', $this->getModuleName()], 'img'); } else { $resultPageSet->populateFromTitles($titles); } }
/** * Factory function for fatal permission-denied errors * * @since 1.22 * @param string $permission User right required * @return Status */ static function newFatalPermissionDeniedStatus($permission) { global $wgLang; $groups = array_map(array('User', 'makeGroupLinkWiki'), User::getGroupsWithPermission($permission)); if ($groups) { return Status::newFatal('badaccess-groups', $wgLang->commaList($groups), count($groups)); } else { return Status::newFatal('badaccess-group0'); } }
public function execute() { $db = $this->getDB(); $params = $this->extractRequestParams(); $this->requireMaxOneParameter($params, 'group', 'excludegroup', 'rights', 'excluderights'); // Only operate on existing pages $pages = array_keys($this->getPageSet()->getGoodTitles()); // Filter out already-processed pages if ($params['continue'] !== null) { $cont = explode('|', $params['continue']); $this->dieContinueUsageIf(count($cont) != 2); $cont_page = (int) $cont[0]; $pages = array_filter($pages, function ($v) use($cont_page) { return $v >= $cont_page; }); } if (!count($pages)) { // Nothing to do return; } // Apply MAX_PAGES, leaving any over the limit for a continue. sort($pages); $continuePages = null; if (count($pages) > self::MAX_PAGES) { $continuePages = $pages[self::MAX_PAGES] . '|0'; $pages = array_slice($pages, 0, self::MAX_PAGES); } $result = $this->getResult(); // First, count anons $this->addTables('revision'); $this->addFields(['page' => 'rev_page', 'anons' => 'COUNT(DISTINCT rev_user_text)']); $this->addWhereFld('rev_page', $pages); $this->addWhere('rev_user = 0'); $this->addWhere($db->bitAnd('rev_deleted', Revision::DELETED_USER) . ' = 0'); $this->addOption('GROUP BY', 'rev_page'); $res = $this->select(__METHOD__); foreach ($res as $row) { $fit = $result->addValue(['query', 'pages', $row->page], 'anoncontributors', (int) $row->anons); if (!$fit) { // This not fitting isn't reasonable, so it probably means that // some other module used up all the space. Just set a dummy // continue and hope it works next time. $this->setContinueEnumParameter('continue', $params['continue'] !== null ? $params['continue'] : '0|0'); return; } } // Next, add logged-in users $this->resetQueryParams(); $this->addTables('revision'); $this->addFields(['page' => 'rev_page', 'user' => 'rev_user', 'username' => 'MAX(rev_user_text)']); $this->addWhereFld('rev_page', $pages); $this->addWhere('rev_user != 0'); $this->addWhere($db->bitAnd('rev_deleted', Revision::DELETED_USER) . ' = 0'); $this->addOption('GROUP BY', 'rev_page, rev_user'); $this->addOption('LIMIT', $params['limit'] + 1); // Force a sort order to ensure that properties are grouped by page // But only if pp_page is not constant in the WHERE clause. if (count($pages) > 1) { $this->addOption('ORDER BY', 'rev_page, rev_user'); } else { $this->addOption('ORDER BY', 'rev_user'); } $limitGroups = []; if ($params['group']) { $excludeGroups = false; $limitGroups = $params['group']; } elseif ($params['excludegroup']) { $excludeGroups = true; $limitGroups = $params['excludegroup']; } elseif ($params['rights']) { $excludeGroups = false; foreach ($params['rights'] as $r) { $limitGroups = array_merge($limitGroups, User::getGroupsWithPermission($r)); } // If no group has the rights requested, no need to query if (!$limitGroups) { if ($continuePages !== null) { // But we still need to continue for the next page's worth // of anoncontributors $this->setContinueEnumParameter('continue', $continuePages); } return; } } elseif ($params['excluderights']) { $excludeGroups = true; foreach ($params['excluderights'] as $r) { $limitGroups = array_merge($limitGroups, User::getGroupsWithPermission($r)); } } if ($limitGroups) { $limitGroups = array_unique($limitGroups); $this->addTables('user_groups'); $this->addJoinConds(['user_groups' => [$excludeGroups ? 'LEFT OUTER JOIN' : 'INNER JOIN', ['ug_user=rev_user', 'ug_group' => $limitGroups]]]); $this->addWhereIf('ug_user IS NULL', $excludeGroups); } if ($params['continue'] !== null) { $cont = explode('|', $params['continue']); $this->dieContinueUsageIf(count($cont) != 2); $cont_page = (int) $cont[0]; $cont_user = (int) $cont[1]; $this->addWhere("rev_page > {$cont_page} OR " . "(rev_page = {$cont_page} AND " . "rev_user >= {$cont_user})"); } $res = $this->select(__METHOD__); $count = 0; foreach ($res as $row) { if (++$count > $params['limit']) { // We've reached the one extra which shows that // there are additional pages to be had. Stop here... $this->setContinueEnumParameter('continue', $row->page . '|' . $row->user); return; } $fit = $this->addPageSubItem($row->page, ['userid' => (int) $row->user, 'name' => $row->username], 'user'); if (!$fit) { $this->setContinueEnumParameter('continue', $row->page . '|' . $row->user); return; } } if ($continuePages !== null) { $this->setContinueEnumParameter('continue', $continuePages); } }
public function execute() { $params = $this->extractRequestParams(); $activeUserDays = $this->getConfig()->get('ActiveUserDays'); if ($params['activeusers']) { // Update active user cache SpecialActiveUsers::mergeActiveUsers(600, $activeUserDays); } $db = $this->getDB(); $prop = $params['prop']; if (!is_null($prop)) { $prop = array_flip($prop); $fld_blockinfo = isset($prop['blockinfo']); $fld_editcount = isset($prop['editcount']); $fld_groups = isset($prop['groups']); $fld_rights = isset($prop['rights']); $fld_registration = isset($prop['registration']); $fld_implicitgroups = isset($prop['implicitgroups']); } else { $fld_blockinfo = $fld_editcount = $fld_groups = $fld_registration = $fld_rights = $fld_implicitgroups = false; } $limit = $params['limit']; $this->addTables('user'); $useIndex = true; $dir = $params['dir'] == 'descending' ? 'older' : 'newer'; $from = is_null($params['from']) ? null : $this->getCanonicalUserName($params['from']); $to = is_null($params['to']) ? null : $this->getCanonicalUserName($params['to']); # MySQL can't figure out that 'user_name' and 'qcc_title' are the same # despite the JOIN condition, so manually sort on the correct one. $userFieldToSort = $params['activeusers'] ? 'qcc_title' : 'user_name'; $this->addWhereRange($userFieldToSort, $dir, $from, $to); if (!is_null($params['prefix'])) { $this->addWhere($userFieldToSort . $db->buildLike($this->getCanonicalUserName($params['prefix']), $db->anyString())); } if (!is_null($params['rights']) && count($params['rights'])) { $groups = array(); foreach ($params['rights'] as $r) { $groups = array_merge($groups, User::getGroupsWithPermission($r)); } // no group with the given right(s) exists, no need for a query if (!count($groups)) { $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), ''); return; } $groups = array_unique($groups); if (is_null($params['group'])) { $params['group'] = $groups; } else { $params['group'] = array_unique(array_merge($params['group'], $groups)); } } if (!is_null($params['group']) && count($params['group'])) { // Filter only users that belong to a given group $this->addWhere('EXISTS (' . $db->selectSQLText('user_groups', '1', array('ug_user=user_id', 'ug_group' => $params['group'])) . ')'); } if (!is_null($params['excludegroup']) && count($params['excludegroup'])) { // Filter only users don't belong to a given group $this->addWhere('NOT EXISTS (' . $db->selectSQLText('user_groups', '1', array('ug_user=user_id', 'ug_group' => $params['excludegroup'])) . ')'); } if ($params['witheditsonly']) { $this->addWhere('user_editcount > 0'); } $this->showHiddenUsersAddBlockInfo($fld_blockinfo); if ($fld_groups || $fld_rights) { // Show the groups the given users belong to // request more than needed to avoid not getting all rows that belong to one user $groupCount = count(User::getAllGroups()); $sqlLimit = $limit + $groupCount + 1; $this->addTables('user_groups', 'ug2'); $this->addJoinConds(array('ug2' => array('LEFT JOIN', 'ug2.ug_user=user_id'))); $this->addFields(array('ug_group2' => 'ug2.ug_group')); } else { $sqlLimit = $limit + 1; } if ($params['activeusers']) { $activeUserSeconds = $activeUserDays * 86400; // Filter query to only include users in the active users cache $this->addTables('querycachetwo'); $this->addJoinConds(array('querycachetwo' => array('INNER JOIN', array('qcc_type' => 'activeusers', 'qcc_namespace' => NS_USER, 'qcc_title=user_name')))); // Actually count the actions using a subquery (bug 64505 and bug 64507) $timestamp = $db->timestamp(wfTimestamp(TS_UNIX) - $activeUserSeconds); $this->addFields(array('recentactions' => '(' . $db->selectSQLText('recentchanges', 'COUNT(*)', array('rc_user_text = user_name', 'rc_type != ' . $db->addQuotes(RC_EXTERNAL), 'rc_log_type IS NULL OR rc_log_type != ' . $db->addQuotes('newusers'), 'rc_timestamp >= ' . $db->addQuotes($timestamp))) . ')')); } $this->addOption('LIMIT', $sqlLimit); $this->addFields(array('user_name', 'user_id')); $this->addFieldsIf('user_editcount', $fld_editcount); $this->addFieldsIf('user_registration', $fld_registration); if ($useIndex) { $this->addOption('USE INDEX', array('user' => 'user_name')); } $res = $this->select(__METHOD__); $count = 0; $lastUserData = false; $lastUser = false; $result = $this->getResult(); // This loop keeps track of the last entry. For each new row, if the // new row is for different user then the last, the last entry is added // to results. Otherwise, the group of the new row is appended to the // last entry. The setContinue... is more complex because of this, and // takes into account the higher sql limit to make sure all rows that // belong to the same user are received. foreach ($res as $row) { $count++; if ($lastUser !== $row->user_name) { // Save the last pass's user data if (is_array($lastUserData)) { if ($params['activeusers'] && $lastUserData['recentactions'] === 0) { // activeusers cache was out of date $fit = true; } else { $fit = $result->addValue(array('query', $this->getModuleName()), null, $lastUserData); } $lastUserData = null; if (!$fit) { $this->setContinueEnumParameter('from', $lastUserData['name']); break; } } if ($count > $limit) { // We've reached the one extra which shows that there are // additional pages to be had. Stop here... $this->setContinueEnumParameter('from', $row->user_name); break; } // Record new user's data $lastUser = $row->user_name; $lastUserData = array('userid' => $row->user_id, 'name' => $lastUser); if ($fld_blockinfo && !is_null($row->ipb_by_text)) { $lastUserData['blockid'] = $row->ipb_id; $lastUserData['blockedby'] = $row->ipb_by_text; $lastUserData['blockedbyid'] = $row->ipb_by; $lastUserData['blockreason'] = $row->ipb_reason; $lastUserData['blockexpiry'] = $row->ipb_expiry; } if ($row->ipb_deleted) { $lastUserData['hidden'] = ''; } if ($fld_editcount) { $lastUserData['editcount'] = intval($row->user_editcount); } if ($params['activeusers']) { $lastUserData['recentactions'] = intval($row->recentactions); // @todo 'recenteditcount' is set for BC, remove in 1.25 $lastUserData['recenteditcount'] = $lastUserData['recentactions']; } if ($fld_registration) { $lastUserData['registration'] = $row->user_registration ? wfTimestamp(TS_ISO_8601, $row->user_registration) : ''; } } if ($sqlLimit == $count) { // @todo BUG! database contains group name that User::getAllGroups() does not return // Should handle this more gracefully ApiBase::dieDebug(__METHOD__, 'MediaWiki configuration error: The database contains more ' . 'user groups than known to User::getAllGroups() function'); } $lastUserObj = User::newFromId($row->user_id); // Add user's group info if ($fld_groups) { if (!isset($lastUserData['groups'])) { if ($lastUserObj) { $lastUserData['groups'] = $lastUserObj->getAutomaticGroups(); } else { // This should not normally happen $lastUserData['groups'] = array(); } } if (!is_null($row->ug_group2)) { $lastUserData['groups'][] = $row->ug_group2; } $result->setIndexedTagName($lastUserData['groups'], 'g'); } if ($fld_implicitgroups && !isset($lastUserData['implicitgroups']) && $lastUserObj) { $lastUserData['implicitgroups'] = $lastUserObj->getAutomaticGroups(); $result->setIndexedTagName($lastUserData['implicitgroups'], 'g'); } if ($fld_rights) { if (!isset($lastUserData['rights'])) { if ($lastUserObj) { $lastUserData['rights'] = User::getGroupPermissions($lastUserObj->getAutomaticGroups()); } else { // This should not normally happen $lastUserData['rights'] = array(); } } if (!is_null($row->ug_group2)) { $lastUserData['rights'] = array_unique(array_merge($lastUserData['rights'], User::getGroupPermissions(array($row->ug_group2)))); } $result->setIndexedTagName($lastUserData['rights'], 'r'); } } if (is_array($lastUserData) && !($params['activeusers'] && $lastUserData['recentactions'] === 0)) { $fit = $result->addValue(array('query', $this->getModuleName()), null, $lastUserData); if (!$fit) { $this->setContinueEnumParameter('from', $lastUserData['name']); } } $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'u'); }
/** * Can $user perform $action on this page? This is an internal function, * which checks ONLY that previously checked by userCan (i.e. it leaves out * checks on wfReadOnly() and blocks) * * @param $action \type{\string} action that permission needs to be checked for * @param $user \type{User} user to check * @param $doExpensiveQueries \type{\bool} Set this to false to avoid doing unnecessary queries. * @param $short \type{\bool} Set this to true to stop after the first permission error. * @return \type{\array} Array of arrays of the arguments to wfMsg to explain permissions problems. */ private function getUserPermissionsErrorsInternal($action, $user, $doExpensiveQueries = true, $short = false) { wfProfileIn(__METHOD__); $errors = array(); // First stop is permissions checks, which fail most often, and which are easiest to test. if ($action == 'move') { if (!$user->isAllowed('move-rootuserpages') && $this->getNamespace() == NS_USER && !$this->isSubpage()) { // Show user page-specific message only if the user can move other pages $errors[] = array('cant-move-user-page'); } // Check if user is allowed to move files if it's a file if ($this->getNamespace() == NS_FILE && !$user->isAllowed('movefile')) { $errors[] = array('movenotallowedfile'); } if (!$user->isAllowed('move')) { // User can't move anything $errors[] = $user->isAnon() ? array('movenologintext') : array('movenotallowed'); } } elseif ($action == 'create') { if ($this->isTalkPage() && !$user->isAllowed('createtalk') || !$this->isTalkPage() && !$user->isAllowed('createpage')) { $errors[] = $user->isAnon() ? array('nocreatetext') : array('nocreate-loggedin'); } } elseif ($action == 'move-target') { if (!$user->isAllowed('move')) { // User can't move anything $errors[] = $user->isAnon() ? array('movenologintext') : array('movenotallowed'); } elseif (!$user->isAllowed('move-rootuserpages') && $this->getNamespace() == NS_USER && !$this->isSubpage()) { // Show user page-specific message only if the user can move other pages $errors[] = array('cant-move-to-user-page'); } } elseif (!$user->isAllowed($action)) { $return = null; $groups = array_map(array('User', 'makeGroupLinkWiki'), User::getGroupsWithPermission($action)); if ($groups) { $return = array('badaccess-groups', array(implode(', ', $groups), count($groups))); } else { $return = array("badaccess-group0"); } $errors[] = $return; } # Short-circuit point if ($short && count($errors) > 0) { wfProfileOut(__METHOD__); return $errors; } // Use getUserPermissionsErrors instead if (!wfRunHooks('userCan', array(&$this, &$user, $action, &$result))) { wfProfileOut(__METHOD__); return $result ? array() : array(array('badaccess-group0')); } // Check getUserPermissionsErrors hook if (!wfRunHooks('getUserPermissionsErrors', array(&$this, &$user, $action, &$result))) { if (is_array($result) && count($result) && !is_array($result[0])) { $errors[] = $result; } else { if (is_array($result) && is_array($result[0])) { $errors = array_merge($errors, $result); } else { if ($result !== '' && is_string($result)) { $errors[] = array($result); } else { if ($result === false) { $errors[] = array('badaccess-group0'); } } } } # a generic "We don't want them to do that" } # Short-circuit point if ($short && count($errors) > 0) { wfProfileOut(__METHOD__); return $errors; } // Check getUserPermissionsErrorsExpensive hook if ($doExpensiveQueries && !wfRunHooks('getUserPermissionsErrorsExpensive', array(&$this, &$user, $action, &$result))) { if (is_array($result) && count($result) && !is_array($result[0])) { $errors[] = $result; } else { if (is_array($result) && is_array($result[0])) { $errors = array_merge($errors, $result); } else { if ($result !== '' && is_string($result)) { $errors[] = array($result); } else { if ($result === false) { $errors[] = array('badaccess-group0'); } } } } # a generic "We don't want them to do that" } # Short-circuit point if ($short && count($errors) > 0) { wfProfileOut(__METHOD__); return $errors; } # Only 'createaccount' and 'execute' can be performed on # special pages, which don't actually exist in the DB. $specialOKActions = array('createaccount', 'execute'); if (NS_SPECIAL == $this->mNamespace && !in_array($action, $specialOKActions)) { $errors[] = array('ns-specialprotected'); } # Check $wgNamespaceProtection for restricted namespaces if ($this->isNamespaceProtected()) { $ns = $this->getNamespace() == NS_MAIN ? wfMsg('nstab-main') : $this->getNsText(); $errors[] = NS_MEDIAWIKI == $this->mNamespace ? array('protectedinterface') : array('namespaceprotected', $ns); } # Protect css/js subpages of user pages # XXX: this might be better using restrictions # XXX: Find a way to work around the php bug that prevents using $this->userCanEditCssJsSubpage() from working if ($this->isCssJsSubpage() && !$user->isAllowed('editusercssjs') && !preg_match('/^' . preg_quote($user->getName(), '/') . '\\//', $this->mTextform)) { $errors[] = array('customcssjsprotected'); } # Check against page_restrictions table requirements on this # page. The user must possess all required rights for this action. foreach ($this->getRestrictions($action) as $right) { // Backwards compatibility, rewrite sysop -> protect if ($right == 'sysop') { $right = 'protect'; } if ('' != $right && !$user->isAllowed($right)) { // Users with 'editprotected' permission can edit protected pages if ($action == 'edit' && $user->isAllowed('editprotected')) { // Users with 'editprotected' permission cannot edit protected pages // with cascading option turned on. if ($this->mCascadeRestriction) { $errors[] = array('protectedpagetext', $right); } } else { $errors[] = array('protectedpagetext', $right); } } } # Short-circuit point if ($short && count($errors) > 0) { wfProfileOut(__METHOD__); return $errors; } if ($doExpensiveQueries && !$this->isCssJsSubpage()) { # We /could/ use the protection level on the source page, but it's fairly ugly # as we have to establish a precedence hierarchy for pages included by multiple # cascade-protected pages. So just restrict it to people with 'protect' permission, # as they could remove the protection anyway. list($cascadingSources, $restrictions) = $this->getCascadeProtectionSources(); # Cascading protection depends on more than this page... # Several cascading protected pages may include this page... # Check each cascading level # This is only for protection restrictions, not for all actions if ($cascadingSources > 0 && isset($restrictions[$action])) { foreach ($restrictions[$action] as $right) { $right = $right == 'sysop' ? 'protect' : $right; if ('' != $right && !$user->isAllowed($right)) { $pages = ''; foreach ($cascadingSources as $page) { $pages .= '* [[:' . $page->getPrefixedText() . "]]\n"; } $errors[] = array('cascadeprotected', count($cascadingSources), $pages); } } } } # Short-circuit point if ($short && count($errors) > 0) { wfProfileOut(__METHOD__); return $errors; } if ($action == 'protect') { if ($this->getUserPermissionsErrors('edit', $user) != array()) { $errors[] = array('protect-cantedit'); // If they can't edit, they shouldn't protect. } } if ($action == 'create') { $title_protection = $this->getTitleProtection(); if (is_array($title_protection)) { extract($title_protection); // is this extract() really needed? if ($pt_create_perm == 'sysop') { $pt_create_perm = 'protect'; // B/C } if ($pt_create_perm == '' || !$user->isAllowed($pt_create_perm)) { $errors[] = array('titleprotected', User::whoIs($pt_user), $pt_reason); } } } elseif ($action == 'move') { // Check for immobile pages if (!MWNamespace::isMovable($this->getNamespace())) { // Specific message for this case $errors[] = array('immobile-source-namespace', $this->getNsText()); } elseif (!$this->isMovable()) { // Less specific message for rarer cases $errors[] = array('immobile-page'); } } elseif ($action == 'move-target') { if (!MWNamespace::isMovable($this->getNamespace())) { $errors[] = array('immobile-target-namespace', $this->getNsText()); } elseif (!$this->isMovable()) { $errors[] = array('immobile-target-page'); } } wfProfileOut(__METHOD__); return $errors; }
function wfSpecialWikiaNewFiles($par, $specialPage) { global $wgOut, $wgLang, $wgRequest, $wgMiserMode; global $wmu, $wgOasisHD; $wpIlMatch = $wgRequest->getText('wpIlMatch'); $dbr = wfGetDB(DB_SLAVE); $sk = RequestContext::getMain()->getSkin(); $shownav = !$specialPage->including(); $hidebots = $wgRequest->getBool('hidebots', 1); $hidebotsql = ''; if ($hidebots) { # Make a list of group names which have the 'bot' flag set. $botconds = array(); foreach (User::getGroupsWithPermission('bot') as $groupname) { $botconds[] = 'ug_group = ' . $dbr->addQuotes($groupname); } # If not bot groups, do not set $hidebotsql if ($botconds) { $isbotmember = $dbr->makeList($botconds, LIST_OR); # This join, in conjunction with WHERE ug_group IS NULL, returns # only those rows from IMAGE where the uploading user is not a mem- # ber of a group which has the 'bot' permission set. $ug = $dbr->tableName('user_groups'); $hidebotsql = " LEFT JOIN {$ug} ON img_user=ug_user AND ({$isbotmember})"; } } $image = $dbr->tableName('image'); $sql = "SELECT img_timestamp from {$image}"; if ($hidebotsql) { $sql .= "{$hidebotsql} WHERE ug_group IS NULL"; } $sql .= ' ORDER BY img_timestamp DESC LIMIT 1'; $res = $dbr->query($sql, __FUNCTION__); $row = $dbr->fetchRow($res); if ($row !== false) { $ts = $row[0]; } else { $ts = false; } $dbr->freeResult($res); $sql = ''; # If we were clever, we'd use this to cache. $latestTimestamp = wfTimestamp(TS_MW, $ts); # Hardcode this for now. $limit = 48; if ($parval = intval($par)) { if ($parval <= $limit && $parval > 0) { $limit = $parval; } } $where = array(); $searchpar = ''; if ($wpIlMatch != '' && !$wgMiserMode) { $nt = Title::newFromUrl($wpIlMatch); if ($nt) { $m = $dbr->buildLike($dbr->anyString(), strtolower($nt->getDBkey()), $dbr->anyString()); $where[] = 'LOWER(img_name)' . $m; $searchpar = '&wpIlMatch=' . urlencode($wpIlMatch); } } $invertSort = false; if ($until = $wgRequest->getVal('until')) { $where[] = "img_timestamp < '" . $dbr->timestamp($until) . "'"; } if ($from = $wgRequest->getVal('from')) { $where[] = "img_timestamp >= '" . $dbr->timestamp($from) . "'"; $invertSort = true; } $sql = 'SELECT img_size, img_name, img_user, img_user_text,' . "img_description,img_timestamp FROM {$image}"; if ($hidebotsql) { $sql .= $hidebotsql; $where[] = 'ug_group IS NULL'; } // hook by Wikia, Bartek Lapinski 26.03.2009, for videos and stuff wfRunHooks('SpecialNewImages::beforeQuery', array(&$where)); if (count($where)) { $sql .= ' WHERE ' . $dbr->makeList($where, LIST_AND); } $sql .= ' ORDER BY img_timestamp ' . ($invertSort ? '' : ' DESC'); $sql .= ' LIMIT ' . ($limit + 1); $res = $dbr->query($sql, __FUNCTION__); /** * We have to flip things around to get the last N after a certain date */ $images = array(); while ($s = $dbr->fetchObject($res)) { if ($invertSort) { array_unshift($images, $s); } else { array_push($images, $s); } } $dbr->freeResult($res); $gallery = new WikiaPhotoGallery(); $gallery->parseParams(array("rowdivider" => true, "hideoverflow" => true)); //FB#1150 - make the images fit the whole horizontal space in Oasis and Oasis HD if (get_class($sk) == 'SkinOasis') { if ($wgOasisHD) { $gallery->setWidths(202); } else { $gallery->setWidths(212); } } $firstTimestamp = null; $lastTimestamp = null; $shownImages = 0; foreach ($images as $s) { $shownImages++; if ($shownImages > $limit) { # One extra just to test for whether to show a page link; # don't actually show it. break; } $name = $s->img_name; $ut = $s->img_user_text; $nt = Title::newFromText($name, NS_FILE); $ul = $sk->link(Title::makeTitle(NS_USER, $ut), $ut, array('class' => 'wikia-gallery-item-user')); $timeago = wfTimeFormatAgo($s->img_timestamp); $links = getLinkedFiles($s); // If there are more than two files, remove the 2nd and link to the // image page if (count($links) == 2) { array_splice($links, 1); $moreFiles = true; } else { $moreFiles = false; } $caption = wfMsgHtml('wikianewfiles-uploadby', $ul) . "<br />\n" . "<i>{$timeago}</i><br />\n"; if (count($links)) { $caption .= wfMsg('wikianewfiles-postedin') . " " . $links[0]; } if ($moreFiles) { $caption .= ", " . '<a href="' . $nt->getLocalUrl() . '#filelinks" class="wikia-gallery-item-more">' . wfMsgHtml('wikianewfiles-more') . '</a>'; } $gallery->add($nt, $caption); $timestamp = wfTimestamp(TS_MW, $s->img_timestamp); if (empty($firstTimestamp)) { $firstTimestamp = $timestamp; } $lastTimestamp = $timestamp; } wfRunHooks('SpecialNewImages::beforeDisplay', array(&$images, &$gallery, &$limit)); $titleObj = SpecialPage::getTitleFor('Newimages'); $action = $titleObj->getLocalURL($hidebots ? '' : 'hidebots=0'); if ($shownav && !$wgMiserMode) { $wgOut->addHTML(Xml::openElement('form', array('action' => $action, 'method' => 'post', 'id' => 'imagesearch')) . Xml::fieldset(wfMsg('newimages-legend')) . Xml::inputLabel(wfMsg('newimages-label'), 'wpIlMatch', 'wpIlMatch', 20, $wpIlMatch) . ' ' . Xml::submitButton(wfMsg('ilsubmit'), array('name' => 'wpIlSubmit')) . Xml::closeElement('fieldset') . Xml::closeElement('form')); } /** * Paging controls... */ # If we change bot visibility, this needs to be carried along. if (!$hidebots) { $botpar = '&hidebots=0'; } else { $botpar = ''; } $now = wfTimestampNow(); $d = $wgLang->date($now, true); $t = $wgLang->time($now, true); $dateLink = $sk->link($titleObj, wfMsgHtml('sp-newimages-showfrom', $d, $t), array('class' => 'navigation-filesfrom'), 'from=' . $now . $botpar . $searchpar); $botLink = $sk->link($titleObj, wfMsgHtml('showhidebots', $hidebots ? wfMsgHtml('show') : wfMsgHtml('hide')), array('class' => 'navigation-' . ($hidebots ? 'showbots' : 'hidebots')), 'hidebots=' . ($hidebots ? '0' : '1') . $searchpar); $opts = array('parsemag', 'escapenoentities'); $prevLink = wfMsgExt('pager-newer-n', $opts, $wgLang->formatNum($limit)); if ($firstTimestamp && $firstTimestamp != $latestTimestamp) { $wmu['prev'] = $firstTimestamp; $prevLink = $sk->link($titleObj, $prevLink, array('class' => 'navigation-newer'), 'from=' . $firstTimestamp . $botpar . $searchpar); } $nextLink = wfMsgExt('pager-older-n', $opts, $wgLang->formatNum($limit)); if ($shownImages > $limit && $lastTimestamp) { $wmu['next'] = $lastTimestamp; $nextLink = $sk->link($titleObj, $nextLink, array('class' => 'navigation-older'), 'until=' . $lastTimestamp . $botpar . $searchpar); } $prevnext = '<p id="newfiles-nav">' . $botLink . ' ' . wfMsgHtml('viewprevnext', $prevLink, $nextLink, $dateLink) . '</p>'; if (count($images)) { $wmu['gallery'] = $gallery; $wgOut->addHTML($gallery->toHTML()); if ($shownav) { $wgOut->addHTML($prevnext); } } else { $wgOut->addWikiMsg('noimages'); } }
/** * Display an error page noting that a given permission bit is required. * * @param string $permission key required */ public function permissionRequired($permission) { global $wgUser; $this->setPageTitle(wfMsg('badaccess')); $this->setHTMLTitle(wfMsg('errorpagetitle')); $this->setRobotPolicy('noindex,nofollow'); $this->setArticleRelated(false); $this->mBodytext = ''; $groups = array_map(array('User', 'makeGroupLinkWiki'), User::getGroupsWithPermission($permission)); if ($groups) { $this->addWikiMsg('badaccess-groups', implode(', ', $groups), count($groups)); } else { $this->addWikiMsg('badaccess-group0'); } $this->returnToMain(); }
public function execute() { $db = $this->getDB(); $params = $this->extractRequestParams(); $prop = $params['prop']; if (!is_null($prop)) { $prop = array_flip($prop); $fld_blockinfo = isset($prop['blockinfo']); $fld_editcount = isset($prop['editcount']); $fld_groups = isset($prop['groups']); $fld_rights = isset($prop['rights']); $fld_registration = isset($prop['registration']); $fld_implicitgroups = isset($prop['implicitgroups']); } else { $fld_blockinfo = $fld_editcount = $fld_groups = $fld_registration = $fld_rights = $fld_implicitgroups = false; } $limit = $params['limit']; $this->addTables('user'); $useIndex = true; $dir = $params['dir'] == 'descending' ? 'older' : 'newer'; $from = is_null($params['from']) ? null : $this->keyToTitle($params['from']); $to = is_null($params['to']) ? null : $this->keyToTitle($params['to']); $this->addWhereRange('user_name', $dir, $from, $to); if (!is_null($params['prefix'])) { $this->addWhere('user_name' . $db->buildLike($this->keyToTitle($params['prefix']), $db->anyString())); } if (!is_null($params['rights'])) { $groups = array(); foreach ($params['rights'] as $r) { $groups = array_merge($groups, User::getGroupsWithPermission($r)); } $groups = array_unique($groups); if (is_null($params['group'])) { $params['group'] = $groups; } else { $params['group'] = array_unique(array_merge($params['group'], $groups)); } } if (!is_null($params['group']) && !is_null($params['excludegroup'])) { $this->dieUsage('group and excludegroup cannot be used together', 'group-excludegroup'); } if (!is_null($params['group']) && count($params['group'])) { $useIndex = false; // Filter only users that belong to a given group $this->addTables('user_groups', 'ug1'); $this->addJoinConds(array('ug1' => array('INNER JOIN', array('ug1.ug_user=user_id', 'ug1.ug_group' => $params['group'])))); } if (!is_null($params['excludegroup']) && count($params['excludegroup'])) { $useIndex = false; // Filter only users don't belong to a given group $this->addTables('user_groups', 'ug1'); if (count($params['excludegroup']) == 1) { $exclude = array('ug1.ug_group' => $params['excludegroup'][0]); } else { $exclude = array($db->makeList(array('ug1.ug_group' => $params['excludegroup']), LIST_OR)); } $this->addJoinConds(array('ug1' => array('LEFT OUTER JOIN', array_merge(array('ug1.ug_user=user_id'), $exclude)))); $this->addWhere('ug1.ug_user IS NULL'); } if ($params['witheditsonly']) { $this->addWhere('user_editcount > 0'); } $this->showHiddenUsersAddBlockInfo($fld_blockinfo); if ($fld_groups || $fld_rights) { // Show the groups the given users belong to // request more than needed to avoid not getting all rows that belong to one user $groupCount = count(User::getAllGroups()); $sqlLimit = $limit + $groupCount + 1; $this->addTables('user_groups', 'ug2'); $this->addJoinConds(array('ug2' => array('LEFT JOIN', 'ug2.ug_user=user_id'))); $this->addFields('ug2.ug_group ug_group2'); } else { $sqlLimit = $limit + 1; } if ($params['activeusers']) { global $wgActiveUserDays; $this->addTables('recentchanges'); $this->addJoinConds(array('recentchanges' => array('INNER JOIN', 'rc_user_text=user_name'))); $this->addFields('COUNT(*) AS recentedits'); $this->addWhere("rc_log_type IS NULL OR rc_log_type != 'newusers'"); $timestamp = $db->timestamp(wfTimestamp(TS_UNIX) - $wgActiveUserDays * 24 * 3600); $this->addWhere("rc_timestamp >= {$db->addQuotes($timestamp)}"); $this->addOption('GROUP BY', 'user_name'); } $this->addOption('LIMIT', $sqlLimit); $this->addFields(array('user_name', 'user_id')); $this->addFieldsIf('user_editcount', $fld_editcount); $this->addFieldsIf('user_registration', $fld_registration); if ($useIndex) { $this->addOption('USE INDEX', array('user' => 'user_name')); } $res = $this->select(__METHOD__); $count = 0; $lastUserData = false; $lastUser = false; $result = $this->getResult(); // // This loop keeps track of the last entry. // For each new row, if the new row is for different user then the last, the last entry is added to results. // Otherwise, the group of the new row is appended to the last entry. // The setContinue... is more complex because of this, and takes into account the higher sql limit // to make sure all rows that belong to the same user are received. foreach ($res as $row) { $count++; if ($lastUser !== $row->user_name) { // Save the last pass's user data if (is_array($lastUserData)) { $fit = $result->addValue(array('query', $this->getModuleName()), null, $lastUserData); $lastUserData = null; if (!$fit) { $this->setContinueEnumParameter('from', $this->keyToTitle($lastUserData['name'])); break; } } if ($count > $limit) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... $this->setContinueEnumParameter('from', $this->keyToTitle($row->user_name)); break; } // Record new user's data $lastUser = $row->user_name; $lastUserData = array('userid' => $row->user_id, 'name' => $lastUser); if ($fld_blockinfo && !is_null($row->ipb_by_text)) { $lastUserData['blockedby'] = $row->ipb_by_text; $lastUserData['blockreason'] = $row->ipb_reason; $lastUserData['blockexpiry'] = $row->ipb_expiry; } if ($row->ipb_deleted) { $lastUserData['hidden'] = ''; } if ($fld_editcount) { $lastUserData['editcount'] = intval($row->user_editcount); } if ($params['activeusers']) { $lastUserData['recenteditcount'] = intval($row->recentedits); } if ($fld_registration) { $lastUserData['registration'] = $row->user_registration ? wfTimestamp(TS_ISO_8601, $row->user_registration) : ''; } } if ($sqlLimit == $count) { // BUG! database contains group name that User::getAllGroups() does not return // TODO: should handle this more gracefully ApiBase::dieDebug(__METHOD__, 'MediaWiki configuration error: the database contains more user groups than known to User::getAllGroups() function'); } // Add user's group info if ($fld_groups) { if (!isset($lastUserData['groups'])) { $lastUserData['groups'] = ApiQueryUsers::getAutoGroups(User::newFromName($lastUser)); } if (!is_null($row->ug_group2)) { $lastUserData['groups'][] = $row->ug_group2; } $result->setIndexedTagName($lastUserData['groups'], 'g'); } if ($fld_implicitgroups && !isset($lastUserData['implicitgroups'])) { $lastUserData['implicitgroups'] = ApiQueryUsers::getAutoGroups(User::newFromName($lastUser)); $result->setIndexedTagName($lastUserData['implicitgroups'], 'g'); } if ($fld_rights) { if (!isset($lastUserData['rights'])) { $lastUserData['rights'] = User::getGroupPermissions(User::newFromName($lastUser)->getAutomaticGroups()); } if (!is_null($row->ug_group2)) { $lastUserData['rights'] = array_unique(array_merge($lastUserData['rights'], User::getGroupPermissions(array($row->ug_group2)))); } $result->setIndexedTagName($lastUserData['rights'], 'r'); } } if (is_array($lastUserData)) { $fit = $result->addValue(array('query', $this->getModuleName()), null, $lastUserData); if (!$fit) { $this->setContinueEnumParameter('from', $this->keyToTitle($lastUserData['name'])); } } $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'u'); }
/** * Rebuild pass 4: Mark bot and autopatrolled entries. */ private function rebuildRecentChangesTablePass4() { global $wgUseRCPatrol, $wgMiserMode; $dbw = $this->getDB(DB_MASTER); list($recentchanges, $usergroups, $user) = $dbw->tableNamesN('recentchanges', 'user_groups', 'user'); # @FIXME: recognize other bot account groups (not the same as users with 'bot' rights) # @NOTE: users with 'bot' rights choose when edits are bot edits or not. That information # may be lost at this point (aside from joining on the patrol log table entries). $botgroups = ['bot']; $autopatrolgroups = $wgUseRCPatrol ? User::getGroupsWithPermission('autopatrol') : []; # Flag our recent bot edits if ($botgroups) { $botwhere = $dbw->makeList($botgroups); $this->output("Flagging bot account edits...\n"); # Find all users that are bots $sql = "SELECT DISTINCT user_name FROM {$usergroups}, {$user} " . "WHERE ug_group IN({$botwhere}) AND user_id = ug_user"; $res = $dbw->query($sql, __METHOD__); $botusers = []; foreach ($res as $obj) { $botusers[] = $obj->user_name; } # Fill in the rc_bot field if ($botusers) { $rcids = $dbw->selectFieldValues('recentchanges', 'rc_id', ['rc_user_text' => $botusers, "rc_timestamp > " . $dbw->addQuotes($dbw->timestamp($this->cutoffFrom)), "rc_timestamp < " . $dbw->addQuotes($dbw->timestamp($this->cutoffTo))], __METHOD__); foreach (array_chunk($rcids, $this->mBatchSize) as $rcidBatch) { $dbw->update('recentchanges', ['rc_bot' => 1], ['rc_id' => $rcidBatch], __METHOD__); wfGetLBFactory()->waitForReplication(); } } } # Flag our recent autopatrolled edits if (!$wgMiserMode && $autopatrolgroups) { $patrolwhere = $dbw->makeList($autopatrolgroups); $patrolusers = []; $this->output("Flagging auto-patrolled edits...\n"); # Find all users in RC with autopatrol rights $sql = "SELECT DISTINCT user_name FROM {$usergroups}, {$user} " . "WHERE ug_group IN({$patrolwhere}) AND user_id = ug_user"; $res = $dbw->query($sql, __METHOD__); foreach ($res as $obj) { $patrolusers[] = $dbw->addQuotes($obj->user_name); } # Fill in the rc_patrolled field if ($patrolusers) { $patrolwhere = implode(',', $patrolusers); $sql2 = "UPDATE {$recentchanges} SET rc_patrolled=1 " . "WHERE rc_user_text IN({$patrolwhere}) " . "AND rc_timestamp > " . $dbw->addQuotes($dbw->timestamp($this->cutoffFrom)) . ' ' . "AND rc_timestamp < " . $dbw->addQuotes($dbw->timestamp($this->cutoffTo)); $dbw->query($sql2); } } }
/** * Permissions checks that fail most often, and which are easiest to test. * * @param $action String the action to check * @param $user User user to check * @param $errors Array list of current errors * @param $doExpensiveQueries Boolean whether or not to perform expensive queries * @param $short Boolean short circuit on first error * * @return Array list of errors */ private function checkQuickPermissions($action, $user, $errors, $doExpensiveQueries, $short) { if ($action == 'create') { if ($this->isTalkPage() && !$user->isAllowed('createtalk') || !$this->isTalkPage() && !$user->isAllowed('createpage')) { $errors[] = $user->isAnon() ? array('nocreatetext') : array('nocreate-loggedin'); } } elseif ($action == 'move') { if (!$user->isAllowed('move-rootuserpages') && $this->mNamespace == NS_USER && !$this->isSubpage()) { // Show user page-specific message only if the user can move other pages $errors[] = array('cant-move-user-page'); } // Check if user is allowed to move files if it's a file if ($this->mNamespace == NS_FILE && !$user->isAllowed('movefile')) { $errors[] = array('movenotallowedfile'); } if (!$user->isAllowed('move')) { // User can't move anything global $wgGroupPermissions; $userCanMove = false; if (isset($wgGroupPermissions['user']['move'])) { $userCanMove = $wgGroupPermissions['user']['move']; } $autoconfirmedCanMove = false; if (isset($wgGroupPermissions['autoconfirmed']['move'])) { $autoconfirmedCanMove = $wgGroupPermissions['autoconfirmed']['move']; } if ($user->isAnon() && ($userCanMove || $autoconfirmedCanMove)) { // custom message if logged-in users without any special rights can move $errors[] = array('movenologintext'); } else { $errors[] = array('movenotallowed'); } } } elseif ($action == 'move-target') { if (!$user->isAllowed('move')) { // User can't move anything $errors[] = array('movenotallowed'); } elseif (!$user->isAllowed('move-rootuserpages') && $this->mNamespace == NS_USER && !$this->isSubpage()) { // Show user page-specific message only if the user can move other pages $errors[] = array('cant-move-to-user-page'); } } elseif (!$user->isAllowed($action)) { // We avoid expensive display logic for quickUserCan's and such $groups = false; if (!$short) { $groups = array_map(array('User', 'makeGroupLinkWiki'), User::getGroupsWithPermission($action)); } if ($groups) { global $wgLang; $return = array('badaccess-groups', $wgLang->commaList($groups), count($groups)); } else { $return = array('badaccess-group0'); } $errors[] = $return; } return $errors; }
public function execute() { $params = $this->extractRequestParams(); $activeUserDays = $this->getConfig()->get('ActiveUserDays'); $db = $this->getDB(); $prop = $params['prop']; if (!is_null($prop)) { $prop = array_flip($prop); $fld_blockinfo = isset($prop['blockinfo']); $fld_editcount = isset($prop['editcount']); $fld_groups = isset($prop['groups']); $fld_rights = isset($prop['rights']); $fld_registration = isset($prop['registration']); $fld_implicitgroups = isset($prop['implicitgroups']); $fld_centralids = isset($prop['centralids']); } else { $fld_blockinfo = $fld_editcount = $fld_groups = $fld_registration = $fld_rights = $fld_implicitgroups = $fld_centralids = false; } $limit = $params['limit']; $this->addTables('user'); $dir = $params['dir'] == 'descending' ? 'older' : 'newer'; $from = is_null($params['from']) ? null : $this->getCanonicalUserName($params['from']); $to = is_null($params['to']) ? null : $this->getCanonicalUserName($params['to']); # MySQL can't figure out that 'user_name' and 'qcc_title' are the same # despite the JOIN condition, so manually sort on the correct one. $userFieldToSort = $params['activeusers'] ? 'qcc_title' : 'user_name'; # Some of these subtable joins are going to give us duplicate rows, so # calculate the maximum number of duplicates we might see. $maxDuplicateRows = 1; $this->addWhereRange($userFieldToSort, $dir, $from, $to); if (!is_null($params['prefix'])) { $this->addWhere($userFieldToSort . $db->buildLike($this->getCanonicalUserName($params['prefix']), $db->anyString())); } if (!is_null($params['rights']) && count($params['rights'])) { $groups = array(); foreach ($params['rights'] as $r) { $groups = array_merge($groups, User::getGroupsWithPermission($r)); } // no group with the given right(s) exists, no need for a query if (!count($groups)) { $this->getResult()->addIndexedTagName(array('query', $this->getModuleName()), ''); return; } $groups = array_unique($groups); if (is_null($params['group'])) { $params['group'] = $groups; } else { $params['group'] = array_unique(array_merge($params['group'], $groups)); } } if (!is_null($params['group']) && !is_null($params['excludegroup'])) { $this->dieUsage('group and excludegroup cannot be used together', 'group-excludegroup'); } if (!is_null($params['group']) && count($params['group'])) { // Filter only users that belong to a given group. This might // produce as many rows-per-user as there are groups being checked. $this->addTables('user_groups', 'ug1'); $this->addJoinConds(array('ug1' => array('INNER JOIN', array('ug1.ug_user=user_id', 'ug1.ug_group' => $params['group'])))); $maxDuplicateRows *= count($params['group']); } if (!is_null($params['excludegroup']) && count($params['excludegroup'])) { // Filter only users don't belong to a given group. This can only // produce one row-per-user, because we only keep on "no match". $this->addTables('user_groups', 'ug1'); if (count($params['excludegroup']) == 1) { $exclude = array('ug1.ug_group' => $params['excludegroup'][0]); } else { $exclude = array($db->makeList(array('ug1.ug_group' => $params['excludegroup']), LIST_OR)); } $this->addJoinConds(array('ug1' => array('LEFT OUTER JOIN', array_merge(array('ug1.ug_user=user_id'), $exclude)))); $this->addWhere('ug1.ug_user IS NULL'); } if ($params['witheditsonly']) { $this->addWhere('user_editcount > 0'); } $this->showHiddenUsersAddBlockInfo($fld_blockinfo); if ($fld_groups || $fld_rights) { $this->addFields(array('groups' => $db->buildGroupConcatField('|', 'user_groups', 'ug_group', 'ug_user=user_id'))); } if ($params['activeusers']) { $activeUserSeconds = $activeUserDays * 86400; // Filter query to only include users in the active users cache. // There shouldn't be any duplicate rows in querycachetwo here. $this->addTables('querycachetwo'); $this->addJoinConds(array('querycachetwo' => array('INNER JOIN', array('qcc_type' => 'activeusers', 'qcc_namespace' => NS_USER, 'qcc_title=user_name')))); // Actually count the actions using a subquery (bug 64505 and bug 64507) $timestamp = $db->timestamp(wfTimestamp(TS_UNIX) - $activeUserSeconds); $this->addFields(array('recentactions' => '(' . $db->selectSQLText('recentchanges', 'COUNT(*)', array('rc_user_text = user_name', 'rc_type != ' . $db->addQuotes(RC_EXTERNAL), 'rc_log_type IS NULL OR rc_log_type != ' . $db->addQuotes('newusers'), 'rc_timestamp >= ' . $db->addQuotes($timestamp))) . ')')); } $sqlLimit = $limit + $maxDuplicateRows; $this->addOption('LIMIT', $sqlLimit); $this->addFields(array('user_name', 'user_id')); $this->addFieldsIf('user_editcount', $fld_editcount); $this->addFieldsIf('user_registration', $fld_registration); $res = $this->select(__METHOD__); $count = 0; $countDuplicates = 0; $lastUser = false; $result = $this->getResult(); foreach ($res as $row) { $count++; if ($lastUser === $row->user_name) { // Duplicate row due to one of the needed subtable joins. // Ignore it, but count the number of them to sanely handle // miscalculation of $maxDuplicateRows. $countDuplicates++; if ($countDuplicates == $maxDuplicateRows) { ApiBase::dieDebug(__METHOD__, 'Saw more duplicate rows than expected'); } continue; } $countDuplicates = 0; $lastUser = $row->user_name; if ($count > $limit) { // We've reached the one extra which shows that there are // additional pages to be had. Stop here... $this->setContinueEnumParameter('from', $row->user_name); break; } if ($count == $sqlLimit) { // Should never hit this (either the $countDuplicates check or // the $count > $limit check should hit first), but check it // anyway just in case. ApiBase::dieDebug(__METHOD__, 'Saw more duplicate rows than expected'); } if ($params['activeusers'] && $row->recentactions === 0) { // activeusers cache was out of date continue; } $data = array('userid' => (int) $row->user_id, 'name' => $row->user_name); if ($fld_centralids) { $data += ApiQueryUserInfo::getCentralUserInfo($this->getConfig(), User::newFromId($row->user_id), $params['attachedwiki']); } if ($fld_blockinfo && !is_null($row->ipb_by_text)) { $data['blockid'] = (int) $row->ipb_id; $data['blockedby'] = $row->ipb_by_text; $data['blockedbyid'] = (int) $row->ipb_by; $data['blockedtimestamp'] = wfTimestamp(TS_ISO_8601, $row->ipb_timestamp); $data['blockreason'] = $row->ipb_reason; $data['blockexpiry'] = $row->ipb_expiry; } if ($row->ipb_deleted) { $data['hidden'] = true; } if ($fld_editcount) { $data['editcount'] = intval($row->user_editcount); } if ($params['activeusers']) { $data['recentactions'] = intval($row->recentactions); // @todo 'recenteditcount' is set for BC, remove in 1.25 $data['recenteditcount'] = $data['recentactions']; } if ($fld_registration) { $data['registration'] = $row->user_registration ? wfTimestamp(TS_ISO_8601, $row->user_registration) : ''; } if ($fld_implicitgroups || $fld_groups || $fld_rights) { $implicitGroups = User::newFromId($row->user_id)->getAutomaticGroups(); if (isset($row->groups) && $row->groups !== '') { $groups = array_merge($implicitGroups, explode('|', $row->groups)); } else { $groups = $implicitGroups; } if ($fld_groups) { $data['groups'] = $groups; ApiResult::setIndexedTagName($data['groups'], 'g'); ApiResult::setArrayType($data['groups'], 'array'); } if ($fld_implicitgroups) { $data['implicitgroups'] = $implicitGroups; ApiResult::setIndexedTagName($data['implicitgroups'], 'g'); ApiResult::setArrayType($data['implicitgroups'], 'array'); } if ($fld_rights) { $data['rights'] = User::getGroupPermissions($groups); ApiResult::setIndexedTagName($data['rights'], 'r'); ApiResult::setArrayType($data['rights'], 'array'); } } $fit = $result->addValue(array('query', $this->getModuleName()), null, $data); if (!$fit) { $this->setContinueEnumParameter('from', $data['name']); break; } } $result->addIndexedTagName(array('query', $this->getModuleName()), 'u'); }
function getUserCond() { $condition = array(); $join_conds = array(); $tables = array('revision', 'page', 'user'); if ($this->contribs == 'newbie') { $max = $this->mDb->selectField('user', 'max(user_id)', false, __METHOD__); $condition[] = 'rev_user >' . (int) ($max - $max / 100); $index = 'user_timestamp'; # ignore local groups with the bot right # @todo FIXME: Global groups may have 'bot' rights $groupsWithBotPermission = User::getGroupsWithPermission('bot'); if (count($groupsWithBotPermission)) { $tables[] = 'user_groups'; $condition[] = 'ug_group IS NULL'; $join_conds['user_groups'] = array('LEFT JOIN', array('ug_user = rev_user', 'ug_group' => $groupsWithBotPermission)); } } else { $uid = User::idFromName($this->target); if ($uid) { $condition['rev_user'] = $uid; $index = 'user_timestamp'; } else { $condition['rev_user_text'] = $this->target; $index = 'usertext_timestamp'; } } if ($this->deletedOnly) { $condition[] = "rev_deleted != '0'"; } if ($this->topOnly) { $condition[] = "rev_id = page_latest"; } return array($tables, $index, $condition, $join_conds); }
/** * Retrieve recently uploaded images from this wiki. This will filter out video files * and images uploaded by bots if necessary. Additionally, arbitrary constraints can * be passed in to filter out additional images. These constraints can be either of: * * $constrain[] = "img_name = 'bar'" * $constrain['img_minor_mime'] = 'youtube' * * @param int $limit Limit the number of images to return * @param int $offset Grab images after an offset. Used with $limit to page the results * @param array $constrain An array of constraint/value that will be used in the query * @return array An array of images */ function getImages($limit, $offset = 0, $constrain = array()) { // Load the next set of images, eliminating images uploaded by bots as // well as eliminating any video files $dbr = wfGetDB(DB_SLAVE); $image = $dbr->tableName('image'); $sql = 'SELECT img_size, img_name, img_user, img_user_text, img_description, img_timestamp ' . "FROM {$image}"; $botconds = array(); foreach (User::getGroupsWithPermission('bot') as $groupname) { $botconds[] = 'ug_group = ' . $dbr->addQuotes($groupname); } $where = array(); if (count($botconds)) { $isbotmember = $dbr->makeList($botconds, LIST_OR); // LEFT join to the user_groups table on being a bot and then make sure // we get null rows back (i.e. we're not a bot) $ug = $dbr->tableName('user_groups'); $sql .= " LEFT JOIN {$ug} ON img_user=ug_user AND ({$isbotmember})"; $where[] = 'ug_group IS NULL'; } // Eliminate videos from this listing $where[] = 'img_media_type != \'VIDEO\''; $where[] = 'img_major_mime != \'video\''; $where[] = 'img_media_type != \'swf\''; // Add any additional constraints if ($constrain) { foreach ($constrain as $cond) { $where[] = $cond; } } $sql .= ' WHERE ' . $dbr->makeList($where, LIST_AND); $sql .= ' ORDER BY img_timestamp DESC '; $sql .= ' LIMIT ' . ($limit + 1); if ($offset) { $sql .= " OFFSET {$offset}"; } $res = $dbr->query($sql, __FUNCTION__); $images = array(); while ($s = $dbr->fetchObject($res)) { $images[] = $s; } $dbr->freeResult($res); // Load the images into a new gallery $gallery = new WikiaPhotoGallery(); $gallery->parseParams(array("rowdivider" => true, "hideoverflow" => true)); $gallery->setWidths(212); $foundImages = 0; foreach ($images as $s) { $foundImages++; if ($foundImages > $limit) { // One extra just to test for whether to show a page link; // don't actually show it. break; } $nt = Title::newFromText($s->img_name, NS_FILE); $gallery->add($nt); } $info = array("gallery" => $gallery); // Set pagination information if ($offset > 0) { $info['prev'] = $offset - $limit; } if ($foundImages > $limit) { $info['next'] = $offset + $limit; } return $info; }
/** * Determine which restriction levels it makes sense to use in a namespace, * optionally filtered by a user's rights. * * @since 1.23 * @param int $index Index to check * @param User $user User to check * @return array */ public static function getRestrictionLevels($index, User $user = null) { global $wgNamespaceProtection, $wgRestrictionLevels; if (!isset($wgNamespaceProtection[$index])) { // All levels are valid if there's no namespace restriction. // But still filter by user, if necessary $levels = $wgRestrictionLevels; if ($user) { $levels = array_values(array_filter($levels, function ($level) use($user) { $right = $level; if ($right == 'sysop') { $right = 'editprotected'; // BC } if ($right == 'autoconfirmed') { $right = 'editsemiprotected'; // BC } return $right == '' || $user->isAllowed($right); })); } return $levels; } // First, get the list of groups that can edit this namespace. $namespaceGroups = []; $combine = 'array_merge'; foreach ((array) $wgNamespaceProtection[$index] as $right) { if ($right == 'sysop') { $right = 'editprotected'; // BC } if ($right == 'autoconfirmed') { $right = 'editsemiprotected'; // BC } if ($right != '') { $namespaceGroups = call_user_func($combine, $namespaceGroups, User::getGroupsWithPermission($right)); $combine = 'array_intersect'; } } // Now, keep only those restriction levels where there is at least one // group that can edit the namespace but would be blocked by the // restriction. $usableLevels = ['']; foreach ($wgRestrictionLevels as $level) { $right = $level; if ($right == 'sysop') { $right = 'editprotected'; // BC } if ($right == 'autoconfirmed') { $right = 'editsemiprotected'; // BC } if ($right != '' && (!$user || $user->isAllowed($right)) && array_diff($namespaceGroups, User::getGroupsWithPermission($right))) { $usableLevels[] = $level; } } return $usableLevels; }
function getQueryInfo() { $conds = $jconds = array(); $tables = array('image'); if (!$this->showBots) { $groupsWithBotPermission = User::getGroupsWithPermission('bot'); if (count($groupsWithBotPermission)) { $tables[] = 'user_groups'; $conds[] = 'ug_group IS NULL'; $jconds['user_groups'] = array('LEFT JOIN', array('ug_group' => $groupsWithBotPermission, 'ug_user = img_user')); } } if ($this->hidePatrolled) { $tables[] = 'recentchanges'; $conds['rc_type'] = RC_LOG; $conds['rc_log_type'] = 'upload'; $conds['rc_patrolled'] = 0; $jconds['recentchanges'] = array('INNER JOIN', array('rc_title = img_name', 'rc_user = img_user', 'rc_timestamp = img_timestamp')); } if (!$this->getConfig()->get('MiserMode') && $this->like !== null) { $dbr = wfGetDB(DB_SLAVE); $likeObj = Title::newFromText($this->like); if ($likeObj instanceof Title) { $like = $dbr->buildLike($dbr->anyString(), strtolower($likeObj->getDBkey()), $dbr->anyString()); $conds[] = "LOWER(img_name) {$like}"; } } $query = array('tables' => $tables, 'fields' => '*', 'join_conds' => $jconds, 'conds' => $conds); return $query; }
/** * @return mixed|string */ public function reallyMakeHelpMsg() { $this->setHelp(); // Use parent to make default message for the main module $msg = parent::makeHelpMsg(); $astriks = str_repeat('*** ', 14); $msg .= "\n\n{$astriks} Modules {$astriks}\n\n"; foreach (array_keys($this->mModules) as $moduleName) { $module = new $this->mModules[$moduleName]($this, $moduleName); $msg .= self::makeHelpMsgHeader($module, 'action'); $msg2 = $module->makeHelpMsg(); if ($msg2 !== false) { $msg .= $msg2; } $msg .= "\n"; } $msg .= "\n{$astriks} Permissions {$astriks}\n\n"; foreach (self::$mRights as $right => $rightMsg) { $groups = User::getGroupsWithPermission($right); $msg .= "* " . $right . " *\n " . wfMsgReplaceArgs($rightMsg['msg'], $rightMsg['params']) . "\nGranted to:\n " . str_replace('*', 'all', implode(', ', $groups)) . "\n\n"; } $msg .= "\n{$astriks} Formats {$astriks}\n\n"; foreach (array_keys($this->mFormats) as $formatName) { $module = $this->createPrinterByName($formatName); $msg .= self::makeHelpMsgHeader($module, 'format'); $msg2 = $module->makeHelpMsg(); if ($msg2 !== false) { $msg .= $msg2; } $msg .= "\n"; } $msg .= "\n*** Credits: ***\n " . implode("\n ", $this->getCredits()) . "\n"; return $msg; }
function __construct($permission) { global $wgLang; $this->permission = $permission; $groups = array_map(array('User', 'makeGroupLinkWiki'), User::getGroupsWithPermission($this->permission)); if ($groups) { parent::__construct('badaccess', 'badaccess-groups', array($wgLang->commaList($groups), count($groups))); } else { parent::__construct('badaccess', 'badaccess-group0'); } }
/** * Rebuild pass 4: Mark bot and autopatrolled entries. */ private function rebuildRecentChangesTablePass4() { global $wgUseRCPatrol; $dbw = $this->getDB(DB_MASTER); list($recentchanges, $usergroups, $user) = $dbw->tableNamesN('recentchanges', 'user_groups', 'user'); $botgroups = User::getGroupsWithPermission('bot'); $autopatrolgroups = $wgUseRCPatrol ? User::getGroupsWithPermission('autopatrol') : array(); # Flag our recent bot edits if (!empty($botgroups)) { $botwhere = $dbw->makeList($botgroups); $botusers = array(); $this->output("Flagging bot account edits...\n"); # Find all users that are bots $sql = "SELECT DISTINCT user_name FROM {$usergroups}, {$user} " . "WHERE ug_group IN({$botwhere}) AND user_id = ug_user"; $res = $dbw->query($sql, DB_MASTER); foreach ($res as $obj) { $botusers[] = $dbw->addQuotes($obj->user_name); } # Fill in the rc_bot field if (!empty($botusers)) { $botwhere = implode(',', $botusers); $sql2 = "UPDATE {$recentchanges} SET rc_bot=1 " . "WHERE rc_user_text IN({$botwhere})"; $dbw->query($sql2); } } global $wgMiserMode; # Flag our recent autopatrolled edits if (!$wgMiserMode && !empty($autopatrolgroups)) { $patrolwhere = $dbw->makeList($autopatrolgroups); $patrolusers = array(); $this->output("Flagging auto-patrolled edits...\n"); # Find all users in RC with autopatrol rights $sql = "SELECT DISTINCT user_name FROM {$usergroups}, {$user} " . "WHERE ug_group IN({$patrolwhere}) AND user_id = ug_user"; $res = $dbw->query($sql, DB_MASTER); foreach ($res as $obj) { $patrolusers[] = $dbw->addQuotes($obj->user_name); } # Fill in the rc_patrolled field if (!empty($patrolusers)) { $patrolwhere = implode(',', $patrolusers); $sql2 = "UPDATE {$recentchanges} SET rc_patrolled=1 " . "WHERE rc_user_text IN({$patrolwhere})"; $dbw->query($sql2); } } }
function getUserCond() { $condition = []; $join_conds = []; $tables = ['revision', 'page', 'user']; $index = false; if ($this->contribs == 'newbie') { $max = $this->mDb->selectField('user', 'max(user_id)', false, __METHOD__); $condition[] = 'rev_user >' . (int) ($max - $max / 100); # ignore local groups with the bot right # @todo FIXME: Global groups may have 'bot' rights $groupsWithBotPermission = User::getGroupsWithPermission('bot'); if (count($groupsWithBotPermission)) { $tables[] = 'user_groups'; $condition[] = 'ug_group IS NULL'; $join_conds['user_groups'] = ['LEFT JOIN', ['ug_user = rev_user', 'ug_group' => $groupsWithBotPermission]]; } // (T140537) Disallow looking too far in the past for 'newbies' queries. If the user requested // a timestamp offset far in the past such that there are no edits by users with user_ids in // the range, we would end up scanning all revisions from that offset until start of time. $condition[] = 'rev_timestamp > ' . $this->mDb->addQuotes($this->mDb->timestamp(wfTimestamp() - 30 * 24 * 60 * 60)); } else { $uid = User::idFromName($this->target); if ($uid) { $condition['rev_user'] = $uid; $index = 'user_timestamp'; } else { $condition['rev_user_text'] = $this->target; $index = 'usertext_timestamp'; } } if ($this->deletedOnly) { $condition[] = 'rev_deleted != 0'; } if ($this->topOnly) { $condition[] = 'rev_id = page_latest'; } if ($this->newOnly) { $condition[] = 'rev_parent_id = 0'; } if ($this->hideMinor) { $condition[] = 'rev_minor_edit = 0'; } return [$tables, $index, $condition, $join_conds]; }
/** * @file * @ingroup SpecialPage * FIXME: this code is crap, should use Pager and Database::select(). */ function wfSpecialNewimages($par, $specialPage) { global $wgUser, $wgOut, $wgLang, $wgRequest, $wgMiserMode; $wpIlMatch = $wgRequest->getText('wpIlMatch'); $dbr = wfGetDB(DB_SLAVE); $sk = $wgUser->getSkin(); $shownav = !$specialPage->including(); $hidebots = $wgRequest->getBool('hidebots', 1); $hidebotsql = ''; if ($hidebots) { # Make a list of group names which have the 'bot' flag set. $botconds = array(); foreach (User::getGroupsWithPermission('bot') as $groupname) { $botconds[] = 'ug_group = ' . $dbr->addQuotes($groupname); } # If not bot groups, do not set $hidebotsql if ($botconds) { $isbotmember = $dbr->makeList($botconds, LIST_OR); # This join, in conjunction with WHERE ug_group IS NULL, returns # only those rows from IMAGE where the uploading user is not a mem- # ber of a group which has the 'bot' permission set. $ug = $dbr->tableName('user_groups'); $hidebotsql = " LEFT JOIN {$ug} ON img_user=ug_user AND ({$isbotmember})"; } } $image = $dbr->tableName('image'); $sql = "SELECT img_timestamp from {$image}"; if ($hidebotsql) { $sql .= "{$hidebotsql} WHERE ug_group IS NULL"; } $sql .= ' ORDER BY img_timestamp DESC LIMIT 1'; $res = $dbr->query($sql, __FUNCTION__); $row = $dbr->fetchRow($res); if ($row !== false) { $ts = $row[0]; } else { $ts = false; } $dbr->freeResult($res); $sql = ''; # If we were clever, we'd use this to cache. $latestTimestamp = wfTimestamp(TS_MW, $ts); # Hardcode this for now. $limit = 48; if ($parval = intval($par)) { if ($parval <= $limit && $parval > 0) { $limit = $parval; } } $where = array(); $searchpar = ''; if ($wpIlMatch != '' && !$wgMiserMode) { $nt = Title::newFromUrl($wpIlMatch); if ($nt) { $m = $dbr->escapeLike(strtolower($nt->getDBkey())); $where[] = "LOWER(img_name) LIKE '%{$m}%'"; $searchpar = '&wpIlMatch=' . urlencode($wpIlMatch); } } $invertSort = false; if ($until = $wgRequest->getVal('until')) { $where[] = "img_timestamp < '" . $dbr->timestamp($until) . "'"; } if ($from = $wgRequest->getVal('from')) { $where[] = "img_timestamp >= '" . $dbr->timestamp($from) . "'"; $invertSort = true; } $sql = 'SELECT img_size, img_name, img_user, img_user_text,' . "img_description,img_timestamp FROM {$image}"; if ($hidebotsql) { $sql .= $hidebotsql; $where[] = 'ug_group IS NULL'; } if (count($where)) { $sql .= ' WHERE ' . $dbr->makeList($where, LIST_AND); } $sql .= ' ORDER BY img_timestamp ' . ($invertSort ? '' : ' DESC'); $sql .= ' LIMIT ' . ($limit + 1); $res = $dbr->query($sql, __FUNCTION__); /** * We have to flip things around to get the last N after a certain date */ $images = array(); while ($s = $dbr->fetchObject($res)) { if ($invertSort) { array_unshift($images, $s); } else { array_push($images, $s); } } $dbr->freeResult($res); $gallery = new ImageGallery(); $firstTimestamp = null; $lastTimestamp = null; $shownImages = 0; foreach ($images as $s) { $shownImages++; if ($shownImages > $limit) { # One extra just to test for whether to show a page link; # don't actually show it. break; } $name = $s->img_name; $ut = $s->img_user_text; $nt = Title::newFromText($name, NS_FILE); $ul = $sk->makeLinkObj(Title::makeTitle(NS_USER, $ut), $ut); $gallery->add($nt, "{$ul}<br />\n<i>" . $wgLang->timeanddate($s->img_timestamp, true) . "</i><br />\n"); $timestamp = wfTimestamp(TS_MW, $s->img_timestamp); if (empty($firstTimestamp)) { $firstTimestamp = $timestamp; } $lastTimestamp = $timestamp; } $titleObj = SpecialPage::getTitleFor('Newimages'); $action = $titleObj->getLocalURL($hidebots ? '' : 'hidebots=0'); if ($shownav && !$wgMiserMode) { $wgOut->addHTML(Xml::openElement('form', array('action' => $action, 'method' => 'post', 'id' => 'imagesearch')) . Xml::fieldset(wfMsg('newimages-legend')) . Xml::inputLabel(wfMsg('newimages-label'), 'wpIlMatch', 'wpIlMatch', 20, $wpIlMatch) . ' ' . Xml::submitButton(wfMsg('ilsubmit'), array('name' => 'wpIlSubmit')) . Xml::closeElement('fieldset') . Xml::closeElement('form')); } $bydate = wfMsg('bydate'); $lt = $wgLang->formatNum(min($shownImages, $limit)); if ($shownav) { $text = wfMsgExt('imagelisttext', array('parse'), $lt, $bydate); $wgOut->addHTML($text . "\n"); } /** * Paging controls... */ # If we change bot visibility, this needs to be carried along. if (!$hidebots) { $botpar = '&hidebots=0'; } else { $botpar = ''; } $now = wfTimestampNow(); $d = $wgLang->date($now, true); $t = $wgLang->time($now, true); $dateLink = $sk->makeKnownLinkObj($titleObj, wfMsgHtml('sp-newimages-showfrom', $d, $t), 'from=' . $now . $botpar . $searchpar); $botLink = $sk->makeKnownLinkObj($titleObj, wfMsgHtml('showhidebots', $hidebots ? wfMsgHtml('show') : wfMsgHtml('hide')), 'hidebots=' . ($hidebots ? '0' : '1') . $searchpar); $opts = array('parsemag', 'escapenoentities'); $prevLink = wfMsgExt('pager-newer-n', $opts, $wgLang->formatNum($limit)); if ($firstTimestamp && $firstTimestamp != $latestTimestamp) { $prevLink = $sk->makeKnownLinkObj($titleObj, $prevLink, 'from=' . $firstTimestamp . $botpar . $searchpar); } $nextLink = wfMsgExt('pager-older-n', $opts, $wgLang->formatNum($limit)); if ($shownImages > $limit && $lastTimestamp) { $nextLink = $sk->makeKnownLinkObj($titleObj, $nextLink, 'until=' . $lastTimestamp . $botpar . $searchpar); } $prevnext = '<p>' . $botLink . ' ' . wfMsgHtml('viewprevnext', $prevLink, $nextLink, $dateLink) . '</p>'; if ($shownav) { $wgOut->addHTML($prevnext); } if (count($images)) { $wgOut->addHTML($gallery->toHTML()); if ($shownav) { $wgOut->addHTML($prevnext); } } else { $wgOut->addWikiMsg('noimages'); } }
public function modifyHelp(array &$help, array $options, array &$tocData) { // Wish PHP had an "array_insert_before". Instead, we have to manually // reindex the array to get 'permissions' in the right place. $oldHelp = $help; $help = []; foreach ($oldHelp as $k => $v) { if ($k === 'submodules') { $help['permissions'] = ''; } $help[$k] = $v; } $help['datatypes'] = ''; $help['credits'] = ''; // Fill 'permissions' $help['permissions'] .= Html::openElement('div', ['class' => 'apihelp-block apihelp-permissions']); $m = $this->msg('api-help-permissions'); if (!$m->isDisabled()) { $help['permissions'] .= Html::rawElement('div', ['class' => 'apihelp-block-head'], $m->numParams(count(self::$mRights))->parse()); } $help['permissions'] .= Html::openElement('dl'); foreach (self::$mRights as $right => $rightMsg) { $help['permissions'] .= Html::element('dt', null, $right); $rightMsg = $this->msg($rightMsg['msg'], $rightMsg['params'])->parse(); $help['permissions'] .= Html::rawElement('dd', null, $rightMsg); $groups = array_map(function ($group) { return $group == '*' ? 'all' : $group; }, User::getGroupsWithPermission($right)); $help['permissions'] .= Html::rawElement('dd', null, $this->msg('api-help-permissions-granted-to')->numParams(count($groups))->params($this->getLanguage()->commaList($groups))->parse()); } $help['permissions'] .= Html::closeElement('dl'); $help['permissions'] .= Html::closeElement('div'); // Fill 'datatypes' and 'credits', if applicable if (empty($options['nolead'])) { $level = $options['headerlevel']; $tocnumber =& $options['tocnumber']; $header = $this->msg('api-help-datatypes-header')->parse(); // Add an additional span with sanitized ID if (!$this->getConfig()->get('ExperimentalHtmlIds')) { $header = Html::element('span', ['id' => Sanitizer::escapeId('main/datatypes')]) . $header; } $help['datatypes'] .= Html::rawElement('h' . min(6, $level), ['id' => 'main/datatypes', 'class' => 'apihelp-header'], $header); $help['datatypes'] .= $this->msg('api-help-datatypes')->parseAsBlock(); if (!isset($tocData['main/datatypes'])) { $tocnumber[$level]++; $tocData['main/datatypes'] = ['toclevel' => count($tocnumber), 'level' => $level, 'anchor' => 'main/datatypes', 'line' => $header, 'number' => implode('.', $tocnumber), 'index' => false]; } // Add an additional span with sanitized ID if (!$this->getConfig()->get('ExperimentalHtmlIds')) { $header = Html::element('span', ['id' => Sanitizer::escapeId('main/credits')]) . $header; } $header = $this->msg('api-credits-header')->parse(); $help['credits'] .= Html::rawElement('h' . min(6, $level), ['id' => 'main/credits', 'class' => 'apihelp-header'], $header); $help['credits'] .= $this->msg('api-credits')->useDatabase(false)->parseAsBlock(); if (!isset($tocData['main/credits'])) { $tocnumber[$level]++; $tocData['main/credits'] = ['toclevel' => count($tocnumber), 'level' => $level, 'anchor' => 'main/credits', 'line' => $header, 'number' => implode('.', $tocnumber), 'index' => false]; } } }