protected function getPasswordResetData($username, $row) { $now = wfTimestamp(); $expiration = wfTimestampOrNull(TS_UNIX, $row->user_password_expires); if ($expiration === null || $expiration >= $now) { return null; } $grace = $this->config->get('PasswordExpireGrace'); if ($expiration + $grace < $now) { $data = ['hard' => true, 'msg' => \Status::newFatal('resetpass-expired')->getMessage()]; } else { $data = ['hard' => false, 'msg' => \Status::newFatal('resetpass-expired-soft')->getMessage()]; } return (object) $data; }
/** * LoginHistoryHook * * store information about when & where user logged in, for stats * purposes, called by Hook UserLoginComplete and UserLoadFromSessionInfo * Data is stored in external storage archive1 * * @author Krzysztof Krzyżaniak (eloy) <*****@*****.**> * @access public * @static * * @param Integer $from -- which hook call this * @param User $user -- User class instance * @param String $type -- UserLoadFromSessionInfo set this to 'cookie' or 'session' * * @return bool true process other hooks */ public static function LoginHistoryHook($from, $user, $type = false) { global $wgCityId; #--- private wikia identifier, you can use wgDBname global $wgEnableScribeReport, $wgStatsDB, $wgStatsDBEnabled; if (wfReadOnly()) { return true; } wfProfileIn(__METHOD__); /** * if user id is empty it means that user object is not loaded * store information only for registered users */ if (!empty($user) && is_object($user)) { $id = $user->getId(); if ($id) { if ($from == self::LOGIN_AUTO && $type == "session") { # ignore } else { $params = array("user_id" => $id, "city_id" => $wgCityId, "ulh_from" => $from, "ulh_rememberme" => $user->getOption('rememberpassword')); if (!empty($wgEnableScribeReport)) { # use scribe try { $message = array('method' => 'login', 'params' => $params); $data = json_encode($message); WScribeClient::singleton('trigger')->send($data); } catch (TException $e) { Wikia::log(__METHOD__, 'scribeClient exception', $e->getMessage()); } } else { # use database if (!empty($wgStatsDBEnabled)) { $dbw = wfGetDB(DB_MASTER, array(), $wgStatsDB); $dbw->insert("user_login_history", $params, __METHOD__, array('IGNORE')); $dbw->replace("user_login_history_summary", array('user_id'), array('ulh_timestamp' => wfTimestampOrNull(), 'user_id' => $id), __METHOD__); if ($dbw->getFlag(DBO_TRX)) { $dbw->commit(__METHOD__); } } } } } } wfProfileOut(__METHOD__); return true; }
/** * LoginHistoryHook * * store information about when & where user logged in, for stats * purposes, called by Hook UserLoginComplete and UserLoadFromSessionInfo * Data is stored in external storage archive1 * * @author Krzysztof Krzyżaniak (eloy) <*****@*****.**> * @access public * @static * * @param Integer $from -- which hook call this * @param User $user -- User class instance * @param String $type -- UserLoadFromSessionInfo set this to 'cookie' or 'session' * * @return bool true process other hooks */ public static function LoginHistoryHook($from, $user, $type = false) { global $wgCityId; #--- private wikia identifier, you can use wgDBname global $wgEnableScribeReport, $wgSpecialsDB; if (wfReadOnly()) { return true; } wfProfileIn(__METHOD__); /** * if user id is empty it means that user object is not loaded * store information only for registered users */ if (!empty($user) && is_object($user)) { $id = $user->getId(); if ($id) { if ($from == self::LOGIN_AUTO && $type == "session") { # ignore } else { $params = array("user_id" => $id, "city_id" => $wgCityId, "ulh_from" => $from, "ulh_rememberme" => $user->getGlobalPreference('rememberpassword')); if (!empty($wgEnableScribeReport)) { # use scribe try { $message = array('method' => 'login', 'params' => $params); $data = json_encode($message); WScribeClient::singleton('trigger')->send($data); } catch (TException $e) { Wikia\Logger\WikiaLogger::instance()->error(__METHOD__ . ' - scribeClient exception', ['exception' => $e]); } } else { // user_login_history_summary is used in joins with specials.events_local_users table // @see PLATFORM-1309 $dbw_specials = wfGetDB(DB_MASTER, array(), $wgSpecialsDB); $dbw_specials->insert("user_login_history", $params, __METHOD__, array('IGNORE')); $dbw_specials->replace("user_login_history_summary", array('user_id'), array('ulh_timestamp' => wfTimestampOrNull(), 'user_id' => $id), __METHOD__); $dbw_specials->commit(__METHOD__); } } } } wfProfileOut(__METHOD__); return true; }
/** * Get image list * * @param integer $userId ID of the user to get the list for * @param string $from Timestamp to get images before * @return array List of images */ public function getImageList($userId, $from = null) { wfProfileIn(__METHOD__); $imageList = []; $db = $this->getDatawareDB(DB_MASTER); $where = ['user_id' => $userId, 'state != ' . ImageReviewStatuses::STATE_DELETED . ' AND state != ' . ImageReviewStatuses::STATE_WIKI_DISABLED]; $from = wfTimestampOrNull(TS_DB, $from); if (!empty($from)) { $where[] = 'last_edited < ' . $db->addQuotes($from); } $result = $db->select(['image_review'], ['wiki_id, page_id, state, flags, priority, last_edited'], $where, __METHOD__, ['ORDER BY' => 'last_edited desc', 'LIMIT' => self::LIMIT_IMAGES]); foreach ($result as $row) { $img = ImagesService::getImageSrc($row->wiki_id, $row->page_id); $wikiRow = WikiFactory::getWikiByID($row->wiki_id); $extension = pathinfo(strtolower($img['page']), PATHINFO_EXTENSION); $isThumb = true; if (empty($img['src'])) { // If we don't have a thumb by this point, we still need to display something, fall back to placeholder $globalTitle = GlobalTitle::newFromId($row->page_id, $row->wiki_id); if (is_object($globalTitle)) { $img['page'] = $globalTitle->getFullUrl(); // @TODO this should be taken from the code instead of being hardcoded $img['src'] = '//images.wikia.com/central/images/8/8c/Wikia_image_placeholder.png'; } else { // This should never happen continue; } } if (in_array($extension, ['gif', 'svg'])) { $img = ImagesService::getImageOriginalUrl($row->wiki_id, $row->page_id); $isThumb = false; } $imageList[] = ['wikiId' => $row->wiki_id, 'pageId' => $row->page_id, 'state' => $row->state, 'src' => $img['src'], 'priority' => $row->priority, 'url' => $img['page'], 'isthumb' => $isThumb, 'flags' => $row->flags, 'wiki_url' => isset($wikiRow->city_url) ? $wikiRow->city_url : '', 'user_page' => '', 'last_edited' => $row->last_edited]; } $db->freeResult($result); wfProfileOut(__METHOD__); return $imageList; }
/** * Check that a temporary password is still valid (hasn't expired). * @param string $timestamp A timestamp in MediaWiki (TS_MW) format * @return bool */ protected function isTimestampValid($timestamp) { $time = wfTimestampOrNull(TS_MW, $timestamp); if ($time !== null) { $expiry = wfTimestamp(TS_UNIX, $time) + $this->newPasswordExpiry; if (time() >= $expiry) { return false; } } return true; }
/** * Called on BeforeParserFetchFileAndTitle hook * Changes links and thumbnails of files to point to the approved revision in all cases except * the primary file on file pages (e.g. the big image in the top left on File:My File.png). To * modify that image see self::onImagePageFindFile() **/ public static function modifyFileLinks($parser, Title $fileTitle, &$options, &$query) { if ($fileTitle->getNamespace() == NS_MEDIA) { $fileTitle = Title::makeTitle(NS_FILE, $fileTitle->getDBkey()); $fileTitle->resetArticleId($fileTitle->getArticleID()); // avoid extra queries // Media link redirects don't get caught by the normal redirect check, so this // extra check is required if ($temp = WikiPage::newFromID($fileTitle->getArticleID())->getRedirectTarget()) { $fileTitle = $temp; unset($temp); } } if ($fileTitle->isRedirect()) { $page = WikiPage::newFromID($fileTitle->getArticleID()); $fileTitle = $page->getRedirectTarget(); $fileTitle->resetArticleId($fileTitle->getArticleID()); // avoid extra queries } # Tell Parser what file version to use list($approvedRevTimestamp, $approvedRevSha1) = ApprovedRevs::getApprovedFileInfo($fileTitle); // no valid approved timestamp or sha1, so don't modify image or image link if (!$approvedRevTimestamp || !$approvedRevSha1) { return true; } $options['time'] = wfTimestampOrNull(TS_MW, $approvedRevTimestamp); $options['sha1'] = $approvedRevSha1; // $options['broken'] = true; // breaks the link? was in FlaggedRevs...why would we want to do this? # Stabilize the file link if ($query != '') { $query .= '&'; } $query .= "filetimestamp=" . urlencode(wfTimestamp(TS_MW, $approvedRevTimestamp)); return true; }
/** * Fetch a row of user data needed for migration. * * @param $wikiID String * @throws Exception if local user not found * @return array */ protected function localUserData($wikiID) { $lb = wfGetLB($wikiID); $db = $lb->getConnection(DB_SLAVE, array(), $wikiID); $fields = array('user_id', 'user_email', 'user_email_authenticated', 'user_password', 'user_editcount', 'user_registration'); $conds = array('user_name' => $this->mName); $row = $db->selectRow('user', $fields, $conds, __METHOD__); if (!$row) { # Row missing from slave, try the master instead $lb->reuseConnection($db); $db = $lb->getConnection(DB_MASTER, array(), $wikiID); $row = $db->selectRow('user', $fields, $conds, __METHOD__); } if (!$row) { $lb->reuseConnection($db); throw new Exception("Could not find local user data for {$this->mName}@{$wikiID}"); } /** @var $row object */ $data = array('wiki' => $wikiID, 'id' => $row->user_id, 'email' => $row->user_email, 'emailAuthenticated' => wfTimestampOrNull(TS_MW, $row->user_email_authenticated), 'registration' => wfTimestampOrNull(TS_MW, $row->user_registration), 'password' => $row->user_password, 'editCount' => $row->user_editcount, 'groups' => array(), 'blocked' => false); // Edit count field may not be initialized... if (is_null($row->user_editcount)) { $data['editCount'] = $db->selectField('revision', 'COUNT(*)', array('rev_user' => $data['id']), __METHOD__); } // And we have to fetch groups separately, sigh... $result = $db->select('user_groups', array('ug_group'), array('ug_user' => $data['id']), __METHOD__); foreach ($result as $row) { $data['groups'][] = $row->ug_group; } $result->free(); // And while we're in here, look for user blocks :D $result = $db->select('ipblocks', array('ipb_expiry', 'ipb_reason', 'ipb_block_email', 'ipb_anon_only', 'ipb_create_account', 'ipb_enable_autoblock', 'ipb_allow_usertalk'), array('ipb_user' => $data['id']), __METHOD__); global $wgLang; foreach ($result as $row) { if ($wgLang->formatExpiry($row->ipb_expiry, TS_MW) > wfTimestampNow()) { $data['block-expiry'] = $row->ipb_expiry; $data['block-reason'] = $row->ipb_reason; $data['block-anononly'] = (bool) $row->ipb_anon_only; $data['block-nocreate'] = (bool) $row->ipb_create_account; $data['block-noautoblock'] = !(bool) $row->ipb_enable_autoblock; $data['block-nousertalk'] = !(bool) $row->ipb_allow_usertalk; // Poorly named database column $data['block-noemail'] = (bool) $row->ipb_block_email; $data['blocked'] = true; } } $result->free(); $lb->reuseConnection($db); return $data; }
public function execute() { $params = $this->extractRequestParams(); if (!is_null($params['prop'])) { $this->prop = array_flip($params['prop']); } else { $this->prop = []; } $users = (array) $params['users']; $goodNames = $done = []; $result = $this->getResult(); // Canonicalize user names foreach ($users as $u) { $n = User::getCanonicalName($u); if ($n === false || $n === '') { $vals = ['name' => $u, 'invalid' => true]; $fit = $result->addValue(['query', $this->getModuleName()], null, $vals); if (!$fit) { $this->setContinueEnumParameter('users', implode('|', array_diff($users, $done))); $goodNames = []; break; } $done[] = $u; } else { $goodNames[] = $n; } } $result = $this->getResult(); if (count($goodNames)) { $this->addTables('user'); $this->addFields(User::selectFields()); $this->addWhereFld('user_name', $goodNames); $this->showHiddenUsersAddBlockInfo(isset($this->prop['blockinfo'])); $data = []; $res = $this->select(__METHOD__); $this->resetQueryParams(); // get user groups if needed if (isset($this->prop['groups']) || isset($this->prop['rights'])) { $userGroups = []; $this->addTables('user'); $this->addWhereFld('user_name', $goodNames); $this->addTables('user_groups'); $this->addJoinConds(['user_groups' => ['INNER JOIN', 'ug_user=user_id']]); $this->addFields(['user_name', 'ug_group']); $userGroupsRes = $this->select(__METHOD__); foreach ($userGroupsRes as $row) { $userGroups[$row->user_name][] = $row->ug_group; } } foreach ($res as $row) { // create user object and pass along $userGroups if set // that reduces the number of database queries needed in User dramatically if (!isset($userGroups)) { $user = User::newFromRow($row); } else { if (!isset($userGroups[$row->user_name]) || !is_array($userGroups[$row->user_name])) { $userGroups[$row->user_name] = []; } $user = User::newFromRow($row, ['user_groups' => $userGroups[$row->user_name]]); } $name = $user->getName(); $data[$name]['userid'] = $user->getId(); $data[$name]['name'] = $name; if (isset($this->prop['editcount'])) { $data[$name]['editcount'] = $user->getEditCount(); } if (isset($this->prop['registration'])) { $data[$name]['registration'] = wfTimestampOrNull(TS_ISO_8601, $user->getRegistration()); } if (isset($this->prop['groups'])) { $data[$name]['groups'] = $user->getEffectiveGroups(); } if (isset($this->prop['implicitgroups'])) { $data[$name]['implicitgroups'] = $user->getAutomaticGroups(); } if (isset($this->prop['rights'])) { $data[$name]['rights'] = $user->getRights(); } if ($row->ipb_deleted) { $data[$name]['hidden'] = true; } if (isset($this->prop['blockinfo']) && !is_null($row->ipb_by_text)) { $data[$name]['blockid'] = (int) $row->ipb_id; $data[$name]['blockedby'] = $row->ipb_by_text; $data[$name]['blockedbyid'] = (int) $row->ipb_by; $data[$name]['blockedtimestamp'] = wfTimestamp(TS_ISO_8601, $row->ipb_timestamp); $data[$name]['blockreason'] = $row->ipb_reason; $data[$name]['blockexpiry'] = $row->ipb_expiry; } if (isset($this->prop['emailable'])) { $data[$name]['emailable'] = $user->canReceiveEmail(); } if (isset($this->prop['gender'])) { $gender = $user->getOption('gender'); if (strval($gender) === '') { $gender = 'unknown'; } $data[$name]['gender'] = $gender; } if (isset($this->prop['centralids'])) { $data[$name] += ApiQueryUserInfo::getCentralUserInfo($this->getConfig(), $user, $params['attachedwiki']); } if (!is_null($params['token'])) { $tokenFunctions = $this->getTokenFunctions(); foreach ($params['token'] as $t) { $val = call_user_func($tokenFunctions[$t], $user); if ($val === false) { $this->setWarning("Action '{$t}' is not allowed for the current user"); } else { $data[$name][$t . 'token'] = $val; } } } } } $context = $this->getContext(); // Second pass: add result data to $retval foreach ($goodNames as $u) { if (!isset($data[$u])) { $data[$u] = ['name' => $u]; $urPage = new UserrightsPage(); $urPage->setContext($context); $iwUser = $urPage->fetchUser($u); if ($iwUser instanceof UserRightsProxy) { $data[$u]['interwiki'] = true; if (!is_null($params['token'])) { $tokenFunctions = $this->getTokenFunctions(); foreach ($params['token'] as $t) { $val = call_user_func($tokenFunctions[$t], $iwUser); if ($val === false) { $this->setWarning("Action '{$t}' is not allowed for the current user"); } else { $data[$u][$t . 'token'] = $val; } } } } else { $data[$u]['missing'] = true; if (isset($this->prop['cancreate'])) { $status = MediaWiki\Auth\AuthManager::singleton()->canCreateAccount($u); $data[$u]['cancreate'] = $status->isGood(); if (!$status->isGood()) { $data[$u]['cancreateerror'] = $this->getErrorFormatter()->arrayFromStatus($status); } } } } else { if (isset($this->prop['groups']) && isset($data[$u]['groups'])) { ApiResult::setArrayType($data[$u]['groups'], 'array'); ApiResult::setIndexedTagName($data[$u]['groups'], 'g'); } if (isset($this->prop['implicitgroups']) && isset($data[$u]['implicitgroups'])) { ApiResult::setArrayType($data[$u]['implicitgroups'], 'array'); ApiResult::setIndexedTagName($data[$u]['implicitgroups'], 'g'); } if (isset($this->prop['rights']) && isset($data[$u]['rights'])) { ApiResult::setArrayType($data[$u]['rights'], 'array'); ApiResult::setIndexedTagName($data[$u]['rights'], 'r'); } } $fit = $result->addValue(['query', $this->getModuleName()], null, $data[$u]); if (!$fit) { $this->setContinueEnumParameter('users', implode('|', array_diff($users, $done))); break; } $done[] = $u; } $result->addIndexedTagName(['query', $this->getModuleName()], 'user'); }
/** * Load the object from a database row * * @since 1.20 * @param object|bool $data DB row containing fields returned by selectFields() or false * @param string|int $from One of the following: * - "fromdb" or WikiPage::READ_NORMAL if the data comes from a replica DB * - "fromdbmaster" or WikiPage::READ_LATEST if the data comes from the master DB * - "forupdate" or WikiPage::READ_LOCKING if the data comes from * the master DB using SELECT FOR UPDATE */ public function loadFromRow($data, $from) { $lc = LinkCache::singleton(); $lc->clearLink($this->mTitle); if ($data) { $lc->addGoodLinkObjFromRow($this->mTitle, $data); $this->mTitle->loadFromRow($data); // Old-fashioned restrictions $this->mTitle->loadRestrictions($data->page_restrictions); $this->mId = intval($data->page_id); $this->mTouched = wfTimestamp(TS_MW, $data->page_touched); $this->mLinksUpdated = wfTimestampOrNull(TS_MW, $data->page_links_updated); $this->mIsRedirect = intval($data->page_is_redirect); $this->mLatest = intval($data->page_latest); // Bug 37225: $latest may no longer match the cached latest Revision object. // Double-check the ID of any cached latest Revision object for consistency. if ($this->mLastRevision && $this->mLastRevision->getId() != $this->mLatest) { $this->mLastRevision = null; $this->mTimestamp = ''; } } else { $lc->addBadLinkObj($this->mTitle); $this->mTitle->loadFromRow(false); $this->clearCacheFields(); $this->mId = 0; } $this->mDataLoaded = true; $this->mDataLoadedFrom = self::convertSelectType($from); }
/** * Initialize this object from a row from the user table. * * @param stdClass $row Row from the user table to load. * @param array $data Further user data to load into the object * * user_groups Array with groups out of the user_groups table * user_properties Array with properties out of the user_properties table */ protected function loadFromRow($row, $data = null) { $all = true; $passwordFactory = self::getPasswordFactory(); $this->mGroups = null; // deferred if (isset($row->user_name)) { $this->mName = $row->user_name; $this->mFrom = 'name'; $this->setItemLoaded('name'); } else { $all = false; } if (isset($row->user_real_name)) { $this->mRealName = $row->user_real_name; $this->setItemLoaded('realname'); } else { $all = false; } if (isset($row->user_id)) { $this->mId = intval($row->user_id); $this->mFrom = 'id'; $this->setItemLoaded('id'); } else { $all = false; } if (isset($row->user_id) && isset($row->user_name)) { self::$idCacheByName[$row->user_name] = $row->user_id; } if (isset($row->user_editcount)) { $this->mEditCount = $row->user_editcount; } else { $all = false; } if (isset($row->user_password)) { // Check for *really* old password hashes that don't even have a type // The old hash format was just an md5 hex hash, with no type information if (preg_match('/^[0-9a-f]{32}$/', $row->user_password)) { $row->user_password = "******"; } try { $this->mPassword = $passwordFactory->newFromCiphertext($row->user_password); } catch (PasswordError $e) { wfDebug('Invalid password hash found in database.'); $this->mPassword = $passwordFactory->newFromCiphertext(null); } try { $this->mNewpassword = $passwordFactory->newFromCiphertext($row->user_newpassword); } catch (PasswordError $e) { wfDebug('Invalid password hash found in database.'); $this->mNewpassword = $passwordFactory->newFromCiphertext(null); } $this->mNewpassTime = wfTimestampOrNull(TS_MW, $row->user_newpass_time); $this->mPasswordExpires = wfTimestampOrNull(TS_MW, $row->user_password_expires); } if (isset($row->user_email)) { $this->mEmail = $row->user_email; $this->mTouched = wfTimestamp(TS_MW, $row->user_touched); $this->mToken = $row->user_token; if ($this->mToken == '') { $this->mToken = null; } $this->mEmailAuthenticated = wfTimestampOrNull(TS_MW, $row->user_email_authenticated); $this->mEmailToken = $row->user_email_token; $this->mEmailTokenExpires = wfTimestampOrNull(TS_MW, $row->user_email_token_expires); $this->mRegistration = wfTimestampOrNull(TS_MW, $row->user_registration); } else { $all = false; } if ($all) { $this->mLoadedItems = true; } if (is_array($data)) { if (isset($data['user_groups']) && is_array($data['user_groups'])) { $this->mGroups = $data['user_groups']; } if (isset($data['user_properties']) && is_array($data['user_properties'])) { $this->loadOptions($data['user_properties']); } } }
/** * Load user and user_group data from the database * $this->mId must be set, this is how the user is identified. * * @return true if the user exists, false if the user is anonymous * @private */ function loadFromDatabase() { # Paranoia $this->mId = intval($this->mId); /** Anonymous user */ if (!$this->mId) { $this->loadDefaults(); return false; } $dbr = wfGetDB(DB_MASTER); $s = $dbr->selectRow('user', '*', array('user_id' => $this->mId), __METHOD__); if ($s !== false) { # Initialise user table data $this->mName = $s->user_name; $this->mRealName = $s->user_real_name; $this->mPassword = $s->user_password; $this->mNewpassword = $s->user_newpassword; $this->mNewpassTime = wfTimestampOrNull(TS_MW, $s->user_newpass_time); $this->mEmail = $s->user_email; $this->decodeOptions($s->user_options); $this->mTouched = wfTimestamp(TS_MW, $s->user_touched); $this->mToken = $s->user_token; $this->mEmailAuthenticated = wfTimestampOrNull(TS_MW, $s->user_email_authenticated); $this->mEmailToken = $s->user_email_token; $this->mEmailTokenExpires = wfTimestampOrNull(TS_MW, $s->user_email_token_expires); $this->mRegistration = wfTimestampOrNull(TS_MW, $s->user_registration); $this->mEditCount = $s->user_editcount; $this->getEditCount(); // revalidation for nulls # Load group data $res = $dbr->select('user_groups', array('ug_group'), array('ug_user' => $this->mId), __METHOD__); $this->mGroups = array(); while ($row = $dbr->fetchObject($res)) { $this->mGroups[] = $row->ug_group; } return true; } else { # Invalid user_id $this->mId = 0; $this->loadDefaults(); return false; } }
public function execute() { $params = $this->extractRequestParams(); if (!is_null($params['prop'])) { $this->prop = array_flip($params['prop']); } else { $this->prop = array(); } $users = (array) $params['users']; $goodNames = $done = array(); $result = $this->getResult(); // Canonicalize user names foreach ($users as $u) { $n = User::getCanonicalName($u); if ($n === false || $n === '') { $vals = array('name' => $u, 'invalid' => ''); $fit = $result->addValue(array('query', $this->getModuleName()), null, $vals); if (!$fit) { $this->setContinueEnumParameter('users', implode('|', array_diff($users, $done))); $goodNames = array(); break; } $done[] = $u; } else { $goodNames[] = $n; } } if (count($goodNames)) { $this->addTables('user', 'u1'); $this->addFields('u1.*'); $this->addWhereFld('u1.user_name', $goodNames); if (isset($this->prop['groups'])) { $this->addTables('user_groups'); $this->addJoinConds(array('user_groups' => array('LEFT JOIN', 'ug_user=u1.user_id'))); $this->addFields('ug_group'); } $this->showHiddenUsersAddBlockInfo(isset($this->prop['blockinfo'])); $data = array(); $res = $this->select(__METHOD__); foreach ($res as $row) { $user = User::newFromRow($row); $name = $user->getName(); $data[$name]['name'] = $name; if (isset($this->prop['editcount'])) { $data[$name]['editcount'] = intval($user->getEditCount()); } if (isset($this->prop['registration'])) { $data[$name]['registration'] = wfTimestampOrNull(TS_ISO_8601, $user->getRegistration()); } if (isset($this->prop['groups']) && !is_null($row->ug_group)) { // This row contains only one group, others will be added from other rows $data[$name]['groups'][] = $row->ug_group; } if (isset($this->prop['rights']) && !is_null($row->ug_group)) { if (!isset($data[$name]['rights'])) { $data[$name]['rights'] = User::getGroupPermissions(User::getImplicitGroups()); } $data[$name]['rights'] = array_unique(array_merge($data[$name]['rights'], User::getGroupPermissions(array($row->ug_group)))); $result->setIndexedTagName($data[$name]['rights'], 'r'); } if ($row->ipb_deleted) { $data[$name]['hidden'] = ''; } if (isset($this->prop['blockinfo']) && !is_null($row->ipb_by_text)) { $data[$name]['blockedby'] = $row->ipb_by_text; $data[$name]['blockreason'] = $row->ipb_reason; $data[$name]['blockexpiry'] = $row->ipb_expiry; } if (isset($this->prop['emailable']) && $user->canReceiveEmail()) { $data[$name]['emailable'] = ''; } if (isset($this->prop['gender'])) { $gender = $user->getOption('gender'); if (strval($gender) === '') { $gender = 'unknown'; } $data[$name]['gender'] = $gender; } if (!is_null($params['token'])) { $tokenFunctions = $this->getTokenFunctions(); foreach ($params['token'] as $t) { $val = call_user_func($tokenFunctions[$t], $user); if ($val === false) { $this->setWarning("Action '{$t}' is not allowed for the current user"); } else { $data[$name][$t . 'token'] = $val; } } } } } // Second pass: add result data to $retval foreach ($goodNames as $u) { if (!isset($data[$u])) { $data[$u] = array('name' => $u); $urPage = new UserrightsPage(); $iwUser = $urPage->fetchUser($u); if ($iwUser instanceof UserRightsProxy) { $data[$u]['interwiki'] = ''; if (!is_null($params['token'])) { $tokenFunctions = $this->getTokenFunctions(); foreach ($params['token'] as $t) { $val = call_user_func($tokenFunctions[$t], $iwUser); if ($val === false) { $this->setWarning("Action '{$t}' is not allowed for the current user"); } else { $data[$u][$t . 'token'] = $val; } } } } else { $data[$u]['missing'] = ''; } } else { if (isset($this->prop['groups']) && isset($data[$u]['groups'])) { $autolist = ApiQueryUsers::getAutoGroups(User::newFromName($u)); $data[$u]['groups'] = array_merge($autolist, $data[$u]['groups']); $this->getResult()->setIndexedTagName($data[$u]['groups'], 'g'); } } $fit = $result->addValue(array('query', $this->getModuleName()), null, $data[$u]); if (!$fit) { $this->setContinueEnumParameter('users', implode('|', array_diff($users, $done))); break; } $done[] = $u; } return $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), 'user'); }
/** * @return int|null UNIX timestamp to delay running this job until, otherwise null * @since 1.22 */ public function getReleaseTimestamp() { return isset($this->params['jobReleaseTimestamp']) ? wfTimestampOrNull(TS_UNIX, $this->params['jobReleaseTimestamp']) : null; }
/** * Fetch pending file changes for this reviewed page version. * For each file, the "version used" (for stable parsing) is: * (a) (the latest rev) if FR_INCLUDES_CURRENT. Might be non-existing. * (b) newest( stable rev, rev at time of review ) if FR_INCLUDES_STABLE * (c) ( rev at time of review ) if FR_INCLUDES_FREEZE * Pending changes exist for a file iff the file is used in * the current rev of this page and one of the following holds: * (a) Current file is newer than the "version used" above (updated) * (b) Current file exists and the "version used" was non-existing (created) * (c) Current file doesn't exist and the "version used" existed (deleted) * * @param bool|string $noForeign Using 'noForeign' skips foreign file updates (bug 15748) * @return array of (title, MW file timestamp in reviewed version, has stable rev) tuples */ public function findPendingFileChanges($noForeign = false) { if (FlaggedRevs::inclusionSetting() == FR_INCLUDES_CURRENT) { return array(); // short-circuit } $dbr = wfGetDB(DB_SLAVE); # Only get templates with stable or "review time" versions. # Note: fi_img_timestamp is nullable (for deadlinks), so use fi_name if (FlaggedRevs::inclusionSetting() == FR_INCLUDES_STABLE) { $reviewed = "fi_name IS NOT NULL OR fr_img_timestamp IS NOT NULL"; } else { $reviewed = "fi_name IS NOT NULL"; } $ret = $dbr->select(array('imagelinks', 'flaggedimages', 'page', 'flaggedpages', 'flaggedrevs'), array('il_to', 'fi_img_timestamp', 'fr_img_timestamp'), array('il_from' => $this->getPage(), $reviewed), __METHOD__, array(), array('flaggedimages' => array('LEFT JOIN', array('fi_rev_id' => $this->getRevId(), 'fi_name = il_to')), 'page' => array('LEFT JOIN', 'page_namespace = ' . NS_FILE . ' AND page_title = il_to'), 'flaggedpages' => array('LEFT JOIN', 'fp_page_id = page_id'), 'flaggedrevs' => array('LEFT JOIN', 'fr_rev_id = fp_stable'))); $fileChanges = array(); foreach ($ret as $row) { // each file $reviewedTS = trim($row->fi_img_timestamp); // may have \0's $reviewedTS = $reviewedTS ? wfTimestamp(TS_MW, $reviewedTS) : null; $stableTS = wfTimestampOrNull(TS_MW, $row->fr_img_timestamp); # Get file timestamp used in this FlaggedRevision when parsed $usedTS = self::fileTimestampUsed($stableTS, $reviewedTS); # Check for edits/creations/deletions... $title = Title::makeTitleSafe(NS_FILE, $row->il_to); if (self::fileChanged($title, $usedTS, $noForeign)) { if (!$title->equals($this->getTitle())) { // bug 42297 $fileChanges[] = array($title, $usedTS, (bool) $stableTS); } } } return $fileChanges; }
/** * Initialize this object from a row from the user table. * * @param $row \type{\arrayof{\mixed}} Row from the user table to load. */ function loadFromRow($row) { $this->mDataLoaded = true; if (isset($row->user_id)) { $this->mId = $row->user_id; } $this->mName = $row->user_name; $this->mRealName = $row->user_real_name; $this->mPassword = $row->user_password; $this->mNewpassword = $row->user_newpassword; $this->mNewpassTime = wfTimestampOrNull(TS_MW, $row->user_newpass_time); $this->mEmail = $row->user_email; $this->decodeOptions($row->user_options); $this->mTouched = wfTimestamp(TS_MW, $row->user_touched); $this->mToken = $row->user_token; $this->mEmailAuthenticated = wfTimestampOrNull(TS_MW, $row->user_email_authenticated); $this->mEmailToken = $row->user_email_token; $this->mEmailTokenExpires = wfTimestampOrNull(TS_MW, $row->user_email_token_expires); $this->mRegistration = wfTimestampOrNull(TS_MW, $row->user_registration); $this->mEditCount = $row->user_editcount; }
public function execute() { $params = $this->extractRequestParams(); $result = $this->getResult(); $r = array(); if (!is_null($params['prop'])) { $this->prop = array_flip($params['prop']); } else { $this->prop = array(); } $users = (array) $params['users']; $goodNames = $done = array(); $result = $this->getResult(); // Canonicalize user names foreach ($users as $u) { $n = User::getCanonicalName($u); if ($n === false || $n === '') { $vals = array('name' => $u, 'invalid' => ''); $fit = $result->addValue(array('query', $this->getModuleName()), null, $vals); if (!$fit) { $this->setContinueEnumParameter('users', implode('|', array_diff($users, $done))); $goodNames = array(); break; } $done[] = $u; } else { $goodNames[] = $n; } } if (count($goodNames)) { $db = $this->getDb(); $this->addTables('user', 'u1'); $this->addFields('u1.*'); $this->addWhereFld('u1.user_name', $goodNames); if (isset($this->prop['groups'])) { $this->addTables('user_groups'); $this->addJoinConds(array('user_groups' => array('LEFT JOIN', 'ug_user=u1.user_id'))); $this->addFields('ug_group'); } if (isset($this->prop['blockinfo'])) { $this->addTables('ipblocks'); $this->addTables('user', 'u2'); $u2 = $this->getAliasedName('user', 'u2'); $this->addJoinConds(array('ipblocks' => array('LEFT JOIN', 'ipb_user=u1.user_id'), $u2 => array('LEFT JOIN', 'ipb_by=u2.user_id'))); $this->addFields(array('ipb_reason', 'u2.user_name AS blocker_name')); } $data = array(); $res = $this->select(__METHOD__); while ($r = $db->fetchObject($res)) { $user = User::newFromRow($r); $name = $user->getName(); $data[$name]['name'] = $name; if (isset($this->prop['editcount'])) { $data[$name]['editcount'] = intval($user->getEditCount()); } if (isset($this->prop['registration'])) { $data[$name]['registration'] = wfTimestampOrNull(TS_ISO_8601, $user->getRegistration()); } if (isset($this->prop['groups']) && !is_null($r->ug_group)) { // This row contains only one group, others will be added from other rows $data[$name]['groups'][] = $r->ug_group; } if (isset($this->prop['blockinfo']) && !is_null($r->blocker_name)) { $data[$name]['blockedby'] = $r->blocker_name; $data[$name]['blockreason'] = $r->ipb_reason; } if (isset($this->prop['emailable']) && $user->canReceiveEmail()) { $data[$name]['emailable'] = ''; } } } // Second pass: add result data to $retval foreach ($goodNames as $u) { if (!isset($data[$u])) { $data[$u] = array('name' => $u, 'missing' => ''); } else { if (isset($this->prop['groups']) && isset($data[$u]['groups'])) { $this->getResult()->setIndexedTagName($data[$u]['groups'], 'g'); } } $fit = $result->addValue(array('query', $this->getModuleName()), null, $data[$u]); if (!$fit) { $this->setContinueEnumParameter('users', implode('|', array_diff($users, $done))); break; } $done[] = $u; } return $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), 'user'); }
protected function getOtherUsersInfo($users) { $goodNames = $retval = array(); // Canonicalize user names foreach ($users as $u) { $n = User::getCanonicalName($u); if ($n === false || $n === '') { $retval[] = array('name' => $u, 'invalid' => ''); } else { $goodNames[] = $n; } } if (!count($goodNames)) { return $retval; } $db = $this->getDB(); $this->addTables('user', 'u1'); $this->addFields('u1.*'); $this->addWhereFld('u1.user_name', $goodNames); if (isset($this->prop['groups'])) { $this->addTables('user_groups'); $this->addJoinConds(array('user_groups' => array('LEFT JOIN', 'ug_user=u1.user_id'))); $this->addFields('ug_group'); } if (isset($this->prop['blockinfo'])) { $this->addTables('ipblocks'); $this->addTables('user', 'u2'); $u2 = $this->getAliasedName('user', 'u2'); $this->addJoinConds(array('ipblocks' => array('LEFT JOIN', 'ipb_user=u1.user_id'), $u2 => array('LEFT JOIN', 'ipb_by=u2.user_id'))); $this->addFields(array('ipb_reason', 'u2.user_name blocker_name')); } $data = array(); $res = $this->select(__METHOD__); while ($r = $db->fetchObject($res)) { $user = User::newFromRow($r); $name = $user->getName(); $data[$name]['name'] = $name; if (isset($this->prop['editcount'])) { // No proper member function in User class for this $data[$name]['editcount'] = $r->user_editcount; } if (isset($this->prop['registration'])) { // Nor for this one $data[$name]['registration'] = wfTimestampOrNull(TS_ISO_8601, $r->user_registration); } if (isset($this->prop['groups'])) { // This row contains only one group, others will be added from other rows if (!is_null($r->ug_group)) { $data[$name]['groups'][] = $r->ug_group; } } if (isset($this->prop['blockinfo'])) { if (!is_null($r->blocker_name)) { $data[$name]['blockedby'] = $r->blocker_name; $data[$name]['blockreason'] = $r->ipb_reason; } } if (isset($this->prop['emailable']) && $user->canReceiveEmail()) { $data[$name]['emailable'] = ''; } } // Second pass: add result data to $retval foreach ($goodNames as $u) { if (!isset($data[$u])) { $retval[] = array('name' => $u, 'missing' => ''); } else { if (isset($this->prop['groups']) && isset($data[$u]['groups'])) { $this->getResult()->setIndexedTagName($data[$u]['groups'], 'g'); } $retval[] = $data[$u]; } } return $retval; }
/** * Initialize this object from a row from the user table. * * @param stdClass $row Row from the user table to load. * @param array $data Further user data to load into the object * * user_groups Array with groups out of the user_groups table * user_properties Array with properties out of the user_properties table */ protected function loadFromRow($row, $data = null) { $all = true; $this->mGroups = null; // deferred if (isset($row->user_name)) { $this->mName = $row->user_name; $this->mFrom = 'name'; $this->setItemLoaded('name'); } else { $all = false; } if (isset($row->user_real_name)) { $this->mRealName = $row->user_real_name; $this->setItemLoaded('realname'); } else { $all = false; } if (isset($row->user_id)) { $this->mId = intval($row->user_id); $this->mFrom = 'id'; $this->setItemLoaded('id'); } else { $all = false; } if (isset($row->user_id) && isset($row->user_name)) { self::$idCacheByName[$row->user_name] = $row->user_id; } if (isset($row->user_editcount)) { $this->mEditCount = $row->user_editcount; } else { $all = false; } if (isset($row->user_email)) { $this->mEmail = $row->user_email; $this->mTouched = wfTimestamp(TS_MW, $row->user_touched); $this->mToken = $row->user_token; if ($this->mToken == '') { $this->mToken = null; } $this->mEmailAuthenticated = wfTimestampOrNull(TS_MW, $row->user_email_authenticated); $this->mEmailToken = $row->user_email_token; $this->mEmailTokenExpires = wfTimestampOrNull(TS_MW, $row->user_email_token_expires); $this->mRegistration = wfTimestampOrNull(TS_MW, $row->user_registration); } else { $all = false; } if ($all) { $this->mLoadedItems = true; } if (is_array($data)) { if (isset($data['user_groups']) && is_array($data['user_groups'])) { $this->mGroups = $data['user_groups']; } if (isset($data['user_properties']) && is_array($data['user_properties'])) { $this->loadOptions($data['user_properties']); } } }
/** * Initialize this object from a row from the user table. * * @param $row Array Row from the user table to load. */ public function loadFromRow($row) { $all = true; $this->mGroups = null; // deferred if (isset($row->user_name)) { $this->mName = $row->user_name; $this->mFrom = 'name'; $this->setItemLoaded('name'); } else { $all = false; } if (isset($row->user_real_name)) { $this->mRealName = $row->user_real_name; $this->setItemLoaded('realname'); } else { $all = false; } if (isset($row->user_id)) { $this->mId = intval($row->user_id); $this->mFrom = 'id'; $this->setItemLoaded('id'); } else { $all = false; } if (isset($row->user_editcount)) { $this->mEditCount = $row->user_editcount; } else { $all = false; } if (isset($row->user_password)) { $this->mPassword = $row->user_password; $this->mNewpassword = $row->user_newpassword; $this->mNewpassTime = wfTimestampOrNull(TS_MW, $row->user_newpass_time); $this->mEmail = $row->user_email; if (isset($row->user_options)) { $this->decodeOptions($row->user_options); } $this->mTouched = wfTimestamp(TS_MW, $row->user_touched); $this->mToken = $row->user_token; if ($this->mToken == '') { $this->mToken = null; } $this->mEmailAuthenticated = wfTimestampOrNull(TS_MW, $row->user_email_authenticated); $this->mEmailToken = $row->user_email_token; $this->mEmailTokenExpires = wfTimestampOrNull(TS_MW, $row->user_email_token_expires); $this->mRegistration = wfTimestampOrNull(TS_MW, $row->user_registration); } else { $all = false; } // Wikia. The following if/else statement has been added to reflect our user table layout. if (isset($row->user_birthdate) && $row->user_birthdate !== '0000-00-00') { $this->mBirthDate = $row->user_birthdate; } else { $all = false; } if ($all) { $this->mLoadedItems = true; } }
/** * @return int|null UNIX timestamp of when the job was queued, or null * @since 1.26 */ public function getQueuedTimestamp() { return isset($this->metadata['timestamp']) ? wfTimestampOrNull(TS_UNIX, $this->metadata['timestamp']) : null; }
/** * @param $vars AbuseFilterVariableHolder * @return AFPData|array|int|mixed|null|string * @throws MWException * @throws AFPException */ function compute($vars) { $parameters = $this->mParameters; $result = null; if (!wfRunHooks('AbuseFilter-interceptVariable', array($this->mMethod, $vars, $parameters, &$result))) { return $result instanceof AFPData ? $result : AFPData::newFromPHPVar($result); } switch ($this->mMethod) { case 'diff': $text1Var = $parameters['oldtext-var']; $text2Var = $parameters['newtext-var']; $text1 = $vars->getVar($text1Var)->toString() . "\n"; $text2 = $vars->getVar($text2Var)->toString() . "\n"; $result = wfDiff($text1, $text2); break; case 'diff-split': $diff = $vars->getVar($parameters['diff-var'])->toString(); $line_prefix = $parameters['line-prefix']; $diff_lines = explode("\n", $diff); $interest_lines = array(); foreach ($diff_lines as $line) { if (substr($line, 0, 1) === $line_prefix) { $interest_lines[] = substr($line, strlen($line_prefix)); } } $result = $interest_lines; break; case 'links-from-wikitext': // This should ONLY be used when sharing a parse operation with the edit. /* @var WikiPage $article */ $article = $parameters['article']; if ($article !== null && (!defined('MW_SUPPORTS_CONTENTHANDLER') || $article->getContentModel() === CONTENT_MODEL_WIKITEXT)) { $textVar = $parameters['text-var']; // XXX: Use prepareContentForEdit. But we need a Content object for that. $new_text = $vars->getVar($textVar)->toString(); $content = ContentHandler::makeContent($new_text, $article->getTitle()); $editInfo = $article->prepareContentForEdit($content); $links = array_keys($editInfo->output->getExternalLinks()); $result = $links; break; } // Otherwise fall back to database // Otherwise fall back to database case 'links-from-wikitext-nonedit': case 'links-from-wikitext-or-database': // TODO: use Content object instead, if available! In any case, use WikiPage, not Article. $article = self::articleFromTitle($parameters['namespace'], $parameters['title']); if ($vars->getVar('context')->toString() == 'filter') { $links = $this->getLinksFromDB($article); wfDebug("AbuseFilter: loading old links from DB\n"); } elseif (!defined('MW_SUPPORTS_CONTENTHANDLER') || $article->getContentModel() === CONTENT_MODEL_WIKITEXT) { wfDebug("AbuseFilter: loading old links from Parser\n"); $textVar = $parameters['text-var']; $wikitext = $vars->getVar($textVar)->toString(); $editInfo = $this->parseNonEditWikitext($wikitext, $article); $links = array_keys($editInfo->output->getExternalLinks()); } else { // TODO: Get links from Content object. But we don't have the content object. // And for non-text content, $wikitext is usually not going to be a valid // serialization, but rather some dummy text for filtering. $links = array(); } $result = $links; break; case 'link-diff-added': case 'link-diff-removed': $oldLinkVar = $parameters['oldlink-var']; $newLinkVar = $parameters['newlink-var']; $oldLinks = $vars->getVar($oldLinkVar)->toString(); $newLinks = $vars->getVar($newLinkVar)->toString(); $oldLinks = explode("\n", $oldLinks); $newLinks = explode("\n", $newLinks); if ($this->mMethod == 'link-diff-added') { $result = array_diff($newLinks, $oldLinks); } if ($this->mMethod == 'link-diff-removed') { $result = array_diff($oldLinks, $newLinks); } break; case 'parse-wikitext': // Should ONLY be used when sharing a parse operation with the edit. $article = $parameters['article']; if ($article !== null && (!defined('MW_SUPPORTS_CONTENTHANDLER') || $article->getContentModel() === CONTENT_MODEL_WIKITEXT)) { $textVar = $parameters['wikitext-var']; // XXX: Use prepareContentForEdit. But we need a Content object for that. $new_text = $vars->getVar($textVar)->toString(); $editInfo = $article->prepareTextForEdit($new_text); if (isset($parameters['pst']) && $parameters['pst']) { $result = $editInfo->pstContent->serialize($editInfo->format); } else { $newHTML = $editInfo->output->getText(); // Kill the PP limit comments. Ideally we'd just remove these by not setting the // parser option, but then we can't share a parse operation with the edit, which is bad. $result = preg_replace('/<!--\\s*NewPP limit report[^>]*-->\\s*$/si', '', $newHTML); } break; } // Otherwise fall back to database // Otherwise fall back to database case 'parse-wikitext-nonedit': // TODO: use Content object instead, if available! In any case, use WikiPage, not Article. $article = self::articleFromTitle($parameters['namespace'], $parameters['title']); $textVar = $parameters['wikitext-var']; if (!defined('MW_SUPPORTS_CONTENTHANDLER') || $article->getContentModel() === CONTENT_MODEL_WIKITEXT) { if (isset($parameters['pst']) && $parameters['pst']) { // $textVar is already PSTed when it's not loaded from an ongoing edit. $result = $vars->getVar($textVar)->toString(); } else { $text = $vars->getVar($textVar)->toString(); $editInfo = $this->parseNonEditWikitext($text, $article); $result = $editInfo->output->getText(); } } else { // TODO: Parser Output from Content object. But we don't have the content object. // And for non-text content, $wikitext is usually not going to be a valid // serialization, but rather some dummy text for filtering. $result = ''; } break; case 'strip-html': $htmlVar = $parameters['html-var']; $html = $vars->getVar($htmlVar)->toString(); $result = StringUtils::delimiterReplace('<', '>', '', $html); break; case 'load-recent-authors': $cutOff = $parameters['cutoff']; $title = Title::makeTitle($parameters['namespace'], $parameters['title']); if (!$title->exists()) { $result = ''; break; } $dbr = wfGetDB(DB_SLAVE); $res = $dbr->select('revision', 'DISTINCT rev_user_text', array('rev_page' => $title->getArticleID(), 'rev_timestamp<' . $dbr->addQuotes($dbr->timestamp($cutOff))), __METHOD__, array('ORDER BY' => 'rev_timestamp DESC', 'LIMIT' => 10)); $users = array(); foreach ($res as $row) { $users[] = $row->rev_user_text; } $result = $users; break; case 'get-page-restrictions': $action = $parameters['action']; $title = Title::makeTitle($parameters['namespace'], $parameters['title']); $rights = $title->getRestrictions($action); $rights = count($rights) ? $rights : array(); $result = $rights; break; case 'simple-user-accessor': $user = $parameters['user']; $method = $parameters['method']; if (!$user) { throw new MWException('No user parameter given.'); } $obj = self::getUserObject($user); if (!$obj) { throw new MWException("Invalid username {$user}"); } $result = call_user_func(array($obj, $method)); break; case 'user-age': $user = $parameters['user']; $asOf = $parameters['asof']; $obj = self::getUserObject($user); if ($obj->getId() == 0) { $result = 0; break; } $registration = $obj->getRegistration(); $result = wfTimestamp(TS_UNIX, $asOf) - wfTimestampOrNull(TS_UNIX, $registration); break; case 'user-groups': // Deprecated but needed by old log entries $user = $parameters['user']; $obj = self::getUserObject($user); $result = $obj->getEffectiveGroups(); break; case 'length': $s = $vars->getVar($parameters['length-var'])->toString(); $result = strlen($s); break; case 'subtract': $v1 = $vars->getVar($parameters['val1-var'])->toFloat(); $v2 = $vars->getVar($parameters['val2-var'])->toFloat(); $result = $v1 - $v2; break; case 'revision-text-by-id': $rev = Revision::newFromId($parameters['revid']); $result = AbuseFilter::revisionToString($rev); break; case 'revision-text-by-timestamp': $timestamp = $parameters['timestamp']; $title = Title::makeTitle($parameters['namespace'], $parameters['title']); $dbr = wfGetDB(DB_SLAVE); $rev = Revision::loadFromTimestamp($dbr, $title, $timestamp); $result = AbuseFilter::revisionToString($rev); break; default: if (wfRunHooks('AbuseFilter-computeVariable', array($this->mMethod, $vars, $parameters, &$result))) { throw new AFPException('Unknown variable compute type ' . $this->mMethod); } } return $result instanceof AFPData ? $result : AFPData::newFromPHPVar($result); }
/** * Get the oldest revision timestamp of this page * * @return String: MW timestamp */ public function getEarliestRevTime() { $dbr = wfGetDB(DB_SLAVE); if ($this->exists()) { $min = $dbr->selectField('revision', 'MIN(rev_timestamp)', array('rev_page' => $this->getArticleId()), __METHOD__); return wfTimestampOrNull(TS_MW, $min); } return null; }
/** * As recCheckCondition, but *not* recursive. The only valid conditions * are those whose first element is APCOND_EMAILCONFIRMED/APCOND_EDITCOUNT/ * APCOND_AGE. Other types will throw an exception if no extension evalu- * ates them. * * @param $cond Array: A condition, which must not contain other conditions * @param $user User The user to check the condition against * @return bool Whether the condition is true for the user */ private static function checkCondition($cond, User $user) { global $wgEmailAuthentication, $wgEnableEditCountLocal; if (count($cond) < 1) { return false; } switch ($cond[0]) { case APCOND_EMAILCONFIRMED: if (Sanitizer::validateEmail($user->getEmail())) { if ($wgEmailAuthentication) { return (bool) $user->getEmailAuthenticationTimestamp(); } else { return true; } } return false; case APCOND_EDITCOUNT: if (!empty($wgEnableEditCountLocal)) { return $user->getEditCountLocal() >= $cond[1]; } else { return $user->getEditCount() >= $cond[1]; } case APCOND_AGE: $age = time() - wfTimestampOrNull(TS_UNIX, $user->getRegistration()); return $age >= $cond[1]; case APCOND_AGE_FROM_EDIT: $age = time() - wfTimestampOrNull(TS_UNIX, $user->getFirstEditTimestamp()); return $age >= $cond[1]; case APCOND_INGROUPS: $groups = array_slice($cond, 1); return count(array_intersect($groups, $user->getGroups())) == count($groups); case APCOND_ISIP: return $cond[1] == $user->getRequest()->getIP(); case APCOND_IPINRANGE: return IP::isInRange($user->getRequest()->getIP(), $cond[1]); case APCOND_BLOCKED: return $user->isBlocked(); case APCOND_ISBOT: return in_array('bot', User::getGroupPermissions($user->getGroups())); default: $result = null; wfRunHooks('AutopromoteCondition', array($cond[0], array_slice($cond, 1), $user, &$result)); if ($result === null) { throw new MWException("Unrecognized condition {$cond[0]} for autopromotion!"); } return (bool) $result; } }
/** * @param $fields array * @return UserAccountRequest */ public static function newFromArray(array $fields) { $req = new self(); $req->id = isset($fields['id']) ? (int) $fields['id'] : null; // determined on insertOn() $req->name = $fields['name']; $req->realName = $fields['real_name']; $req->email = $fields['email']; $req->registration = wfTimestampOrNull(TS_MW, $fields['registration']); $req->bio = $fields['bio']; $req->notes = $fields['notes']; $req->urls = $fields['urls']; $req->type = (int) $fields['type']; $req->areas = is_string($fields['areas']) ? self::expandAreas($fields['areas']) : $fields['areas']; // already expanded $req->fileName = strlen($fields['filename']) ? $fields['filename'] : null; $req->fileStorageKey = $fields['storage_key']; $req->ip = $fields['ip']; $req->emailToken = $fields['email_token']; // MD5 of token $req->emailTokenExpires = wfTimestampOrNull(TS_MW, $fields['email_token_expires']); // These fields are typically left to default on insertion... $req->emailAuthTimestamp = isset($fields['email_authenticated']) ? wfTimestampOrNull(TS_MW, $fields['email_authenticated']) : null; $req->deleted = isset($fields['deleted']) ? $fields['deleted'] : false; $req->rejectedTimestamp = isset($fields['rejected']) ? wfTimestampOrNull(TS_MW, $fields['rejected']) : null; $req->heldTimestamp = isset($fields['held']) ? wfTimestampOrNull(TS_MW, $fields['held']) : null; $req->user = isset($fields['user']) ? (int) $fields['user'] : 0; $req->comment = isset($fields['comment']) ? $fields['comment'] : ''; return $req; }
/** * @static * @param $user User * @param $promote * @return bool */ public static function onGetAutoPromoteGroups( $user, &$promote ) { // Check against stricter requirements for tor nodes. // Counterintuitively, we do the requirement checks first. // This is so that we don't have to hit memcached to get the // exit list, unnecessarily. if (!count($promote)) { return true; // No groups to promote to anyway } $age = time() - wfTimestampOrNull( TS_UNIX, $user->getRegistration() ); global $wgTorAutoConfirmAge, $wgTorAutoConfirmCount; if ($age >= $wgTorAutoConfirmAge && $user->getEditCount() >= $wgTorAutoConfirmCount) { return true; // Does match requirements. Don't bother checking if we're an exit node. } if (self::isExitNode()) { // Tor user, doesn't match the expanded requirements. $promote = array(); } return true; }
/** * Load a user from the database */ function loadFromDatabase() { $fname = "User::loadFromDatabase"; # Counter-intuitive, breaks various things, use User::setLoaded() if you want to suppress # loading in a command line script, don't assume all command line scripts need it like this #if ( $this->mDataLoaded || $wgCommandLineMode ) { if ($this->mDataLoaded) { return; } # Paranoia $this->mId = intval($this->mId); /** Anonymous user */ if (!$this->mId) { /** Get rights */ $this->mRights = $this->getGroupPermissions(array('*')); $this->mDataLoaded = true; return; } # the following stuff is for non-anonymous users only $dbr =& wfGetDB(DB_SLAVE); $s = $dbr->selectRow('user', array('user_name', 'user_password', 'user_newpassword', 'user_email', 'user_email_authenticated', 'user_real_name', 'user_options', 'user_touched', 'user_token', 'user_registration'), array('user_id' => $this->mId), $fname); if ($s !== false) { $this->mName = $s->user_name; $this->mEmail = $s->user_email; $this->mEmailAuthenticated = wfTimestampOrNull(TS_MW, $s->user_email_authenticated); $this->mRealName = $s->user_real_name; $this->mPassword = $s->user_password; $this->mNewpassword = $s->user_newpassword; $this->decodeOptions($s->user_options); $this->mTouched = wfTimestamp(TS_MW, $s->user_touched); $this->mToken = $s->user_token; $this->mRegistration = wfTimestampOrNull(TS_MW, $s->user_registration); $res = $dbr->select('user_groups', array('ug_group'), array('ug_user' => $this->mId), $fname); $this->mGroups = array(); while ($row = $dbr->fetchObject($res)) { $this->mGroups[] = $row->ug_group; } $implicitGroups = array('*', 'user'); global $wgAutoConfirmAge; $accountAge = time() - wfTimestampOrNull(TS_UNIX, $this->mRegistration); if ($accountAge >= $wgAutoConfirmAge) { $implicitGroups[] = 'autoconfirmed'; } # Implicit group for users whose email addresses are confirmed global $wgEmailAuthentication; if ($this->isValidEmailAddr($this->mEmail)) { if ($wgEmailAuthentication) { if ($this->mEmailAuthenticated) { $implicitGroups[] = 'emailconfirmed'; } } else { $implicitGroups[] = 'emailconfirmed'; } } $effectiveGroups = array_merge($implicitGroups, $this->mGroups); $this->mRights = $this->getGroupPermissions($effectiveGroups); } $this->mDataLoaded = true; }
/** * Initialize this object from a row from the user table. * * @param $row Array Row from the user table to load. */ public function loadFromRow($row) { $all = true; $this->mGroups = null; // deferred if (isset($row->user_name)) { $this->mName = $row->user_name; $this->mFrom = 'name'; $this->setItemLoaded('name'); } else { $all = false; } if (isset($row->user_real_name)) { $this->mRealName = $row->user_real_name; $this->setItemLoaded('realname'); } else { $all = false; } if (isset($row->user_id)) { $this->mId = intval($row->user_id); $this->mFrom = 'id'; $this->setItemLoaded('id'); } else { $all = false; } if (isset($row->user_editcount)) { $this->mEditCount = $row->user_editcount; } else { $all = false; } if (isset($row->user_password)) { $this->mPassword = $row->user_password; $this->mNewpassword = $row->user_newpassword; $this->mNewpassTime = wfTimestampOrNull(TS_MW, $row->user_newpass_time); $this->mEmail = $row->user_email; if (isset($row->user_options)) { $this->decodeOptions($row->user_options); } $this->mTouched = wfTimestamp(TS_MW, $row->user_touched); $this->mToken = $row->user_token; if ($this->mToken == '') { $this->mToken = null; } $this->mEmailAuthenticated = wfTimestampOrNull(TS_MW, $row->user_email_authenticated); $this->mEmailToken = $row->user_email_token; $this->mEmailTokenExpires = wfTimestampOrNull(TS_MW, $row->user_email_token_expires); $this->mRegistration = wfTimestampOrNull(TS_MW, $row->user_registration); } else { $all = false; } if ($all) { $this->mLoadedItems = true; } }
function compute($vars) { $parameters = $this->mParameters; $result = null; switch ($this->mMethod) { case 'diff': $text1Var = $parameters['oldtext-var']; $text2Var = $parameters['newtext-var']; $text1 = $vars->getVar($text1Var)->toString(); $text2 = $vars->getVar($text2Var)->toString(); $result = wfDiff($text1, $text2); $result = trim(preg_replace("/^\\\\ No newline at end of file\n/m", '', $result)); break; case 'diff-split': $diff = $vars->getVar($parameters['diff-var'])->toString(); $line_prefix = $parameters['line-prefix']; $diff_lines = explode("\n", $diff); $interest_lines = array(); foreach ($diff_lines as $line) { if (substr($line, 0, 1) === $line_prefix) { $interest_lines[] = substr($line, strlen($line_prefix)); } } $result = $interest_lines; break; case 'links-from-wikitext': // This should ONLY be used when sharing a parse operation with the edit. $article = $parameters['article']; if ($article) { $textVar = $parameters['text-var']; $new_text = $vars->getVar($textVar)->toString(); $editInfo = $article->prepareTextForEdit($new_text); $links = array_keys($editInfo->output->getExternalLinks()); $result = $links; break; } // Otherwise fall back to database // Otherwise fall back to database case 'links-from-wikitext-nonedit': case 'links-from-wikitext-or-database': $article = self::articleFromTitle($parameters['namespace'], $parameters['title']); if ($vars->getVar('context')->toString() == 'filter') { $links = $this->getLinksFromDB($article); wfDebug("AbuseFilter: loading old links from DB\n"); } else { wfDebug("AbuseFilter: loading old links from Parser\n"); $textVar = $parameters['text-var']; $wikitext = $vars->getVar($textVar)->toString(); $editInfo = $this->parseNonEditWikitext($wikitext, $article); $links = array_keys($editInfo->output->getExternalLinks()); } $result = $links; break; case 'link-diff-added': case 'link-diff-removed': $oldLinkVar = $parameters['oldlink-var']; $newLinkVar = $parameters['newlink-var']; $oldLinks = $vars->getVar($oldLinkVar)->toString(); $newLinks = $vars->getVar($newLinkVar)->toString(); $oldLinks = explode("\n", $oldLinks); $newLinks = explode("\n", $newLinks); if ($this->mMethod == 'link-diff-added') { $result = array_diff($newLinks, $oldLinks); } if ($this->mMethod == 'link-diff-removed') { $result = array_diff($oldLinks, $newLinks); } break; case 'parse-wikitext': // Should ONLY be used when sharing a parse operation with the edit. $article = $parameters['article']; if ($article) { $textVar = $parameters['wikitext-var']; $new_text = $vars->getVar($textVar)->toString(); $editInfo = $article->prepareTextForEdit($new_text); $newHTML = $editInfo->output->getText(); // Kill the PP limit comments. Ideally we'd just remove these by not setting the // parser option, but then we can't share a parse operation with the edit, which is bad. $result = preg_replace('/<!--\\s*NewPP limit report[^>]*-->\\s*$/si', '', $newHTML); break; } // Otherwise fall back to database // Otherwise fall back to database case 'parse-wikitext-nonedit': $article = self::articleFromTitle($parameters['namespace'], $parameters['title']); $textVar = $parameters['wikitext-var']; $text = $vars->getVar($textVar)->toString(); $editInfo = $this->parseNonEditWikitext($text, $article); $result = $editInfo->output->getText(); break; case 'strip-html': $htmlVar = $parameters['html-var']; $html = $vars->getVar($htmlVar)->toString(); $result = StringUtils::delimiterReplace('<', '>', '', $html); break; case 'load-recent-authors': $cutOff = $parameters['cutoff']; $title = Title::makeTitle($parameters['namespace'], $parameters['title']); if (!$title->exists()) { $result = ''; break; } $dbr = wfGetDB(DB_SLAVE); $res = $dbr->select('revision', 'DISTINCT rev_user_text', array('rev_page' => $title->getArticleId(), 'rev_timestamp<' . $dbr->addQuotes($dbr->timestamp($cutOff))), __METHOD__, array('ORDER BY' => 'rev_timestamp DESC', 'LIMIT' => 10)); $users = array(); foreach ($res as $row) { $users[] = $row->rev_user_text; } $result = $users; break; case 'get-page-restrictions': $action = $parameters['action']; $title = Title::makeTitle($parameters['namespace'], $parameters['title']); $rights = $title->getRestrictions($action); $rights = count($rights) ? $rights : array(); $result = $rights; break; case 'simple-user-accessor': $user = $parameters['user']; $method = $parameters['method']; if (!$user) { throw new MWException('No user parameter given.'); } $obj = self::userObjectFromName($user); if (!$obj) { throw new MWException("Invalid username {$user}"); } $result = call_user_func(array($obj, $method)); break; case 'user-age': $user = $parameters['user']; $asOf = $parameters['asof']; $obj = self::userObjectFromName($user); if ($obj->getId() == 0) { $result = 0; break; } $registration = $obj->getRegistration(); $result = wfTimestamp(TS_UNIX, $asOf) - wfTimestampOrNull(TS_UNIX, $registration); break; case 'user-groups': $user = $parameters['user']; $obj = self::userObjectFromName($user); $result = $obj->getEffectiveGroups(); break; case 'length': $s = $vars->getVar($parameters['length-var'])->toString(); $result = strlen($s); break; case 'subtract': $v1 = $vars->getVar($parameters['val1-var'])->toFloat(); $v2 = $vars->getVar($parameters['val2-var'])->toFloat(); $result = $v1 - $v2; break; case 'revision-text-by-id': $rev = Revision::newFromId($parameters['revid']); $result = $rev->getText(); break; case 'revision-text-by-timestamp': $timestamp = $parameters['timestamp']; $title = Title::makeTitle($parameters['namespace'], $parameters['title']); $dbr = wfGetDB(DB_SLAVE); $rev = Revision::loadFromTimestamp($dbr, $title, $timestamp); if ($rev) { $result = $rev->getText(); } else { $result = ''; } break; default: if (wfRunHooks('AbuseFilter-computeVariable', array($this->mMethod, $vars))) { throw new AFPException('Unknown variable compute type ' . $this->mMethod); } } return $result instanceof AFPData ? $result : AFPData::newFromPHPVar($result); }
/** * Get an array containing the variables to be set in mw.config in JavaScript. * * Do not add things here which can be evaluated in ResourceLoaderStartUpModule * - in other words, page-independent/site-wide variables (without state). * You will only be adding bloat to the html page and causing page caches to * have to be purged on configuration changes. * @return array */ public function getJSVars() { global $wgContLang; $curRevisionId = 0; $articleId = 0; $canonicalSpecialPageName = false; # bug 21115 $title = $this->getTitle(); $ns = $title->getNamespace(); $canonicalNamespace = MWNamespace::exists($ns) ? MWNamespace::getCanonicalName($ns) : $title->getNsText(); $sk = $this->getSkin(); // Get the relevant title so that AJAX features can use the correct page name // when making API requests from certain special pages (bug 34972). $relevantTitle = $sk->getRelevantTitle(); $relevantUser = $sk->getRelevantUser(); if ($ns == NS_SPECIAL) { list($canonicalSpecialPageName, ) = SpecialPageFactory::resolveAlias($title->getDBkey()); } elseif ($this->canUseWikiPage()) { $wikiPage = $this->getWikiPage(); $curRevisionId = $wikiPage->getLatest(); $articleId = $wikiPage->getId(); } $lang = $title->getPageLanguage(); // Pre-process information $separatorTransTable = $lang->separatorTransformTable(); $separatorTransTable = $separatorTransTable ? $separatorTransTable : array(); $compactSeparatorTransTable = array(implode("\t", array_keys($separatorTransTable)), implode("\t", $separatorTransTable)); $digitTransTable = $lang->digitTransformTable(); $digitTransTable = $digitTransTable ? $digitTransTable : array(); $compactDigitTransTable = array(implode("\t", array_keys($digitTransTable)), implode("\t", $digitTransTable)); $user = $this->getUser(); $vars = array('wgCanonicalNamespace' => $canonicalNamespace, 'wgCanonicalSpecialPageName' => $canonicalSpecialPageName, 'wgNamespaceNumber' => $title->getNamespace(), 'wgPageName' => $title->getPrefixedDBkey(), 'wgTitle' => $title->getText(), 'wgCurRevisionId' => $curRevisionId, 'wgRevisionId' => (int) $this->getRevisionId(), 'wgArticleId' => $articleId, 'wgIsArticle' => $this->isArticle(), 'wgIsRedirect' => $title->isRedirect(), 'wgAction' => Action::getActionName($this->getContext()), 'wgUserName' => $user->isAnon() ? null : $user->getName(), 'wgUserGroups' => $user->getEffectiveGroups(), 'wgCategories' => $this->getCategories(), 'wgBreakFrames' => $this->getFrameOptions() == 'DENY', 'wgPageContentLanguage' => $lang->getCode(), 'wgPageContentModel' => $title->getContentModel(), 'wgSeparatorTransformTable' => $compactSeparatorTransTable, 'wgDigitTransformTable' => $compactDigitTransTable, 'wgDefaultDateFormat' => $lang->getDefaultDateFormat(), 'wgMonthNames' => $lang->getMonthNamesArray(), 'wgMonthNamesShort' => $lang->getMonthAbbreviationsArray(), 'wgRelevantPageName' => $relevantTitle->getPrefixedDBkey(), 'wgRelevantArticleId' => $relevantTitle->getArticleId()); if ($user->isLoggedIn()) { $vars['wgUserId'] = $user->getId(); $vars['wgUserEditCount'] = $user->getEditCount(); $userReg = wfTimestampOrNull(TS_UNIX, $user->getRegistration()); $vars['wgUserRegistration'] = $userReg !== null ? $userReg * 1000 : null; // Get the revision ID of the oldest new message on the user's talk // page. This can be used for constructing new message alerts on // the client side. $vars['wgUserNewMsgRevisionId'] = $user->getNewMessageRevisionId(); } if ($wgContLang->hasVariants()) { $vars['wgUserVariant'] = $wgContLang->getPreferredVariant(); } // Same test as SkinTemplate $vars['wgIsProbablyEditable'] = $title->quickUserCan('edit', $user) && ($title->exists() || $title->quickUserCan('create', $user)); foreach ($title->getRestrictionTypes() as $type) { $vars['wgRestriction' . ucfirst($type)] = $title->getRestrictions($type); } if ($title->isMainPage()) { $vars['wgIsMainPage'] = true; } if ($this->mRedirectedFrom) { $vars['wgRedirectedFrom'] = $this->mRedirectedFrom->getPrefixedDBkey(); } if ($relevantUser) { $vars['wgRelevantUserName'] = $relevantUser->getName(); } // Allow extensions to add their custom variables to the mw.config map. // Use the 'ResourceLoaderGetConfigVars' hook if the variable is not // page-dependant but site-wide (without state). // Alternatively, you may want to use OutputPage->addJsConfigVars() instead. Hooks::run('MakeGlobalVariablesScript', array(&$vars, $this)); // Merge in variables from addJsConfigVars last return array_merge($vars, $this->getJsConfigVars()); }
/** * Get the list of implicit group memberships this user has. * This includes all explicit groups, plus 'user' if logged in * and '*' for all accounts. * @param boolean $recache Don't use the cache * @return array of strings */ function getEffectiveGroups($recache = false) { if ($recache || is_null($this->mEffectiveGroups)) { $this->load(); $this->mEffectiveGroups = $this->mGroups; $this->mEffectiveGroups[] = '*'; if ($this->mId) { $this->mEffectiveGroups[] = 'user'; global $wgAutoConfirmAge, $wgAutoConfirmCount; $accountAge = time() - wfTimestampOrNull(TS_UNIX, $this->mRegistration); if ($accountAge >= $wgAutoConfirmAge && $this->getEditCount() >= $wgAutoConfirmCount) { $this->mEffectiveGroups[] = 'autoconfirmed'; } # Implicit group for users whose email addresses are confirmed global $wgEmailAuthentication; if (self::isValidEmailAddr($this->mEmail)) { if ($wgEmailAuthentication) { if ($this->mEmailAuthenticated) { $this->mEffectiveGroups[] = 'emailconfirmed'; } } else { $this->mEffectiveGroups[] = 'emailconfirmed'; } } } } return $this->mEffectiveGroups; }