UsersTable.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. <?php
  2. namespace Elgg\Database;
  3. /// Map a username to a cached GUID
  4. /**
  5. * @var int[] $USERNAME_TO_GUID_MAP_CACHE
  6. * @access private
  7. */
  8. global $USERNAME_TO_GUID_MAP_CACHE;
  9. $USERNAME_TO_GUID_MAP_CACHE = array();
  10. /**
  11. * WARNING: API IN FLUX. DO NOT USE DIRECTLY.
  12. *
  13. * @access private
  14. *
  15. * @package Elgg.Core
  16. * @subpackage Database
  17. * @since 1.10.0
  18. */
  19. class UsersTable {
  20. /**
  21. * Global Elgg configuration
  22. *
  23. * @var \stdClass
  24. */
  25. private $CONFIG;
  26. /**
  27. * Constructor
  28. */
  29. public function __construct() {
  30. global $CONFIG;
  31. $this->CONFIG = $CONFIG;
  32. }
  33. /**
  34. * Return the user specific details of a user by a row.
  35. *
  36. * @param int $guid The \ElggUser guid
  37. *
  38. * @return mixed
  39. * @access private
  40. */
  41. function getRow($guid) {
  42. $guid = (int)$guid;
  43. return _elgg_services()->db->getDataRow("SELECT * from {$this->CONFIG->dbprefix}users_entity where guid=$guid");
  44. }
  45. /**
  46. * Disables all of a user's entities
  47. *
  48. * @param int $owner_guid The owner GUID
  49. *
  50. * @return bool Depending on success
  51. */
  52. function disableEntities($owner_guid) {
  53. $owner_guid = (int) $owner_guid;
  54. if ($entity = get_entity($owner_guid)) {
  55. if (_elgg_services()->events->trigger('disable', $entity->type, $entity)) {
  56. if ($entity->canEdit()) {
  57. $query = "UPDATE {$this->CONFIG->dbprefix}entities
  58. set enabled='no' where owner_guid={$owner_guid}
  59. or container_guid = {$owner_guid}";
  60. $res = _elgg_services()->db->updateData($query);
  61. return $res;
  62. }
  63. }
  64. }
  65. return false;
  66. }
  67. /**
  68. * Ban a user
  69. *
  70. * @param int $user_guid The user guid
  71. * @param string $reason A reason
  72. *
  73. * @return bool
  74. */
  75. function ban($user_guid, $reason = "") {
  76. $user_guid = (int)$user_guid;
  77. $user = get_entity($user_guid);
  78. if (($user) && ($user->canEdit()) && ($user instanceof \ElggUser)) {
  79. if (_elgg_services()->events->trigger('ban', 'user', $user)) {
  80. // Add reason
  81. if ($reason) {
  82. create_metadata($user_guid, 'ban_reason', $reason, '', 0, ACCESS_PUBLIC);
  83. }
  84. // invalidate memcache for this user
  85. static $newentity_cache;
  86. if ((!$newentity_cache) && (is_memcache_available())) {
  87. $newentity_cache = new \ElggMemcache('new_entity_cache');
  88. }
  89. if ($newentity_cache) {
  90. $newentity_cache->delete($user_guid);
  91. }
  92. // Set ban flag
  93. $query = "UPDATE {$this->CONFIG->dbprefix}users_entity set banned='yes' where guid=$user_guid";
  94. return _elgg_services()->db->updateData($query);
  95. }
  96. return false;
  97. }
  98. return false;
  99. }
  100. /**
  101. * Unban a user.
  102. *
  103. * @param int $user_guid Unban a user.
  104. *
  105. * @return bool
  106. */
  107. function unban($user_guid) {
  108. $user_guid = (int)$user_guid;
  109. $user = get_entity($user_guid);
  110. if (($user) && ($user->canEdit()) && ($user instanceof \ElggUser)) {
  111. if (_elgg_services()->events->trigger('unban', 'user', $user)) {
  112. create_metadata($user_guid, 'ban_reason', '', '', 0, ACCESS_PUBLIC);
  113. // invalidate memcache for this user
  114. static $newentity_cache;
  115. if ((!$newentity_cache) && (is_memcache_available())) {
  116. $newentity_cache = new \ElggMemcache('new_entity_cache');
  117. }
  118. if ($newentity_cache) {
  119. $newentity_cache->delete($user_guid);
  120. }
  121. $query = "UPDATE {$this->CONFIG->dbprefix}users_entity set banned='no' where guid=$user_guid";
  122. return _elgg_services()->db->updateData($query);
  123. }
  124. return false;
  125. }
  126. return false;
  127. }
  128. /**
  129. * Makes user $guid an admin.
  130. *
  131. * @param int $user_guid User guid
  132. *
  133. * @return bool
  134. */
  135. function makeAdmin($user_guid) {
  136. $user = get_entity((int)$user_guid);
  137. if (($user) && ($user instanceof \ElggUser) && ($user->canEdit())) {
  138. if (_elgg_services()->events->trigger('make_admin', 'user', $user)) {
  139. // invalidate memcache for this user
  140. static $newentity_cache;
  141. if ((!$newentity_cache) && (is_memcache_available())) {
  142. $newentity_cache = new \ElggMemcache('new_entity_cache');
  143. }
  144. if ($newentity_cache) {
  145. $newentity_cache->delete($user_guid);
  146. }
  147. $r = _elgg_services()->db->updateData("UPDATE {$this->CONFIG->dbprefix}users_entity set admin='yes' where guid=$user_guid");
  148. _elgg_invalidate_cache_for_entity($user_guid);
  149. return $r;
  150. }
  151. return false;
  152. }
  153. return false;
  154. }
  155. /**
  156. * Removes user $guid's admin flag.
  157. *
  158. * @param int $user_guid User GUID
  159. *
  160. * @return bool
  161. */
  162. function removeAdmin($user_guid) {
  163. $user = get_entity((int)$user_guid);
  164. if (($user) && ($user instanceof \ElggUser) && ($user->canEdit())) {
  165. if (_elgg_services()->events->trigger('remove_admin', 'user', $user)) {
  166. // invalidate memcache for this user
  167. static $newentity_cache;
  168. if ((!$newentity_cache) && (is_memcache_available())) {
  169. $newentity_cache = new \ElggMemcache('new_entity_cache');
  170. }
  171. if ($newentity_cache) {
  172. $newentity_cache->delete($user_guid);
  173. }
  174. $r = _elgg_services()->db->updateData("UPDATE {$this->CONFIG->dbprefix}users_entity set admin='no' where guid=$user_guid");
  175. _elgg_invalidate_cache_for_entity($user_guid);
  176. return $r;
  177. }
  178. return false;
  179. }
  180. return false;
  181. }
  182. /**
  183. * Get user by username
  184. *
  185. * @param string $username The user's username
  186. *
  187. * @return \ElggUser|false Depending on success
  188. */
  189. function getByUsername($username) {
  190. global $USERNAME_TO_GUID_MAP_CACHE;
  191. // Fixes #6052. Username is frequently sniffed from the path info, which,
  192. // unlike $_GET, is not URL decoded. If the username was not URL encoded,
  193. // this is harmless.
  194. $username = rawurldecode($username);
  195. $username = sanitise_string($username);
  196. $access = _elgg_get_access_where_sql();
  197. // Caching
  198. if ((isset($USERNAME_TO_GUID_MAP_CACHE[$username]))
  199. && (_elgg_retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]))) {
  200. return _elgg_retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]);
  201. }
  202. $query = "SELECT e.* FROM {$this->CONFIG->dbprefix}users_entity u
  203. JOIN {$this->CONFIG->dbprefix}entities e ON e.guid = u.guid
  204. WHERE u.username = '$username' AND $access";
  205. $entity = _elgg_services()->db->getDataRow($query, 'entity_row_to_elggstar');
  206. if ($entity) {
  207. $USERNAME_TO_GUID_MAP_CACHE[$username] = $entity->guid;
  208. } else {
  209. $entity = false;
  210. }
  211. return $entity;
  212. }
  213. /**
  214. * Get an array of users from an email address
  215. *
  216. * @param string $email Email address.
  217. *
  218. * @return array
  219. */
  220. function getByEmail($email) {
  221. $email = sanitise_string($email);
  222. $access = _elgg_get_access_where_sql();
  223. $query = "SELECT e.* FROM {$this->CONFIG->dbprefix}entities e
  224. JOIN {$this->CONFIG->dbprefix}users_entity u ON e.guid = u.guid
  225. WHERE email = '$email' AND $access";
  226. return _elgg_services()->db->getData($query, 'entity_row_to_elggstar');
  227. }
  228. /**
  229. * Return users (or the number of them) who have been active within a recent period.
  230. *
  231. * @param array $options Array of options with keys:
  232. *
  233. * seconds (int) => Length of period (default 600 = 10min)
  234. * limit (int) => Limit (default 10)
  235. * offset (int) => Offset (default 0)
  236. * count (bool) => Return a count instead of users? (default false)
  237. *
  238. * Formerly this was the seconds parameter.
  239. *
  240. * @param int $limit Limit (deprecated usage, use $options)
  241. * @param int $offset Offset (deprecated usage, use $options)
  242. * @param bool $count Count (deprecated usage, use $options)
  243. *
  244. * @return \ElggUser[]|int
  245. */
  246. function findActive($options = array(), $limit = 10, $offset = 0, $count = false) {
  247. $seconds = 600; //default value
  248. if (!is_array($options)) {
  249. elgg_deprecated_notice("find_active_users() now accepts an \$options array", 1.9);
  250. if (!$options) {
  251. $options = $seconds; //assign default value
  252. }
  253. $options = array('seconds' => $options);
  254. }
  255. if ($limit === null) {
  256. $limit = _elgg_services()->config->get('default_limit');
  257. }
  258. $options = array_merge(array(
  259. 'seconds' => $seconds,
  260. 'limit' => $limit,
  261. 'offset' => $offset,
  262. 'count' => $count,
  263. ), $options);
  264. // cast options we're sending to hook
  265. foreach (array('seconds', 'limit', 'offset') as $key) {
  266. $options[$key] = (int)$options[$key];
  267. }
  268. $options['count'] = (bool)$options['count'];
  269. // allow plugins to override
  270. $params = array(
  271. 'seconds' => $options['seconds'],
  272. 'limit' => $options['limit'],
  273. 'offset' => $options['offset'],
  274. 'count' => $options['count'],
  275. 'options' => $options,
  276. );
  277. $data = _elgg_services()->hooks->trigger('find_active_users', 'system', $params, null);
  278. // check null because the handler could legitimately return falsey values.
  279. if ($data !== null) {
  280. return $data;
  281. }
  282. $dbprefix = _elgg_services()->config->get('dbprefix');
  283. $time = time() - $options['seconds'];
  284. return elgg_get_entities(array(
  285. 'type' => 'user',
  286. 'limit' => $options['limit'],
  287. 'offset' => $options['offset'],
  288. 'count' => $options['count'],
  289. 'joins' => array("join {$dbprefix}users_entity u on e.guid = u.guid"),
  290. 'wheres' => array("u.last_action >= {$time}"),
  291. 'order_by' => "u.last_action desc",
  292. ));
  293. }
  294. /**
  295. * Registers a user, returning false if the username already exists
  296. *
  297. * @param string $username The username of the new user
  298. * @param string $password The password
  299. * @param string $name The user's display name
  300. * @param string $email The user's email address
  301. * @param bool $allow_multiple_emails Allow the same email address to be
  302. * registered multiple times?
  303. *
  304. * @return int|false The new user's GUID; false on failure
  305. * @throws \RegistrationException
  306. */
  307. function register($username, $password, $name, $email, $allow_multiple_emails = false) {
  308. // no need to trim password.
  309. $username = trim($username);
  310. $name = trim(strip_tags($name));
  311. $email = trim($email);
  312. // A little sanity checking
  313. if (empty($username)
  314. || empty($password)
  315. || empty($name)
  316. || empty($email)) {
  317. return false;
  318. }
  319. // Make sure a user with conflicting details hasn't registered and been disabled
  320. $access_status = access_get_show_hidden_status();
  321. access_show_hidden_entities(true);
  322. if (!validate_email_address($email)) {
  323. throw new \RegistrationException(_elgg_services()->translator->translate('registration:emailnotvalid'));
  324. }
  325. if (!validate_password($password)) {
  326. throw new \RegistrationException(_elgg_services()->translator->translate('registration:passwordnotvalid'));
  327. }
  328. if (!validate_username($username)) {
  329. throw new \RegistrationException(_elgg_services()->translator->translate('registration:usernamenotvalid'));
  330. }
  331. if ($user = get_user_by_username($username)) {
  332. throw new \RegistrationException(_elgg_services()->translator->translate('registration:userexists'));
  333. }
  334. if ((!$allow_multiple_emails) && (get_user_by_email($email))) {
  335. throw new \RegistrationException(_elgg_services()->translator->translate('registration:dupeemail'));
  336. }
  337. access_show_hidden_entities($access_status);
  338. // Create user
  339. $user = new \ElggUser();
  340. $user->username = $username;
  341. $user->email = $email;
  342. $user->name = $name;
  343. $user->access_id = ACCESS_PUBLIC;
  344. $user->setPassword($password);
  345. $user->owner_guid = 0; // Users aren't owned by anyone, even if they are admin created.
  346. $user->container_guid = 0; // Users aren't contained by anyone, even if they are admin created.
  347. $user->language = _elgg_services()->translator->getCurrentLanguage();
  348. if ($user->save() === false) {
  349. return false;
  350. }
  351. // Turn on email notifications by default
  352. set_user_notification_setting($user->getGUID(), 'email', true);
  353. return $user->getGUID();
  354. }
  355. /**
  356. * Generates a unique invite code for a user
  357. *
  358. * @param string $username The username of the user sending the invitation
  359. *
  360. * @return string Invite code
  361. * @see validateInviteCode
  362. */
  363. function generateInviteCode($username) {
  364. $time = time();
  365. return "$time." . _elgg_services()->crypto->getHmac([(int)$time, $username])->getToken();
  366. }
  367. /**
  368. * Validate a user's invite code
  369. *
  370. * @param string $username The username
  371. * @param string $code The invite code
  372. *
  373. * @return bool
  374. * @see generateInviteCode
  375. */
  376. function validateInviteCode($username, $code) {
  377. // validate the format of the token created by ->generateInviteCode()
  378. if (!preg_match('~^(\d+)\.([a-zA-Z0-9\-_]+)$~', $code, $m)) {
  379. return false;
  380. }
  381. $time = $m[1];
  382. $mac = $m[2];
  383. return _elgg_services()->crypto->getHmac([(int)$time, $username])->matchesToken($mac);
  384. }
  385. /**
  386. * Set the validation status for a user.
  387. *
  388. * @param int $user_guid The user's GUID
  389. * @param bool $status Validated (true) or unvalidated (false)
  390. * @param string $method Optional method to say how a user was validated
  391. * @return bool
  392. */
  393. function setValidationStatus($user_guid, $status, $method = '') {
  394. $result1 = create_metadata($user_guid, 'validated', $status, '', 0, ACCESS_PUBLIC, false);
  395. $result2 = create_metadata($user_guid, 'validated_method', $method, '', 0, ACCESS_PUBLIC, false);
  396. if ($result1 && $result2) {
  397. return true;
  398. } else {
  399. return false;
  400. }
  401. }
  402. /**
  403. * Gets the validation status of a user.
  404. *
  405. * @param int $user_guid The user's GUID
  406. * @return bool|null Null means status was not set for this user.
  407. */
  408. function getValidationStatus($user_guid) {
  409. $md = elgg_get_metadata(array(
  410. 'guid' => $user_guid,
  411. 'metadata_name' => 'validated'
  412. ));
  413. if ($md == false) {
  414. return null;
  415. }
  416. if ($md[0]->value) {
  417. return true;
  418. }
  419. return false;
  420. }
  421. /**
  422. * Sets the last action time of the given user to right now.
  423. *
  424. * @param int $user_guid The user GUID
  425. *
  426. * @return void
  427. */
  428. function setLastAction($user_guid) {
  429. $user_guid = (int) $user_guid;
  430. $time = time();
  431. $query = "UPDATE {$this->CONFIG->dbprefix}users_entity
  432. set prev_last_action = last_action,
  433. last_action = {$time} where guid = {$user_guid}";
  434. execute_delayed_write_query($query);
  435. }
  436. /**
  437. * Sets the last logon time of the given user to right now.
  438. *
  439. * @param int $user_guid The user GUID
  440. *
  441. * @return void
  442. */
  443. function setLastLogin($user_guid) {
  444. $user_guid = (int) $user_guid;
  445. $time = time();
  446. $query = "UPDATE {$this->CONFIG->dbprefix}users_entity
  447. set prev_last_login = last_login, last_login = {$time} where guid = {$user_guid}";
  448. execute_delayed_write_query($query);
  449. }
  450. }