PageRenderTime 26ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/engine/lib/access.php

https://github.com/fragilbert/Elgg
PHP | 1087 lines | 491 code | 146 blank | 450 comment | 104 complexity | 805f5037e97aca07b7639335cebd1ca9 MD5 | raw file
Possible License(s): MIT, BSD-3-Clause, LGPL-2.1, GPL-2.0
  1. <?php
  2. /**
  3. * Functions for Elgg's access system for entities, metadata, and annotations.
  4. *
  5. * Access is generally saved in the database as access_id. This corresponds to
  6. * one of the ACCESS_* constants defined in {@link elgglib.php} or the ID of an
  7. * access collection.
  8. *
  9. * @package Elgg.Core
  10. * @subpackage Access
  11. * @link http://docs.elgg.org/Access
  12. */
  13. /**
  14. * Return an ElggCache static variable cache for the access caches
  15. *
  16. * @staticvar ElggStaticVariableCache $access_cache
  17. * @return \ElggStaticVariableCache
  18. * @access private
  19. */
  20. function _elgg_get_access_cache() {
  21. /**
  22. * A default filestore cache using the dataroot.
  23. */
  24. static $access_cache;
  25. if (!$access_cache) {
  26. $access_cache = new ElggStaticVariableCache('access');
  27. }
  28. return $access_cache;
  29. }
  30. /**
  31. * Return a string of access_ids for $user_id appropriate for inserting into an SQL IN clause.
  32. *
  33. * @uses get_access_array
  34. *
  35. * @link http://docs.elgg.org/Access
  36. * @see get_access_array()
  37. *
  38. * @param int $user_id User ID; defaults to currently logged in user
  39. * @param int $site_id Site ID; defaults to current site
  40. * @param bool $flush If set to true, will refresh the access list from the
  41. * database rather than using this function's cache.
  42. *
  43. * @return string A list of access collections suitable for using in an SQL call
  44. * @access private
  45. */
  46. function get_access_list($user_id = 0, $site_id = 0, $flush = false) {
  47. global $CONFIG, $init_finished;
  48. $cache = _elgg_get_access_cache();
  49. if ($flush) {
  50. $cache->clear();
  51. }
  52. if ($user_id == 0) {
  53. $user_id = elgg_get_logged_in_user_guid();
  54. }
  55. if (($site_id == 0) && (isset($CONFIG->site_id))) {
  56. $site_id = $CONFIG->site_id;
  57. }
  58. $user_id = (int) $user_id;
  59. $site_id = (int) $site_id;
  60. $hash = $user_id . $site_id . 'get_access_list';
  61. if ($cache[$hash]) {
  62. return $cache[$hash];
  63. }
  64. $access_array = get_access_array($user_id, $site_id, $flush);
  65. $access = "(" . implode(",", $access_array) . ")";
  66. if ($init_finished) {
  67. $cache[$hash] = $access;
  68. }
  69. return $access;
  70. }
  71. /**
  72. * Returns an array of access IDs a user is permitted to see.
  73. *
  74. * Can be overridden with the 'access:collections:read', 'user' plugin hook.
  75. *
  76. * This returns a list of all the collection ids a user owns or belongs
  77. * to plus public and logged in access levels. If the user is an admin, it includes
  78. * the private access level.
  79. *
  80. * @internal this is only used in core for creating the SQL where clause when
  81. * retrieving content from the database. The friends access level is handled by
  82. * get_access_sql_suffix().
  83. *
  84. * @see get_write_access_array() for the access levels that a user can write to.
  85. *
  86. * @param int $user_id User ID; defaults to currently logged in user
  87. * @param int $site_id Site ID; defaults to current site
  88. * @param bool $flush If set to true, will refresh the access ids from the
  89. * database rather than using this function's cache.
  90. *
  91. * @return array An array of access collections ids
  92. */
  93. function get_access_array($user_id = 0, $site_id = 0, $flush = false) {
  94. global $CONFIG, $init_finished;
  95. $cache = _elgg_get_access_cache();
  96. if ($flush) {
  97. $cache->clear();
  98. }
  99. if ($user_id == 0) {
  100. $user_id = elgg_get_logged_in_user_guid();
  101. }
  102. if (($site_id == 0) && (isset($CONFIG->site_guid))) {
  103. $site_id = $CONFIG->site_guid;
  104. }
  105. $user_id = (int) $user_id;
  106. $site_id = (int) $site_id;
  107. $hash = $user_id . $site_id . 'get_access_array';
  108. if ($cache[$hash]) {
  109. $access_array = $cache[$hash];
  110. } else {
  111. $access_array = array(ACCESS_PUBLIC);
  112. // The following can only return sensible data if the user is logged in.
  113. if (elgg_is_logged_in()) {
  114. $access_array[] = ACCESS_LOGGED_IN;
  115. // Get ACL memberships
  116. $query = "SELECT am.access_collection_id"
  117. . " FROM {$CONFIG->dbprefix}access_collection_membership am"
  118. . " LEFT JOIN {$CONFIG->dbprefix}access_collections ag ON ag.id = am.access_collection_id"
  119. . " WHERE am.user_guid = $user_id AND (ag.site_guid = $site_id OR ag.site_guid = 0)";
  120. $collections = get_data($query);
  121. if ($collections) {
  122. foreach ($collections as $collection) {
  123. if (!empty($collection->access_collection_id)) {
  124. $access_array[] = (int)$collection->access_collection_id;
  125. }
  126. }
  127. }
  128. // Get ACLs owned.
  129. $query = "SELECT ag.id FROM {$CONFIG->dbprefix}access_collections ag ";
  130. $query .= "WHERE ag.owner_guid = $user_id AND (ag.site_guid = $site_id OR ag.site_guid = 0)";
  131. $collections = get_data($query);
  132. if ($collections) {
  133. foreach ($collections as $collection) {
  134. if (!empty($collection->id)) {
  135. $access_array[] = (int)$collection->id;
  136. }
  137. }
  138. }
  139. $ignore_access = elgg_check_access_overrides($user_id);
  140. if ($ignore_access == true) {
  141. $access_array[] = ACCESS_PRIVATE;
  142. }
  143. }
  144. if ($init_finished) {
  145. $cache[$hash] = $access_array;
  146. }
  147. }
  148. $options = array(
  149. 'user_id' => $user_id,
  150. 'site_id' => $site_id
  151. );
  152. return elgg_trigger_plugin_hook('access:collections:read', 'user', $options, $access_array);
  153. }
  154. /**
  155. * Gets the default access permission.
  156. *
  157. * This returns the default access level for the site or optionally of the user.
  158. * If want you to change the default access based on group of other information,
  159. * use the 'default', 'access' plugin hook.
  160. *
  161. * @param ElggUser $user Get the user's default access. Defaults to logged in user.
  162. *
  163. * @return int default access id (see ACCESS defines in elgglib.php)
  164. * @link http://docs.elgg.org/Access
  165. */
  166. function get_default_access(ElggUser $user = null) {
  167. global $CONFIG;
  168. // site default access
  169. $default_access = $CONFIG->default_access;
  170. // user default access if enabled
  171. if ($CONFIG->allow_user_default_access) {
  172. $user = $user ? $user : elgg_get_logged_in_user_entity();
  173. if ($user) {
  174. $user_access = $user->getPrivateSetting('elgg_default_access');
  175. if ($user_access !== false) {
  176. $default_access = $user_access;
  177. }
  178. }
  179. }
  180. $params = array(
  181. 'user' => $user,
  182. 'default_access' => $default_access,
  183. );
  184. return elgg_trigger_plugin_hook('default', 'access', $params, $default_access);
  185. }
  186. /**
  187. * Allow disabled entities and metadata to be returned by getter functions
  188. *
  189. * @todo Replace this with query object!
  190. * @global bool $ENTITY_SHOW_HIDDEN_OVERRIDE
  191. * @access private
  192. */
  193. $ENTITY_SHOW_HIDDEN_OVERRIDE = false;
  194. /**
  195. * Show or hide disabled entities.
  196. *
  197. * @param bool $show_hidden Show disabled entities.
  198. * @return bool
  199. * @access private
  200. */
  201. function access_show_hidden_entities($show_hidden) {
  202. global $ENTITY_SHOW_HIDDEN_OVERRIDE;
  203. $current_value = $ENTITY_SHOW_HIDDEN_OVERRIDE;
  204. $ENTITY_SHOW_HIDDEN_OVERRIDE = $show_hidden;
  205. return $current_value;
  206. }
  207. /**
  208. * Return current status of showing disabled entities.
  209. *
  210. * @return bool
  211. * @access private
  212. */
  213. function access_get_show_hidden_status() {
  214. global $ENTITY_SHOW_HIDDEN_OVERRIDE;
  215. return $ENTITY_SHOW_HIDDEN_OVERRIDE;
  216. }
  217. /**
  218. * Returns the SQL where clause for a table with a access_id and enabled columns.
  219. *
  220. * This handles returning where clauses for ACCESS_FRIENDS and the currently
  221. * unused block and filter lists in addition to using get_access_list() for
  222. * access collections and the standard access levels.
  223. *
  224. * @param string $table_prefix Optional table. prefix for the access code.
  225. * @param int $owner The guid to check access for. Defaults to logged in user.
  226. *
  227. * @return string The SQL for a where clause
  228. * @access private
  229. */
  230. function get_access_sql_suffix($table_prefix = '', $owner = null) {
  231. global $ENTITY_SHOW_HIDDEN_OVERRIDE, $CONFIG;
  232. $sql = "";
  233. $friends_bit = "";
  234. $enemies_bit = "";
  235. if ($table_prefix) {
  236. $table_prefix = sanitise_string($table_prefix) . ".";
  237. }
  238. if (!isset($owner)) {
  239. $owner = elgg_get_logged_in_user_guid();
  240. }
  241. if (!$owner) {
  242. $owner = -1;
  243. }
  244. $ignore_access = elgg_check_access_overrides($owner);
  245. $access = get_access_list($owner);
  246. if ($ignore_access) {
  247. $sql = " (1 = 1) ";
  248. } else if ($owner != -1) {
  249. // we have an entity's guid and auto check for friend relationships
  250. $friends_bit = "{$table_prefix}access_id = " . ACCESS_FRIENDS . "
  251. AND {$table_prefix}owner_guid IN (
  252. SELECT guid_one FROM {$CONFIG->dbprefix}entity_relationships
  253. WHERE relationship='friend' AND guid_two=$owner
  254. )";
  255. $friends_bit = '(' . $friends_bit . ') OR ';
  256. // @todo untested and unsupported at present
  257. if ((isset($CONFIG->user_block_and_filter_enabled)) && ($CONFIG->user_block_and_filter_enabled)) {
  258. // check to see if the user is in the entity owner's block list
  259. // or if the entity owner is in the user's filter list
  260. // if so, disallow access
  261. $enemies_bit = get_access_restriction_sql('elgg_block_list', "{$table_prefix}owner_guid", $owner, false);
  262. $enemies_bit = '('
  263. . $enemies_bit
  264. . ' AND ' . get_access_restriction_sql('elgg_filter_list', $owner, "{$table_prefix}owner_guid", false)
  265. . ')';
  266. }
  267. }
  268. if (empty($sql)) {
  269. $sql = " $friends_bit ({$table_prefix}access_id IN {$access}
  270. OR ({$table_prefix}owner_guid = {$owner})
  271. OR (
  272. {$table_prefix}access_id = " . ACCESS_PRIVATE . "
  273. AND {$table_prefix}owner_guid = $owner
  274. )
  275. )";
  276. }
  277. if ($enemies_bit) {
  278. $sql = "$enemies_bit AND ($sql)";
  279. }
  280. if (!$ENTITY_SHOW_HIDDEN_OVERRIDE) {
  281. $sql .= " and {$table_prefix}enabled='yes'";
  282. }
  283. return '(' . $sql . ')';
  284. }
  285. /**
  286. * Get the where clause for an access restriction based on annotations
  287. *
  288. * Returns an SQL fragment that is true (or optionally false) if the given user has
  289. * added an annotation with the given name to the given entity.
  290. *
  291. * @warning this is a private function for an untested capability and will likely
  292. * be removed from a future version of Elgg.
  293. *
  294. * @param string $annotation_name Name of the annotation
  295. * @param string $entity_guid SQL GUID of entity the annotation is attached to.
  296. * @param string $owner_guid SQL string that evaluates to the GUID of the annotation owner
  297. * @param boolean $exists If true, returns BOOL if the annotation exists
  298. *
  299. * @return string An SQL fragment suitable for inserting into a WHERE clause
  300. * @access private
  301. */
  302. function get_access_restriction_sql($annotation_name, $entity_guid, $owner_guid, $exists) {
  303. global $CONFIG;
  304. if ($exists) {
  305. $not = '';
  306. } else {
  307. $not = 'NOT';
  308. }
  309. $sql = <<<END
  310. $not EXISTS (SELECT * FROM {$CONFIG->dbprefix}annotations a
  311. INNER JOIN {$CONFIG->dbprefix}metastrings ms ON (a.name_id = ms.id)
  312. WHERE ms.string = '$annotation_name'
  313. AND a.entity_guid = $entity_guid
  314. AND a.owner_guid = $owner_guid)
  315. END;
  316. return $sql;
  317. }
  318. /**
  319. * Can a user access an entity.
  320. *
  321. * @warning If a logged in user doesn't have access to an entity, the
  322. * core engine will not load that entity.
  323. *
  324. * @tip This is mostly useful for checking if a user other than the logged in
  325. * user has access to an entity that is currently loaded.
  326. *
  327. * @todo This function would be much more useful if we could pass the guid of the
  328. * entity to test access for. We need to be able to tell whether the entity exists
  329. * and whether the user has access to the entity.
  330. *
  331. * @param ElggEntity $entity The entity to check access for.
  332. * @param ElggUser $user Optionally user to check access for. Defaults to
  333. * logged in user (which is a useless default).
  334. *
  335. * @return bool
  336. * @link http://docs.elgg.org/Access
  337. */
  338. function has_access_to_entity($entity, $user = null) {
  339. global $CONFIG;
  340. if (!isset($user)) {
  341. $access_bit = get_access_sql_suffix("e");
  342. } else {
  343. $access_bit = get_access_sql_suffix("e", $user->getGUID());
  344. }
  345. $query = "SELECT guid from {$CONFIG->dbprefix}entities e WHERE e.guid = " . $entity->getGUID();
  346. // Add access controls
  347. $query .= " AND " . $access_bit;
  348. if (get_data($query)) {
  349. return true;
  350. } else {
  351. return false;
  352. }
  353. }
  354. /**
  355. * Returns an array of access permissions that the user is allowed to save content with.
  356. * Permissions returned are of the form (id => 'name').
  357. *
  358. * Example return value in English:
  359. * array(
  360. * 0 => 'Private',
  361. * -2 => 'Friends',
  362. * 1 => 'Logged in users',
  363. * 2 => 'Public',
  364. * 34 => 'My favorite friends',
  365. * );
  366. *
  367. * Plugin hook of 'access:collections:write', 'user'
  368. *
  369. * @warning this only returns access collections that the user owns plus the
  370. * standard access levels. It does not return access collections that the user
  371. * belongs to such as the access collection for a group.
  372. *
  373. * @param int $user_id The user's GUID.
  374. * @param int $site_id The current site.
  375. * @param bool $flush If this is set to true, this will ignore a cached access array
  376. *
  377. * @return array List of access permissions
  378. * @link http://docs.elgg.org/Access
  379. */
  380. function get_write_access_array($user_id = 0, $site_id = 0, $flush = false) {
  381. global $CONFIG, $init_finished;
  382. $cache = _elgg_get_access_cache();
  383. if ($flush) {
  384. $cache->clear();
  385. }
  386. if ($user_id == 0) {
  387. $user_id = elgg_get_logged_in_user_guid();
  388. }
  389. if (($site_id == 0) && (isset($CONFIG->site_id))) {
  390. $site_id = $CONFIG->site_id;
  391. }
  392. $user_id = (int) $user_id;
  393. $site_id = (int) $site_id;
  394. $hash = $user_id . $site_id . 'get_write_access_array';
  395. if ($cache[$hash]) {
  396. $access_array = $cache[$hash];
  397. } else {
  398. // @todo is there such a thing as public write access?
  399. $access_array = array(
  400. ACCESS_PRIVATE => elgg_echo("PRIVATE"),
  401. ACCESS_FRIENDS => elgg_echo("access:friends:label"),
  402. ACCESS_LOGGED_IN => elgg_echo("LOGGED_IN"),
  403. ACCESS_PUBLIC => elgg_echo("PUBLIC")
  404. );
  405. $query = "SELECT ag.* FROM {$CONFIG->dbprefix}access_collections ag ";
  406. $query .= " WHERE (ag.site_guid = $site_id OR ag.site_guid = 0)";
  407. $query .= " AND (ag.owner_guid = $user_id)";
  408. $collections = get_data($query);
  409. if ($collections) {
  410. foreach ($collections as $collection) {
  411. $access_array[$collection->id] = $collection->name;
  412. }
  413. }
  414. if ($init_finished) {
  415. $cache[$hash] = $access_array;
  416. }
  417. }
  418. $options = array(
  419. 'user_id' => $user_id,
  420. 'site_id' => $site_id
  421. );
  422. return elgg_trigger_plugin_hook('access:collections:write', 'user',
  423. $options, $access_array);
  424. }
  425. /**
  426. * Can the user change this access collection?
  427. *
  428. * Use the plugin hook of 'access:collections:write', 'user' to change this.
  429. * @see get_write_access_array() for details on the hook.
  430. *
  431. * Respects access control disabling for admin users and {@see elgg_set_ignore_access()}
  432. *
  433. * @see get_write_access_array()
  434. *
  435. * @param int $collection_id The collection id
  436. * @param mixed $user_guid The user GUID to check for. Defaults to logged in user.
  437. * @return bool
  438. */
  439. function can_edit_access_collection($collection_id, $user_guid = null) {
  440. if ($user_guid) {
  441. $user = get_entity((int) $user_guid);
  442. } else {
  443. $user = elgg_get_logged_in_user_entity();
  444. }
  445. $collection = get_access_collection($collection_id);
  446. if (!($user instanceof ElggUser) || !$collection) {
  447. return false;
  448. }
  449. $write_access = get_write_access_array($user->getGUID(), 0, true);
  450. // don't ignore access when checking users.
  451. if ($user_guid) {
  452. return array_key_exists($collection_id, $write_access);
  453. } else {
  454. return elgg_get_ignore_access() || array_key_exists($collection_id, $write_access);
  455. }
  456. }
  457. /**
  458. * Creates a new access collection.
  459. *
  460. * Access colletions allow plugins and users to create granular access
  461. * for entities.
  462. *
  463. * Triggers plugin hook 'access:collections:addcollection', 'collection'
  464. *
  465. * @internal Access collections are stored in the access_collections table.
  466. * Memberships to collections are in access_collections_membership.
  467. *
  468. * @param string $name The name of the collection.
  469. * @param int $owner_guid The GUID of the owner (default: currently logged in user).
  470. * @param int $site_guid The GUID of the site (default: current site).
  471. *
  472. * @return int|false The collection ID if successful and false on failure.
  473. * @link http://docs.elgg.org/Access/Collections
  474. * @see update_access_collection()
  475. * @see delete_access_collection()
  476. */
  477. function create_access_collection($name, $owner_guid = 0, $site_guid = 0) {
  478. global $CONFIG;
  479. $name = trim($name);
  480. if (empty($name)) {
  481. return false;
  482. }
  483. if ($owner_guid == 0) {
  484. $owner_guid = elgg_get_logged_in_user_guid();
  485. }
  486. if (($site_guid == 0) && (isset($CONFIG->site_guid))) {
  487. $site_guid = $CONFIG->site_guid;
  488. }
  489. $name = sanitise_string($name);
  490. $q = "INSERT INTO {$CONFIG->dbprefix}access_collections
  491. SET name = '{$name}',
  492. owner_guid = {$owner_guid},
  493. site_guid = {$site_guid}";
  494. $id = insert_data($q);
  495. if (!$id) {
  496. return false;
  497. }
  498. $params = array(
  499. 'collection_id' => $id
  500. );
  501. if (!elgg_trigger_plugin_hook('access:collections:addcollection', 'collection', $params, true)) {
  502. return false;
  503. }
  504. return $id;
  505. }
  506. /**
  507. * Updates the membership in an access collection.
  508. *
  509. * @warning Expects a full list of all members that should
  510. * be part of the access collection
  511. *
  512. * @note This will run all hooks associated with adding or removing
  513. * members to access collections.
  514. *
  515. * @param int $collection_id The ID of the collection.
  516. * @param array $members Array of member GUIDs
  517. *
  518. * @return bool
  519. * @link http://docs.elgg.org/Access/Collections
  520. * @see add_user_to_access_collection()
  521. * @see remove_user_from_access_collection()
  522. */
  523. function update_access_collection($collection_id, $members) {
  524. $acl = get_access_collection($collection_id);
  525. if (!$acl) {
  526. return false;
  527. }
  528. $members = (is_array($members)) ? $members : array();
  529. $cur_members = get_members_of_access_collection($collection_id, true);
  530. $cur_members = (is_array($cur_members)) ? $cur_members : array();
  531. $remove_members = array_diff($cur_members, $members);
  532. $add_members = array_diff($members, $cur_members);
  533. $result = true;
  534. foreach ($add_members as $guid) {
  535. $result = $result && add_user_to_access_collection($guid, $collection_id);
  536. }
  537. foreach ($remove_members as $guid) {
  538. $result = $result && remove_user_from_access_collection($guid, $collection_id);
  539. }
  540. return $result;
  541. }
  542. /**
  543. * Deletes a specified access collection and its membership.
  544. *
  545. * @param int $collection_id The collection ID
  546. *
  547. * @return bool
  548. * @link http://docs.elgg.org/Access/Collections
  549. * @see create_access_collection()
  550. * @see update_access_collection()
  551. */
  552. function delete_access_collection($collection_id) {
  553. global $CONFIG;
  554. $collection_id = (int) $collection_id;
  555. $params = array('collection_id' => $collection_id);
  556. if (!elgg_trigger_plugin_hook('access:collections:deletecollection', 'collection', $params, true)) {
  557. return false;
  558. }
  559. // Deleting membership doesn't affect result of deleting ACL.
  560. $q = "DELETE FROM {$CONFIG->dbprefix}access_collection_membership
  561. WHERE access_collection_id = {$collection_id}";
  562. delete_data($q);
  563. $q = "DELETE FROM {$CONFIG->dbprefix}access_collections
  564. WHERE id = {$collection_id}";
  565. $result = delete_data($q);
  566. return (bool)$result;
  567. }
  568. /**
  569. * Get a specified access collection
  570. *
  571. * @note This doesn't return the members of an access collection,
  572. * just the database row of the actual collection.
  573. *
  574. * @see get_members_of_access_collection()
  575. *
  576. * @param int $collection_id The collection ID
  577. *
  578. * @return object|false
  579. */
  580. function get_access_collection($collection_id) {
  581. global $CONFIG;
  582. $collection_id = (int) $collection_id;
  583. $query = "SELECT * FROM {$CONFIG->dbprefix}access_collections WHERE id = {$collection_id}";
  584. $get_collection = get_data_row($query);
  585. return $get_collection;
  586. }
  587. /**
  588. * Adds a user to an access collection.
  589. *
  590. * Triggers the 'access:collections:add_user', 'collection' plugin hook.
  591. *
  592. * @param int $user_guid The GUID of the user to add
  593. * @param int $collection_id The ID of the collection to add them to
  594. *
  595. * @return bool
  596. * @see update_access_collection()
  597. * @see remove_user_from_access_collection()
  598. * @link http://docs.elgg.org/Access/Collections
  599. */
  600. function add_user_to_access_collection($user_guid, $collection_id) {
  601. global $CONFIG;
  602. $collection_id = (int) $collection_id;
  603. $user_guid = (int) $user_guid;
  604. $user = get_user($user_guid);
  605. $collection = get_access_collection($collection_id);
  606. if (!($user instanceof Elgguser) || !$collection) {
  607. return false;
  608. }
  609. $params = array(
  610. 'collection_id' => $collection_id,
  611. 'user_guid' => $user_guid
  612. );
  613. $result = elgg_trigger_plugin_hook('access:collections:add_user', 'collection', $params, true);
  614. if ($result == false) {
  615. return false;
  616. }
  617. // if someone tries to insert the same data twice, we do a no-op on duplicate key
  618. $q = "INSERT INTO {$CONFIG->dbprefix}access_collection_membership
  619. SET access_collection_id = $collection_id, user_guid = $user_guid
  620. ON DUPLICATE KEY UPDATE user_guid = user_guid";
  621. $result = insert_data($q);
  622. return $result !== false;
  623. }
  624. /**
  625. * Removes a user from an access collection.
  626. *
  627. * Triggers the 'access:collections:remove_user', 'collection' plugin hook.
  628. *
  629. * @param int $user_guid The user GUID
  630. * @param int $collection_id The access collection ID
  631. *
  632. * @return bool
  633. * @see update_access_collection()
  634. * @see remove_user_from_access_collection()
  635. * @link http://docs.elgg.org/Access/Collections
  636. */
  637. function remove_user_from_access_collection($user_guid, $collection_id) {
  638. global $CONFIG;
  639. $collection_id = (int) $collection_id;
  640. $user_guid = (int) $user_guid;
  641. $user = get_user($user_guid);
  642. $collection = get_access_collection($collection_id);
  643. if (!($user instanceof Elgguser) || !$collection) {
  644. return false;
  645. }
  646. $params = array(
  647. 'collection_id' => $collection_id,
  648. 'user_guid' => $user_guid
  649. );
  650. if (!elgg_trigger_plugin_hook('access:collections:remove_user', 'collection', $params, true)) {
  651. return false;
  652. }
  653. $q = "DELETE FROM {$CONFIG->dbprefix}access_collection_membership
  654. WHERE access_collection_id = {$collection_id}
  655. AND user_guid = {$user_guid}";
  656. return (bool)delete_data($q);
  657. }
  658. /**
  659. * Returns an array of database row objects of the access collections owned by $owner_guid.
  660. *
  661. * @param int $owner_guid The entity guid
  662. * @param int $site_guid The GUID of the site (default: current site).
  663. *
  664. * @return array|false
  665. * @see add_access_collection()
  666. * @see get_members_of_access_collection()
  667. * @link http://docs.elgg.org/Access/Collections
  668. */
  669. function get_user_access_collections($owner_guid, $site_guid = 0) {
  670. global $CONFIG;
  671. $owner_guid = (int) $owner_guid;
  672. $site_guid = (int) $site_guid;
  673. if (($site_guid == 0) && (isset($CONFIG->site_guid))) {
  674. $site_guid = $CONFIG->site_guid;
  675. }
  676. $query = "SELECT * FROM {$CONFIG->dbprefix}access_collections
  677. WHERE owner_guid = {$owner_guid}
  678. AND site_guid = {$site_guid}";
  679. $collections = get_data($query);
  680. return $collections;
  681. }
  682. /**
  683. * Get all of members of an access collection
  684. *
  685. * @param int $collection The collection's ID
  686. * @param bool $idonly If set to true, will only return the members' GUIDs (default: false)
  687. *
  688. * @return array ElggUser guids or entities if successful, false if not
  689. * @see add_user_to_access_collection()
  690. * @see http://docs.elgg.org/Access/Collections
  691. */
  692. function get_members_of_access_collection($collection, $idonly = FALSE) {
  693. global $CONFIG;
  694. $collection = (int)$collection;
  695. if (!$idonly) {
  696. $query = "SELECT e.* FROM {$CONFIG->dbprefix}access_collection_membership m"
  697. . " JOIN {$CONFIG->dbprefix}entities e ON e.guid = m.user_guid"
  698. . " WHERE m.access_collection_id = {$collection}";
  699. $collection_members = get_data($query, "entity_row_to_elggstar");
  700. } else {
  701. $query = "SELECT e.guid FROM {$CONFIG->dbprefix}access_collection_membership m"
  702. . " JOIN {$CONFIG->dbprefix}entities e ON e.guid = m.user_guid"
  703. . " WHERE m.access_collection_id = {$collection}";
  704. $collection_members = get_data($query);
  705. if (!$collection_members) {
  706. return FALSE;
  707. }
  708. foreach ($collection_members as $key => $val) {
  709. $collection_members[$key] = $val->guid;
  710. }
  711. }
  712. return $collection_members;
  713. }
  714. /**
  715. * Return entities based upon access id.
  716. *
  717. * @param array $options Any options accepted by {@link elgg_get_entities()} and
  718. * access_id => int The access ID of the entity.
  719. *
  720. * @see elgg_get_entities()
  721. * @return mixed If count, int. If not count, array. false on errors.
  722. * @since 1.7.0
  723. */
  724. function elgg_get_entities_from_access_id(array $options = array()) {
  725. // restrict the resultset to access collection provided
  726. if (!isset($options['access_id'])) {
  727. return FALSE;
  728. }
  729. // @todo add support for an array of collection_ids
  730. $where = "e.access_id = '{$options['access_id']}'";
  731. if (isset($options['wheres'])) {
  732. if (is_array($options['wheres'])) {
  733. $options['wheres'][] = $where;
  734. } else {
  735. $options['wheres'] = array($options['wheres'], $where);
  736. }
  737. } else {
  738. $options['wheres'] = array($where);
  739. }
  740. // return entities with the desired options
  741. return elgg_get_entities($options);
  742. }
  743. /**
  744. * Lists entities from an access collection
  745. *
  746. * @param array $options See elgg_list_entities() and elgg_get_entities_from_access_id()
  747. *
  748. * @see elgg_list_entities()
  749. * @see elgg_get_entities_from_access_id()
  750. *
  751. * @return string
  752. */
  753. function elgg_list_entities_from_access_id(array $options = array()) {
  754. return elgg_list_entities($options, 'elgg_get_entities_from_access_id');
  755. }
  756. /**
  757. * Return the name of an ACCESS_* constant or a access collection,
  758. * but only if the user has write access on that ACL.
  759. *
  760. * @warning This function probably doesn't work how it's meant to.
  761. *
  762. * @param int $entity_access_id The entity's access id
  763. *
  764. * @return string 'Public', 'Private', etc.
  765. * @since 1.7.0
  766. * @todo I think this probably wants get_access_array() instead of get_write_access_array(),
  767. * but those two functions return different types of arrays.
  768. */
  769. function get_readable_access_level($entity_access_id) {
  770. $access = (int) $entity_access_id;
  771. //get the access level for object in readable string
  772. $options = get_write_access_array();
  773. if (array_key_exists($access, $options)) {
  774. return $options[$access];
  775. }
  776. // return 'Limited' if the user does not have access to the access collection
  777. return elgg_echo('access:limited:label');
  778. }
  779. /**
  780. * Set if entity access system should be ignored.
  781. *
  782. * The access system will not return entities in any getter
  783. * functions if the user doesn't have access.
  784. *
  785. * @internal For performance reasons this is done at the database access clause level.
  786. *
  787. * @tip Use this to access entities in automated scripts
  788. * when no user is logged in.
  789. *
  790. * @note This clears the access cache.
  791. *
  792. * @warning This will not show disabled entities.
  793. * Use {@link access_show_hidden_entities()} to access disabled entities.
  794. *
  795. * @param bool $ignore If true, disables all access checks.
  796. *
  797. * @return bool Previous ignore_access setting.
  798. * @since 1.7.0
  799. * @see http://docs.elgg.org/Access/IgnoreAccess
  800. * @see elgg_get_ignore_access()
  801. */
  802. function elgg_set_ignore_access($ignore = true) {
  803. $cache = _elgg_get_access_cache();
  804. $cache->clear();
  805. $elgg_access = elgg_get_access_object();
  806. return $elgg_access->setIgnoreAccess($ignore);
  807. }
  808. /**
  809. * Get current ignore access setting.
  810. *
  811. * @return bool
  812. * @since 1.7.0
  813. * @see http://docs.elgg.org/Access/IgnoreAccess
  814. * @see elgg_set_ignore_access()
  815. */
  816. function elgg_get_ignore_access() {
  817. return elgg_get_access_object()->getIgnoreAccess();
  818. }
  819. /**
  820. * Decides if the access system should be ignored for a user.
  821. *
  822. * Returns true (meaning ignore access) if either of these 2 conditions are true:
  823. * 1) an admin user guid is passed to this function.
  824. * 2) {@link elgg_get_ignore_access()} returns true.
  825. *
  826. * @see elgg_set_ignore_access()
  827. *
  828. * @param int $user_guid The user to check against.
  829. *
  830. * @return bool
  831. * @since 1.7.0
  832. */
  833. function elgg_check_access_overrides($user_guid = 0) {
  834. if (!$user_guid || $user_guid <= 0) {
  835. $is_admin = false;
  836. } else {
  837. $is_admin = elgg_is_admin_user($user_guid);
  838. }
  839. return ($is_admin || elgg_get_ignore_access());
  840. }
  841. /**
  842. * Returns the ElggAccess object.
  843. *
  844. * // @todo comment is incomplete
  845. * This is used to
  846. *
  847. * @return ElggAccess
  848. * @since 1.7.0
  849. * @access private
  850. */
  851. function elgg_get_access_object() {
  852. static $elgg_access;
  853. if (!$elgg_access) {
  854. $elgg_access = new ElggAccess();
  855. }
  856. return $elgg_access;
  857. }
  858. /**
  859. * A flag to set if Elgg's access initialization is finished.
  860. *
  861. * @global bool $init_finished
  862. * @access private
  863. * @todo This is required to tell the access system to start caching because
  864. * calls are made while in ignore access mode and before the user is logged in.
  865. */
  866. $init_finished = false;
  867. /**
  868. * A quick and dirty way to make sure the access permissions have been correctly set up
  869. *
  870. * @elgg_event_handler init system
  871. * @todo Invesigate
  872. *
  873. * @return void
  874. */
  875. function access_init() {
  876. global $init_finished;
  877. $init_finished = true;
  878. }
  879. /**
  880. * Overrides the access system if appropriate.
  881. *
  882. * Allows admin users and calls after {@link elgg_set_ignore_access} to
  883. * bypass the access system.
  884. *
  885. * Registered for the 'permissions_check', 'all' and the
  886. * 'container_permissions_check', 'all' plugin hooks.
  887. *
  888. * Returns true to override the access system or null if no change is needed.
  889. *
  890. * @param string $hook
  891. * @param string $type
  892. * @param bool $value
  893. * @param array $params
  894. * @return true|null
  895. * @access private
  896. */
  897. function elgg_override_permissions($hook, $type, $value, $params) {
  898. $user = elgg_extract('user', $params);
  899. if ($user) {
  900. $user_guid = $user->getGUID();
  901. } else {
  902. $user_guid = elgg_get_logged_in_user_guid();
  903. }
  904. // don't do this so ignore access still works with no one logged in
  905. //if (!$user instanceof ElggUser) {
  906. // return false;
  907. //}
  908. // check for admin
  909. if ($user_guid && elgg_is_admin_user($user_guid)) {
  910. return true;
  911. }
  912. // check access overrides
  913. if ((elgg_check_access_overrides($user_guid))) {
  914. return true;
  915. }
  916. // consult other hooks
  917. return NULL;
  918. }
  919. /**
  920. * Runs unit tests for the entities object.
  921. *
  922. * @param string $hook
  923. * @param string $type
  924. * @param array $value
  925. * @param array $params
  926. * @return array
  927. *
  928. * @access private
  929. */
  930. function access_test($hook, $type, $value, $params) {
  931. global $CONFIG;
  932. $value[] = $CONFIG->path . 'engine/tests/ElggCoreAccessCollectionsTest.php';
  933. return $value;
  934. }
  935. // Tell the access functions the system has booted, plugins are loaded,
  936. // and the user is logged in so it can start caching
  937. elgg_register_event_handler('ready', 'system', 'access_init');
  938. // For overrided permissions
  939. elgg_register_plugin_hook_handler('permissions_check', 'all', 'elgg_override_permissions');
  940. elgg_register_plugin_hook_handler('container_permissions_check', 'all', 'elgg_override_permissions');
  941. elgg_register_plugin_hook_handler('unit_test', 'system', 'access_test');