entities.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  1. <?php
  2. /**
  3. * Procedural code for creating, loading, and modifying \ElggEntity objects.
  4. *
  5. * @package Elgg.Core
  6. * @subpackage DataModel.Entities
  7. */
  8. /**
  9. * Cache entities in memory once loaded.
  10. *
  11. * @global \ElggEntity[] $ENTITY_CACHE
  12. * @access private
  13. */
  14. global $ENTITY_CACHE;
  15. $ENTITY_CACHE = array();
  16. /**
  17. * GUIDs of entities banned from the entity cache (during this request)
  18. *
  19. * @global array $ENTITY_CACHE_DISABLED_GUIDS
  20. * @access private
  21. */
  22. global $ENTITY_CACHE_DISABLED_GUIDS;
  23. $ENTITY_CACHE_DISABLED_GUIDS = array();
  24. /**
  25. * Remove this entity from the entity cache and make sure it is not re-added
  26. *
  27. * @param int $guid The entity guid
  28. *
  29. * @access private
  30. * @todo this is a workaround until #5604 can be implemented
  31. */
  32. function _elgg_disable_caching_for_entity($guid) {
  33. global $ENTITY_CACHE_DISABLED_GUIDS;
  34. _elgg_invalidate_cache_for_entity($guid);
  35. $ENTITY_CACHE_DISABLED_GUIDS[$guid] = true;
  36. }
  37. /**
  38. * Allow this entity to be stored in the entity cache
  39. *
  40. * @param int $guid The entity guid
  41. *
  42. * @access private
  43. */
  44. function _elgg_enable_caching_for_entity($guid) {
  45. global $ENTITY_CACHE_DISABLED_GUIDS;
  46. unset($ENTITY_CACHE_DISABLED_GUIDS[$guid]);
  47. }
  48. /**
  49. * Invalidate this class's entry in the cache.
  50. *
  51. * @param int $guid The entity guid
  52. *
  53. * @return void
  54. * @access private
  55. */
  56. function _elgg_invalidate_cache_for_entity($guid) {
  57. global $ENTITY_CACHE;
  58. $guid = (int)$guid;
  59. if (isset($ENTITY_CACHE[$guid])) {
  60. unset($ENTITY_CACHE[$guid]);
  61. // Purge separate metadata cache. Original idea was to do in entity destructor, but that would
  62. // have caused a bunch of unnecessary purges at every shutdown. Doing it this way we have no way
  63. // to know that the expunged entity will be GCed (might be another reference living), but that's
  64. // OK; the metadata will reload if necessary.
  65. _elgg_services()->metadataCache->clear($guid);
  66. }
  67. }
  68. /**
  69. * Cache an entity.
  70. *
  71. * Stores an entity in $ENTITY_CACHE;
  72. *
  73. * @param \ElggEntity $entity Entity to cache
  74. *
  75. * @return void
  76. * @see _elgg_retrieve_cached_entity()
  77. * @see _elgg_invalidate_cache_for_entity()
  78. * @access private
  79. * @todo Use an \ElggCache object
  80. */
  81. function _elgg_cache_entity(\ElggEntity $entity) {
  82. global $ENTITY_CACHE, $ENTITY_CACHE_DISABLED_GUIDS;
  83. // Don't cache non-plugin entities while access control is off, otherwise they could be
  84. // exposed to users who shouldn't see them when control is re-enabled.
  85. if (!($entity instanceof \ElggPlugin) && elgg_get_ignore_access()) {
  86. return;
  87. }
  88. $guid = $entity->getGUID();
  89. if (isset($ENTITY_CACHE_DISABLED_GUIDS[$guid])) {
  90. return;
  91. }
  92. // Don't store too many or we'll have memory problems
  93. // @todo Pick a less arbitrary limit
  94. if (count($ENTITY_CACHE) > 256) {
  95. _elgg_invalidate_cache_for_entity(array_rand($ENTITY_CACHE));
  96. }
  97. $ENTITY_CACHE[$guid] = $entity;
  98. }
  99. /**
  100. * Retrieve a entity from the cache.
  101. *
  102. * @param int $guid The guid
  103. *
  104. * @return \ElggEntity|bool false if entity not cached, or not fully loaded
  105. * @see _elgg_cache_entity()
  106. * @see _elgg_invalidate_cache_for_entity()
  107. * @access private
  108. */
  109. function _elgg_retrieve_cached_entity($guid) {
  110. global $ENTITY_CACHE;
  111. if (isset($ENTITY_CACHE[$guid])) {
  112. if ($ENTITY_CACHE[$guid]->isFullyLoaded()) {
  113. return $ENTITY_CACHE[$guid];
  114. }
  115. }
  116. return false;
  117. }
  118. /**
  119. * Return the id for a given subtype.
  120. *
  121. * \ElggEntity objects have a type and a subtype. Subtypes
  122. * are defined upon creation and cannot be changed.
  123. *
  124. * Plugin authors generally don't need to use this function
  125. * unless writing their own SQL queries. Use {@link \ElggEntity::getSubtype()}
  126. * to return the string subtype.
  127. *
  128. * @internal Subtypes are stored in the entity_subtypes table. There is a foreign
  129. * key in the entities table.
  130. *
  131. * @param string $type Type
  132. * @param string $subtype Subtype
  133. *
  134. * @return int Subtype ID
  135. * @see get_subtype_from_id()
  136. * @access private
  137. */
  138. function get_subtype_id($type, $subtype) {
  139. return _elgg_services()->subtypeTable->getId($type, $subtype);
  140. }
  141. /**
  142. * Gets the denormalized string for a given subtype ID.
  143. *
  144. * @param int $subtype_id Subtype ID from database
  145. * @return string|false Subtype name, false if subtype not found
  146. * @see get_subtype_id()
  147. * @access private
  148. */
  149. function get_subtype_from_id($subtype_id) {
  150. return _elgg_services()->subtypeTable->getSubtype($subtype_id);
  151. }
  152. /**
  153. * Retrieve subtype from the cache.
  154. *
  155. * @param string $type
  156. * @param string $subtype
  157. * @return \stdClass|null
  158. *
  159. * @access private
  160. */
  161. function _elgg_retrieve_cached_subtype($type, $subtype) {
  162. return _elgg_services()->subtypeTable->retrieveFromCache($type, $subtype);
  163. }
  164. /**
  165. * Fetch all suptypes from DB to local cache.
  166. *
  167. * @access private
  168. */
  169. function _elgg_populate_subtype_cache() {
  170. return _elgg_services()->subtypeTable->populateCache();
  171. }
  172. /**
  173. * Return the class name for a registered type and subtype.
  174. *
  175. * Entities can be registered to always be loaded as a certain class
  176. * with add_subtype() or update_subtype(). This function returns the class
  177. * name if found and null if not.
  178. *
  179. * @param string $type The type
  180. * @param string $subtype The subtype
  181. *
  182. * @return string|null a class name or null
  183. * @see get_subtype_from_id()
  184. * @see get_subtype_class_from_id()
  185. * @access private
  186. */
  187. function get_subtype_class($type, $subtype) {
  188. return _elgg_services()->subtypeTable->getClass($type, $subtype);
  189. }
  190. /**
  191. * Returns the class name for a subtype id.
  192. *
  193. * @param int $subtype_id The subtype id
  194. *
  195. * @return string|null
  196. * @see get_subtype_class()
  197. * @see get_subtype_from_id()
  198. * @access private
  199. */
  200. function get_subtype_class_from_id($subtype_id) {
  201. return _elgg_services()->subtypeTable->getClassFromId($subtype_id);
  202. }
  203. /**
  204. * Register \ElggEntities with a certain type and subtype to be loaded as a specific class.
  205. *
  206. * By default entities are loaded as one of the 4 parent objects: site, user, object, or group.
  207. * If you subclass any of these you can register the classname with add_subtype() so
  208. * it will be loaded as that class automatically when retrieved from the database with
  209. * {@link get_entity()}.
  210. *
  211. * @warning This function cannot be used to change the class for a type-subtype pair.
  212. * Use update_subtype() for that.
  213. *
  214. * @param string $type The type you're subtyping (site, user, object, or group)
  215. * @param string $subtype The subtype
  216. * @param string $class Optional class name for the object
  217. *
  218. * @return int
  219. * @see update_subtype()
  220. * @see remove_subtype()
  221. * @see get_entity()
  222. */
  223. function add_subtype($type, $subtype, $class = "") {
  224. return _elgg_services()->subtypeTable->add($type, $subtype, $class);
  225. }
  226. /**
  227. * Removes a registered \ElggEntity type, subtype, and classname.
  228. *
  229. * @warning You do not want to use this function. If you want to unregister
  230. * a class for a subtype, use update_subtype(). Using this function will
  231. * permanently orphan all the objects created with the specified subtype.
  232. *
  233. * @param string $type Type
  234. * @param string $subtype Subtype
  235. *
  236. * @return bool
  237. * @see add_subtype()
  238. * @see update_subtype()
  239. */
  240. function remove_subtype($type, $subtype) {
  241. return _elgg_services()->subtypeTable->remove($type, $subtype);
  242. }
  243. /**
  244. * Update a registered \ElggEntity type, subtype, and class name
  245. *
  246. * @param string $type Type
  247. * @param string $subtype Subtype
  248. * @param string $class Class name to use when loading this entity
  249. *
  250. * @return bool
  251. */
  252. function update_subtype($type, $subtype, $class = '') {
  253. return _elgg_services()->subtypeTable->update($type, $subtype, $class);
  254. }
  255. /**
  256. * Determine if a given user can write to an entity container.
  257. *
  258. * An entity can be a container for any other entity by setting the
  259. * container_guid. container_guid can differ from owner_guid.
  260. *
  261. * A plugin hook container_permissions_check:$entity_type is emitted to allow granular
  262. * access controls in plugins.
  263. *
  264. * @param int $user_guid The user guid, or 0 for logged in user.
  265. * @param int $container_guid The container, or 0 for the current page owner.
  266. * @param string $type The type of entity we want to create (default: 'all')
  267. * @param string $subtype The subtype of the entity we want to create (default: 'all')
  268. *
  269. * @return bool
  270. */
  271. function can_write_to_container($user_guid = 0, $container_guid = 0, $type = 'all', $subtype = 'all') {
  272. $container_guid = (int)$container_guid;
  273. if (!$container_guid) {
  274. $container_guid = elgg_get_page_owner_guid();
  275. }
  276. $container = get_entity($container_guid);
  277. $user_guid = (int)$user_guid;
  278. if ($user_guid == 0) {
  279. $user = elgg_get_logged_in_user_entity();
  280. $user_guid = elgg_get_logged_in_user_guid();
  281. } else {
  282. $user = get_user($user_guid);
  283. if (!$user) {
  284. return false;
  285. }
  286. }
  287. $return = false;
  288. if ($container) {
  289. // If the user can edit the container, they can also write to it
  290. if ($container->canEdit($user_guid)) {
  291. $return = true;
  292. }
  293. }
  294. // See if anyone else has anything to say
  295. return elgg_trigger_plugin_hook(
  296. 'container_permissions_check',
  297. $type,
  298. array(
  299. 'container' => $container,
  300. 'user' => $user,
  301. 'subtype' => $subtype
  302. ),
  303. $return);
  304. }
  305. /**
  306. * Returns a database row from the entities table.
  307. *
  308. * @tip Use get_entity() to return the fully loaded entity.
  309. *
  310. * @warning This will only return results if a) it exists, b) you have access to it.
  311. * see {@link _elgg_get_access_where_sql()}.
  312. *
  313. * @param int $guid The GUID of the object to extract
  314. *
  315. * @return \stdClass|false
  316. * @see entity_row_to_elggstar()
  317. * @access private
  318. */
  319. function get_entity_as_row($guid) {
  320. return _elgg_services()->entityTable->getRow($guid);
  321. }
  322. /**
  323. * Create an Elgg* object from a given entity row.
  324. *
  325. * Handles loading all tables into the correct class.
  326. *
  327. * @param \stdClass $row The row of the entry in the entities table.
  328. *
  329. * @return \ElggEntity|false
  330. * @see get_entity_as_row()
  331. * @see add_subtype()
  332. * @see get_entity()
  333. * @access private
  334. *
  335. * @throws ClassException|InstallationException
  336. */
  337. function entity_row_to_elggstar($row) {
  338. return _elgg_services()->entityTable->rowToElggStar($row);
  339. }
  340. /**
  341. * Loads and returns an entity object from a guid.
  342. *
  343. * @param int $guid The GUID of the entity
  344. *
  345. * @return \ElggEntity The correct Elgg or custom object based upon entity type and subtype
  346. */
  347. function get_entity($guid) {
  348. return _elgg_services()->entityTable->get($guid);
  349. }
  350. /**
  351. * Does an entity exist?
  352. *
  353. * This function checks for the existence of an entity independent of access
  354. * permissions. It is useful for situations when a user cannot access an entity
  355. * and it must be determined whether entity has been deleted or the access level
  356. * has changed.
  357. *
  358. * @param int $guid The GUID of the entity
  359. *
  360. * @return bool
  361. * @since 1.8.0
  362. */
  363. function elgg_entity_exists($guid) {
  364. return _elgg_services()->entityTable->exists($guid);
  365. }
  366. /**
  367. * Enable an entity.
  368. *
  369. * @param int $guid GUID of entity to enable
  370. * @param bool $recursive Recursively enable all entities disabled with the entity?
  371. *
  372. * @return bool
  373. * @since 1.9.0
  374. */
  375. function elgg_enable_entity($guid, $recursive = true) {
  376. return _elgg_services()->entityTable->enable($guid, $recursive);
  377. }
  378. /**
  379. * Returns an array of entities with optional filtering.
  380. *
  381. * Entities are the basic unit of storage in Elgg. This function
  382. * provides the simplest way to get an array of entities. There
  383. * are many options available that can be passed to filter
  384. * what sorts of entities are returned.
  385. *
  386. * @tip To output formatted strings of entities, use {@link elgg_list_entities()} and
  387. * its cousins.
  388. *
  389. * @tip Plural arguments can be written as singular if only specifying a
  390. * single element. ('type' => 'object' vs 'types' => array('object')).
  391. *
  392. * @param array $options Array in format:
  393. *
  394. * types => null|STR entity type (type IN ('type1', 'type2')
  395. * Joined with subtypes by AND. See below)
  396. *
  397. * subtypes => null|STR entity subtype (SQL: subtype IN ('subtype1', 'subtype2))
  398. * Use ELGG_ENTITIES_NO_VALUE to match the default subtype.
  399. * Use ELGG_ENTITIES_ANY_VALUE to match any subtype.
  400. *
  401. * type_subtype_pairs => null|ARR (array('type' => 'subtype'))
  402. * array(
  403. * 'object' => array('blog', 'file'), // All objects with subtype of 'blog' or 'file'
  404. * 'user' => ELGG_ENTITY_ANY_VALUE, // All users irrespective of subtype
  405. * );
  406. *
  407. * guids => null|ARR Array of entity guids
  408. *
  409. * owner_guids => null|ARR Array of owner guids
  410. *
  411. * container_guids => null|ARR Array of container_guids
  412. *
  413. * site_guids => null (current_site)|ARR Array of site_guid
  414. *
  415. * order_by => null (time_created desc)|STR SQL order by clause
  416. *
  417. * reverse_order_by => BOOL Reverse the default order by clause
  418. *
  419. * limit => null (from settings)|INT SQL limit clause (0 means no limit)
  420. *
  421. * offset => null (0)|INT SQL offset clause
  422. *
  423. * created_time_lower => null|INT Created time lower boundary in epoch time
  424. *
  425. * created_time_upper => null|INT Created time upper boundary in epoch time
  426. *
  427. * modified_time_lower => null|INT Modified time lower boundary in epoch time
  428. *
  429. * modified_time_upper => null|INT Modified time upper boundary in epoch time
  430. *
  431. * count => true|false return a count instead of entities
  432. *
  433. * wheres => array() Additional where clauses to AND together
  434. *
  435. * joins => array() Additional joins
  436. *
  437. * preload_owners => bool (false) If set to true, this function will preload
  438. * all the owners of the returned entities resulting in better
  439. * performance when displaying entities owned by several users
  440. *
  441. * callback => string A callback function to pass each row through
  442. *
  443. * distinct => bool (true) If set to false, Elgg will drop the DISTINCT clause from
  444. * the MySQL query, which will improve performance in some situations.
  445. * Avoid setting this option without a full understanding of the underlying
  446. * SQL query Elgg creates.
  447. *
  448. * @return mixed If count, int. If not count, array. false on errors.
  449. * @since 1.7.0
  450. * @see elgg_get_entities_from_metadata()
  451. * @see elgg_get_entities_from_relationship()
  452. * @see elgg_get_entities_from_access_id()
  453. * @see elgg_get_entities_from_annotations()
  454. * @see elgg_list_entities()
  455. */
  456. function elgg_get_entities(array $options = array()) {
  457. return _elgg_services()->entityTable->getEntities($options);
  458. }
  459. /**
  460. * Return entities from an SQL query generated by elgg_get_entities.
  461. *
  462. * @param string $sql
  463. * @param \ElggBatch $batch
  464. * @return \ElggEntity[]
  465. *
  466. * @access private
  467. * @throws LogicException
  468. */
  469. function _elgg_fetch_entities_from_sql($sql, \ElggBatch $batch = null) {
  470. return _elgg_services()->entityTable->fetchFromSql($sql, $batch);
  471. }
  472. /**
  473. * Returns SQL where clause for type and subtype on main entity table
  474. *
  475. * @param string $table Entity table prefix as defined in SELECT...FROM entities $table
  476. * @param null|array $types Array of types or null if none.
  477. * @param null|array $subtypes Array of subtypes or null if none
  478. * @param null|array $pairs Array of pairs of types and subtypes
  479. *
  480. * @return false|string
  481. * @since 1.7.0
  482. * @access private
  483. */
  484. function _elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pairs) {
  485. return _elgg_services()->entityTable->getEntityTypeSubtypeWhereSql($table, $types, $subtypes, $pairs);
  486. }
  487. /**
  488. * Returns SQL where clause for owner and containers.
  489. *
  490. * @param string $column Column name the guids should be checked against. Usually
  491. * best to provide in table.column format.
  492. * @param null|array $guids Array of GUIDs.
  493. *
  494. * @return false|string
  495. * @since 1.8.0
  496. * @access private
  497. */
  498. function _elgg_get_guid_based_where_sql($column, $guids) {
  499. return _elgg_services()->entityTable->getGuidBasedWhereSql($column, $guids);
  500. }
  501. /**
  502. * Returns SQL where clause for entity time limits.
  503. *
  504. * @param string $table Entity table prefix as defined in
  505. * SELECT...FROM entities $table
  506. * @param null|int $time_created_upper Time created upper limit
  507. * @param null|int $time_created_lower Time created lower limit
  508. * @param null|int $time_updated_upper Time updated upper limit
  509. * @param null|int $time_updated_lower Time updated lower limit
  510. *
  511. * @return false|string false on fail, string on success.
  512. * @since 1.7.0
  513. * @access private
  514. */
  515. function _elgg_get_entity_time_where_sql($table, $time_created_upper = null,
  516. $time_created_lower = null, $time_updated_upper = null, $time_updated_lower = null) {
  517. return _elgg_services()->entityTable->getEntityTimeWhereSql($table,
  518. $time_created_upper, $time_created_lower, $time_updated_upper, $time_updated_lower);
  519. }
  520. /**
  521. * Returns a string of rendered entities.
  522. *
  523. * Displays list of entities with formatting specified by the entity view.
  524. *
  525. * @tip Pagination is handled automatically.
  526. *
  527. * @note Internal: This also provides the views for elgg_view_annotation().
  528. *
  529. * @note Internal: If the initial COUNT query returns 0, the $getter will not be called again.
  530. *
  531. * @param array $options Any options from $getter options plus:
  532. * item_view => STR Optional. Alternative view used to render list items
  533. * full_view => BOOL Display full view of entities (default: false)
  534. * list_type => STR 'list' or 'gallery'
  535. * list_type_toggle => BOOL Display gallery / list switch
  536. * pagination => BOOL Display pagination links
  537. * no_results => STR|Closure Message to display when there are no entities
  538. *
  539. * @param callback $getter The entity getter function to use to fetch the entities.
  540. * @param callback $viewer The function to use to view the entity list.
  541. *
  542. * @return string
  543. * @since 1.7
  544. * @see elgg_get_entities()
  545. * @see elgg_view_entity_list()
  546. */
  547. function elgg_list_entities(array $options = array(), $getter = 'elgg_get_entities',
  548. $viewer = 'elgg_view_entity_list') {
  549. global $autofeed;
  550. $autofeed = true;
  551. $offset_key = isset($options['offset_key']) ? $options['offset_key'] : 'offset';
  552. $defaults = array(
  553. 'offset' => (int) max(get_input($offset_key, 0), 0),
  554. 'limit' => (int) max(get_input('limit', elgg_get_config('default_limit')), 0),
  555. 'full_view' => false,
  556. 'list_type_toggle' => false,
  557. 'pagination' => true,
  558. 'no_results' => '',
  559. );
  560. $options = array_merge($defaults, $options);
  561. // backward compatibility
  562. if (isset($options['view_type_toggle'])) {
  563. elgg_deprecated_notice("Option 'view_type_toggle' deprecated by 'list_type_toggle' in elgg_list* functions", 1.9);
  564. $options['list_type_toggle'] = $options['view_type_toggle'];
  565. }
  566. $options['count'] = true;
  567. $count = call_user_func($getter, $options);
  568. if ($count > 0) {
  569. $options['count'] = false;
  570. $entities = call_user_func($getter, $options);
  571. } else {
  572. $entities = array();
  573. }
  574. $options['count'] = $count;
  575. return call_user_func($viewer, $entities, $options);
  576. }
  577. /**
  578. * Gets entities based upon attributes in secondary tables.
  579. * Also accepts all options available to elgg_get_entities(),
  580. * elgg_get_entities_from_metadata(), and elgg_get_entities_from_relationship().
  581. *
  582. * @warning requires that the entity type be specified and there can only be one
  583. * type.
  584. *
  585. * @see elgg_get_entities
  586. * @see elgg_get_entities_from_metadata
  587. * @see elgg_get_entities_from_relationship
  588. *
  589. * @param array $options Array in format:
  590. *
  591. * attribute_name_value_pairs => ARR (
  592. * 'name' => 'name',
  593. * 'value' => 'value',
  594. * 'operand' => '=', (optional)
  595. * 'case_sensitive' => false (optional)
  596. * )
  597. * If multiple values are sent via
  598. * an array ('value' => array('value1', 'value2')
  599. * the pair's operand will be forced to "IN".
  600. *
  601. * attribute_name_value_pairs_operator => null|STR The operator to use for combining
  602. * (name = value) OPERATOR (name = value); default is AND
  603. *
  604. * @return \ElggEntity[]|mixed If count, int. If not count, array. false on errors.
  605. * @since 1.9.0
  606. * @throws InvalidArgumentException
  607. * @todo Does not support ordering by attributes or using an attribute pair shortcut like this ('title' => 'foo')
  608. */
  609. function elgg_get_entities_from_attributes(array $options = array()) {
  610. return _elgg_services()->entityTable->getEntitiesFromAttributes($options);
  611. }
  612. /**
  613. * Get the join and where clauses for working with entity attributes
  614. *
  615. * @return false|array False on fail, array('joins', 'wheres')
  616. * @since 1.9.0
  617. * @access private
  618. * @throws InvalidArgumentException
  619. */
  620. function _elgg_get_entity_attribute_where_sql(array $options = array()) {
  621. return _elgg_services()->entityTable->getEntityAttributeWhereSql($options);
  622. }
  623. /**
  624. * Returns a list of months in which entities were updated or created.
  625. *
  626. * @tip Use this to generate a list of archives by month for when entities were added or updated.
  627. *
  628. * @todo document how to pass in array for $subtype
  629. *
  630. * @warning Months are returned in the form YYYYMM.
  631. *
  632. * @param string $type The type of entity
  633. * @param string $subtype The subtype of entity
  634. * @param int $container_guid The container GUID that the entities belong to
  635. * @param int $site_guid The site GUID
  636. * @param string $order_by Order_by SQL order by clause
  637. *
  638. * @return array|false Either an array months as YYYYMM, or false on failure
  639. */
  640. function get_entity_dates($type = '', $subtype = '', $container_guid = 0, $site_guid = 0,
  641. $order_by = 'time_created') {
  642. return _elgg_services()->entityTable->getDates(
  643. $type, $subtype, $container_guid, $site_guid, $order_by);
  644. }
  645. /**
  646. * Registers an entity type and subtype as a public-facing entity that should
  647. * be shown in search and by {@link elgg_list_registered_entities()}.
  648. *
  649. * @warning Entities that aren't registered here will not show up in search.
  650. *
  651. * @tip Add a language string item:type:subtype to make sure the items are display properly.
  652. *
  653. * @param string $type The type of entity (object, site, user, group)
  654. * @param string $subtype The subtype to register (may be blank)
  655. *
  656. * @return bool Depending on success
  657. * @see get_registered_entity_types()
  658. */
  659. function elgg_register_entity_type($type, $subtype = null) {
  660. global $CONFIG;
  661. $type = strtolower($type);
  662. if (!in_array($type, $CONFIG->entity_types)) {
  663. return false;
  664. }
  665. if (!isset($CONFIG->registered_entities)) {
  666. $CONFIG->registered_entities = array();
  667. }
  668. if (!isset($CONFIG->registered_entities[$type])) {
  669. $CONFIG->registered_entities[$type] = array();
  670. }
  671. if ($subtype) {
  672. $CONFIG->registered_entities[$type][] = $subtype;
  673. }
  674. return true;
  675. }
  676. /**
  677. * Unregisters an entity type and subtype as a public-facing type.
  678. *
  679. * @warning With a blank subtype, it unregisters that entity type including
  680. * all subtypes. This must be called after all subtypes have been registered.
  681. *
  682. * @param string $type The type of entity (object, site, user, group)
  683. * @param string $subtype The subtype to register (may be blank)
  684. *
  685. * @return bool Depending on success
  686. * @see elgg_register_entity_type()
  687. */
  688. function elgg_unregister_entity_type($type, $subtype = null) {
  689. global $CONFIG;
  690. $type = strtolower($type);
  691. if (!in_array($type, $CONFIG->entity_types)) {
  692. return false;
  693. }
  694. if (!isset($CONFIG->registered_entities)) {
  695. return false;
  696. }
  697. if (!isset($CONFIG->registered_entities[$type])) {
  698. return false;
  699. }
  700. if ($subtype) {
  701. if (in_array($subtype, $CONFIG->registered_entities[$type])) {
  702. $key = array_search($subtype, $CONFIG->registered_entities[$type]);
  703. unset($CONFIG->registered_entities[$type][$key]);
  704. } else {
  705. return false;
  706. }
  707. } else {
  708. unset($CONFIG->registered_entities[$type]);
  709. }
  710. return true;
  711. }
  712. /**
  713. * Returns registered entity types and subtypes
  714. *
  715. * @param string $type The type of entity (object, site, user, group) or blank for all
  716. *
  717. * @return array|false Depending on whether entities have been registered
  718. * @see elgg_register_entity_type()
  719. */
  720. function get_registered_entity_types($type = null) {
  721. global $CONFIG;
  722. if (!isset($CONFIG->registered_entities)) {
  723. return false;
  724. }
  725. if ($type) {
  726. $type = strtolower($type);
  727. }
  728. if (!empty($type) && empty($CONFIG->registered_entities[$type])) {
  729. return false;
  730. }
  731. if (empty($type)) {
  732. return $CONFIG->registered_entities;
  733. }
  734. return $CONFIG->registered_entities[$type];
  735. }
  736. /**
  737. * Returns if the entity type and subtype have been registered with {@link elgg_register_entity_type()}.
  738. *
  739. * @param string $type The type of entity (object, site, user, group)
  740. * @param string $subtype The subtype (may be blank)
  741. *
  742. * @return bool Depending on whether or not the type has been registered
  743. */
  744. function is_registered_entity_type($type, $subtype = null) {
  745. global $CONFIG;
  746. if (!isset($CONFIG->registered_entities)) {
  747. return false;
  748. }
  749. $type = strtolower($type);
  750. // @todo registering a subtype implicitly registers the type.
  751. // see #2684
  752. if (!isset($CONFIG->registered_entities[$type])) {
  753. return false;
  754. }
  755. if ($subtype && !in_array($subtype, $CONFIG->registered_entities[$type])) {
  756. return false;
  757. }
  758. return true;
  759. }
  760. /**
  761. * Returns a viewable list of entities based on the registered types.
  762. *
  763. * @see elgg_view_entity_list
  764. *
  765. * @param array $options Any elgg_get_entity() options plus:
  766. *
  767. * full_view => BOOL Display full view entities
  768. *
  769. * list_type_toggle => BOOL Display gallery / list switch
  770. *
  771. * allowed_types => true|ARRAY True to show all types or an array of valid types.
  772. *
  773. * pagination => BOOL Display pagination links
  774. *
  775. * @return string A viewable list of entities
  776. * @since 1.7.0
  777. */
  778. function elgg_list_registered_entities(array $options = array()) {
  779. global $autofeed;
  780. $autofeed = true;
  781. $defaults = array(
  782. 'full_view' => false,
  783. 'allowed_types' => true,
  784. 'list_type_toggle' => false,
  785. 'pagination' => true,
  786. 'offset' => 0,
  787. 'types' => array(),
  788. 'type_subtype_pairs' => array(),
  789. );
  790. $options = array_merge($defaults, $options);
  791. // backward compatibility
  792. if (isset($options['view_type_toggle'])) {
  793. elgg_deprecated_notice("Option 'view_type_toggle' deprecated by 'list_type_toggle' in elgg_list* functions", 1.9);
  794. $options['list_type_toggle'] = $options['view_type_toggle'];
  795. }
  796. $types = get_registered_entity_types();
  797. foreach ($types as $type => $subtype_array) {
  798. if (in_array($type, $options['allowed_types']) || $options['allowed_types'] === true) {
  799. // you must explicitly register types to show up in here and in search for objects
  800. if ($type == 'object') {
  801. if (is_array($subtype_array) && count($subtype_array)) {
  802. $options['type_subtype_pairs'][$type] = $subtype_array;
  803. }
  804. } else {
  805. if (is_array($subtype_array) && count($subtype_array)) {
  806. $options['type_subtype_pairs'][$type] = $subtype_array;
  807. } else {
  808. $options['type_subtype_pairs'][$type] = ELGG_ENTITIES_ANY_VALUE;
  809. }
  810. }
  811. }
  812. }
  813. if (!empty($options['type_subtype_pairs'])) {
  814. $count = elgg_get_entities(array_merge(array('count' => true), $options));
  815. if ($count > 0) {
  816. $entities = elgg_get_entities($options);
  817. } else {
  818. $entities = array();
  819. }
  820. } else {
  821. $count = 0;
  822. $entities = array();
  823. }
  824. $options['count'] = $count;
  825. return elgg_view_entity_list($entities, $options);
  826. }
  827. /**
  828. * Checks if $entity is an \ElggEntity and optionally for type and subtype.
  829. *
  830. * @tip Use this function in actions and views to check that you are dealing
  831. * with the correct type of entity.
  832. *
  833. * @param mixed $entity Entity
  834. * @param string $type Entity type
  835. * @param string $subtype Entity subtype
  836. * @param string $class Class name
  837. *
  838. * @return bool
  839. * @since 1.8.0
  840. */
  841. function elgg_instanceof($entity, $type = null, $subtype = null, $class = null) {
  842. $return = ($entity instanceof \ElggEntity);
  843. if ($type) {
  844. /* @var \ElggEntity $entity */
  845. $return = $return && ($entity->getType() == $type);
  846. }
  847. if ($subtype) {
  848. $return = $return && ($entity->getSubtype() == $subtype);
  849. }
  850. if ($class) {
  851. $return = $return && ($entity instanceof $class);
  852. }
  853. return $return;
  854. }
  855. /**
  856. * Update the last_action column in the entities table for $guid.
  857. *
  858. * @warning This is different to time_updated. Time_updated is automatically set,
  859. * while last_action is only set when explicitly called.
  860. *
  861. * @param int $guid Entity annotation|relationship action carried out on
  862. * @param int $posted Timestamp of last action
  863. *
  864. * @return bool
  865. * @access private
  866. */
  867. function update_entity_last_action($guid, $posted = null) {
  868. return _elgg_services()->entityTable->updateLastAction($guid, $posted);
  869. }
  870. /**
  871. * Runs unit tests for the entity objects.
  872. *
  873. * @param string $hook unit_test
  874. * @param string $type system
  875. * @param array $value Array of tests
  876. *
  877. * @return array
  878. * @access private
  879. */
  880. function _elgg_entities_test($hook, $type, $value) {
  881. global $CONFIG;
  882. $value[] = $CONFIG->path . 'engine/tests/ElggEntityTest.php';
  883. $value[] = $CONFIG->path . 'engine/tests/ElggCoreAttributeLoaderTest.php';
  884. $value[] = $CONFIG->path . 'engine/tests/ElggCoreGetEntitiesTest.php';
  885. $value[] = $CONFIG->path . 'engine/tests/ElggCoreGetEntitiesFromAnnotationsTest.php';
  886. $value[] = $CONFIG->path . 'engine/tests/ElggCoreGetEntitiesFromMetadataTest.php';
  887. $value[] = $CONFIG->path . 'engine/tests/ElggCoreGetEntitiesFromPrivateSettingsTest.php';
  888. $value[] = $CONFIG->path . 'engine/tests/ElggCoreGetEntitiesFromRelationshipTest.php';
  889. $value[] = $CONFIG->path . 'engine/tests/ElggCoreGetEntitiesFromAttributesTest.php';
  890. $value[] = $CONFIG->path . 'engine/tests/ElggEntityPreloaderIntegrationTest.php';
  891. return $value;
  892. }
  893. /**
  894. * Entities init function; establishes the default entity page handler
  895. *
  896. * @return void
  897. * @elgg_event_handler init system
  898. * @access private
  899. */
  900. function _elgg_entities_init() {
  901. elgg_register_plugin_hook_handler('unit_test', 'system', '_elgg_entities_test');
  902. }
  903. return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
  904. $events->registerHandler('init', 'system', '_elgg_entities_init');
  905. };