ElggEntity.php 69 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560
  1. <?php
  2. /**
  3. * The parent class for all Elgg Entities.
  4. *
  5. * An \ElggEntity is one of the basic data models in Elgg. It is the primary
  6. * means of storing and retrieving data from the database. An \ElggEntity
  7. * represents one row of the entities table.
  8. *
  9. * The \ElggEntity class handles CRUD operations for the entities table.
  10. * \ElggEntity should always be extended by another class to handle CRUD
  11. * operations on the type-specific table.
  12. *
  13. * \ElggEntity uses magic methods for get and set, so any property that isn't
  14. * declared will be assumed to be metadata and written to the database
  15. * as metadata on the object. All children classes must declare which
  16. * properties are columns of the type table or they will be assumed
  17. * to be metadata. See \ElggObject::initializeAttributes() for examples.
  18. *
  19. * Core supports 4 types of entities: \ElggObject, \ElggUser, \ElggGroup, and
  20. * \ElggSite.
  21. *
  22. * @tip Plugin authors will want to extend the \ElggObject class, not this class.
  23. *
  24. * @package Elgg.Core
  25. * @subpackage DataModel.Entities
  26. *
  27. * @property string $type object, user, group, or site (read-only after save)
  28. * @property-write string $subtype Further clarifies the nature of the entity (this should not be read)
  29. * @property int $guid The unique identifier for this entity (read only)
  30. * @property int $owner_guid The GUID of the owner of this entity (usually the creator)
  31. * @property int $container_guid The GUID of the entity containing this entity
  32. * @property int $site_guid The GUID of the website this entity is associated with
  33. * @property int $access_id Specifies the visibility level of this entity
  34. * @property int $time_created A UNIX timestamp of when the entity was created
  35. * @property int $time_updated A UNIX timestamp of when the entity was last updated (automatically updated on save)
  36. * @property string $enabled Is this entity enabled ('yes' or 'no')
  37. *
  38. * Metadata (the above are attributes)
  39. * @property string $location A location of the entity
  40. */
  41. abstract class ElggEntity extends \ElggData implements
  42. Notable, // Calendar interface (deprecated 1.9)
  43. Locatable, // Geocoding interface
  44. Importable // Allow import of data (deprecated 1.9)
  45. {
  46. /**
  47. * If set, overrides the value of getURL()
  48. */
  49. protected $url_override;
  50. /**
  51. * Icon override, overrides the value of getIcon().
  52. */
  53. protected $icon_override;
  54. /**
  55. * Holds metadata until entity is saved. Once the entity is saved,
  56. * metadata are written immediately to the database.
  57. */
  58. protected $temp_metadata = array();
  59. /**
  60. * Holds annotations until entity is saved. Once the entity is saved,
  61. * annotations are written immediately to the database.
  62. */
  63. protected $temp_annotations = array();
  64. /**
  65. * Holds private settings until entity is saved. Once the entity is saved,
  66. * private settings are written immediately to the database.
  67. */
  68. protected $temp_private_settings = array();
  69. /**
  70. * Volatile data structure for this object, allows for storage of data
  71. * in-memory that isn't sync'd back to the metadata table.
  72. */
  73. protected $volatile = array();
  74. /**
  75. * Tells how many tables are going to need to be searched in order to fully populate this object
  76. *
  77. * @var int
  78. */
  79. protected $tables_split;
  80. /**
  81. * Tells how many tables describing object have been loaded thus far
  82. *
  83. * @var int
  84. */
  85. protected $tables_loaded;
  86. /**
  87. * Initialize the attributes array.
  88. *
  89. * This is vital to distinguish between metadata and base parameters.
  90. *
  91. * @return void
  92. */
  93. protected function initializeAttributes() {
  94. parent::initializeAttributes();
  95. $this->attributes['guid'] = null;
  96. $this->attributes['type'] = null;
  97. $this->attributes['subtype'] = null;
  98. $this->attributes['owner_guid'] = _elgg_services()->session->getLoggedInUserGuid();
  99. $this->attributes['container_guid'] = _elgg_services()->session->getLoggedInUserGuid();
  100. $this->attributes['site_guid'] = null;
  101. $this->attributes['access_id'] = ACCESS_PRIVATE;
  102. $this->attributes['time_updated'] = null;
  103. $this->attributes['last_action'] = null;
  104. $this->attributes['enabled'] = "yes";
  105. // There now follows a bit of a hack
  106. /* Problem: To speed things up, some objects are split over several tables,
  107. * this means that it requires n number of database reads to fully populate
  108. * an entity. This causes problems for caching and create events
  109. * since it is not possible to tell whether a subclassed entity is complete.
  110. *
  111. * Solution: We have two counters, one 'tables_split' which tells whatever is
  112. * interested how many tables are going to need to be searched in order to fully
  113. * populate this object, and 'tables_loaded' which is how many have been
  114. * loaded thus far.
  115. *
  116. * If the two are the same then this object is complete.
  117. *
  118. * Use: isFullyLoaded() to check
  119. */
  120. $this->tables_split = 1;
  121. $this->tables_loaded = 0;
  122. }
  123. /**
  124. * Clone an entity
  125. *
  126. * Resets the guid so that the entity can be saved as a distinct entity from
  127. * the original. Creation time will be set when this new entity is saved.
  128. * The owner and container guids come from the original entity. The clone
  129. * method copies metadata but does not copy annotations or private settings.
  130. *
  131. * @note metadata will have its owner and access id set when the entity is saved
  132. * and it will be the same as that of the entity.
  133. *
  134. * @return void
  135. */
  136. public function __clone() {
  137. $orig_entity = get_entity($this->guid);
  138. if (!$orig_entity) {
  139. _elgg_services()->logger->error("Failed to clone entity with GUID $this->guid");
  140. return;
  141. }
  142. $metadata_array = elgg_get_metadata(array(
  143. 'guid' => $this->guid,
  144. 'limit' => 0
  145. ));
  146. $this->attributes['guid'] = "";
  147. $this->attributes['subtype'] = $orig_entity->getSubtype();
  148. // copy metadata over to new entity - slightly convoluted due to
  149. // handling of metadata arrays
  150. if (is_array($metadata_array)) {
  151. // create list of metadata names
  152. $metadata_names = array();
  153. foreach ($metadata_array as $metadata) {
  154. $metadata_names[] = $metadata['name'];
  155. }
  156. // arrays are stored with multiple enties per name
  157. $metadata_names = array_unique($metadata_names);
  158. // move the metadata over
  159. foreach ($metadata_names as $name) {
  160. $this->__set($name, $orig_entity->$name);
  161. }
  162. }
  163. }
  164. /**
  165. * Set an attribute or metadata value for this entity
  166. *
  167. * Anything that is not an attribute is saved as metadata.
  168. *
  169. * @warning Metadata set this way will inherit the entity's owner and
  170. * access ID. If you want more control over metadata, use \ElggEntity::setMetadata()
  171. *
  172. * @param string $name Name of the attribute or metadata
  173. * @param mixed $value The value to be set
  174. * @return void
  175. * @see \ElggEntity::setMetadata()
  176. */
  177. public function __set($name, $value) {
  178. if ($this->$name === $value) {
  179. // quick return if value is not changing
  180. return;
  181. }
  182. if (array_key_exists($name, $this->attributes)) {
  183. // Certain properties should not be manually changed!
  184. switch ($name) {
  185. case 'guid':
  186. case 'time_updated':
  187. case 'last_action':
  188. return;
  189. break;
  190. case 'access_id':
  191. case 'owner_guid':
  192. case 'container_guid':
  193. if ($value !== null) {
  194. $this->attributes[$name] = (int)$value;
  195. } else {
  196. $this->attributes[$name] = null;
  197. }
  198. break;
  199. default:
  200. $this->attributes[$name] = $value;
  201. break;
  202. }
  203. } else {
  204. $this->setMetadata($name, $value);
  205. }
  206. }
  207. /**
  208. * Sets the value of an attribute or metadata
  209. *
  210. * @param string $name Name
  211. * @param mixed $value Value
  212. *
  213. * @return bool
  214. * @deprecated 1.9
  215. */
  216. public function set($name, $value) {
  217. elgg_deprecated_notice("Use -> instead of set()", 1.9);
  218. $this->__set($name, $value);
  219. return true;
  220. }
  221. /**
  222. * Get an attribute or metadata value
  223. *
  224. * If the name matches an attribute, the attribute is returned. If metadata
  225. * does not exist with that name, a null is returned.
  226. *
  227. * This only returns an array if there are multiple values for a particular
  228. * $name key.
  229. *
  230. * @param string $name Name of the attribute or metadata
  231. * @return mixed
  232. */
  233. public function __get($name) {
  234. if (array_key_exists($name, $this->attributes)) {
  235. if ($name === 'subtype' && $this->attributes['guid']) {
  236. // note: only show deprecation notice if user reads ->subtype after save/load
  237. elgg_deprecated_notice("Use getSubtype()", 1.9);
  238. }
  239. return $this->attributes[$name];
  240. }
  241. return $this->getMetadata($name);
  242. }
  243. /**
  244. * Return the value of an attribute or metadata
  245. *
  246. * @param string $name Name
  247. * @return mixed Returns the value of a given value, or null.
  248. * @deprecated 1.9
  249. */
  250. public function get($name) {
  251. elgg_deprecated_notice("Use -> instead of get()", 1.9);
  252. return $this->__get($name);
  253. }
  254. /**
  255. * Get the entity's display name
  256. *
  257. * @return string The title or name of this entity.
  258. */
  259. abstract public function getDisplayName();
  260. /**
  261. * Sets the title or name of this entity.
  262. *
  263. * @param string $displayName The title or name of this entity.
  264. * @return void
  265. */
  266. abstract public function setDisplayName($displayName);
  267. /**
  268. * Return the value of a piece of metadata.
  269. *
  270. * @param string $name Name
  271. *
  272. * @return mixed The value, or null if not found.
  273. */
  274. public function getMetadata($name) {
  275. $guid = $this->getGUID();
  276. if (!$guid) {
  277. if (isset($this->temp_metadata[$name])) {
  278. // md is returned as an array only if more than 1 entry
  279. if (count($this->temp_metadata[$name]) == 1) {
  280. return $this->temp_metadata[$name][0];
  281. } else {
  282. return $this->temp_metadata[$name];
  283. }
  284. } else {
  285. return null;
  286. }
  287. }
  288. // upon first cache miss, just load/cache all the metadata and retry.
  289. // if this works, the rest of this function may not be needed!
  290. $cache = _elgg_services()->metadataCache;
  291. if ($cache->isLoaded($guid)) {
  292. return $cache->getSingle($guid, $name);
  293. } else {
  294. $cache->populateFromEntities(array($guid));
  295. // in case ignore_access was on, we have to check again...
  296. if ($cache->isLoaded($guid)) {
  297. return $cache->getSingle($guid, $name);
  298. }
  299. }
  300. $md = elgg_get_metadata(array(
  301. 'guid' => $guid,
  302. 'metadata_name' => $name,
  303. 'limit' => 0,
  304. 'distinct' => false,
  305. ));
  306. $value = null;
  307. if ($md && !is_array($md)) {
  308. $value = $md->value;
  309. } elseif (count($md) == 1) {
  310. $value = $md[0]->value;
  311. } else if ($md && is_array($md)) {
  312. $value = metadata_array_to_values($md);
  313. }
  314. return $value;
  315. }
  316. /**
  317. * Unset a property from metadata or attribute.
  318. *
  319. * @warning If you use this to unset an attribute, you must save the object!
  320. *
  321. * @param string $name The name of the attribute or metadata.
  322. *
  323. * @return void
  324. * @todo some attributes should be set to null or other default values
  325. */
  326. public function __unset($name) {
  327. if (array_key_exists($name, $this->attributes)) {
  328. $this->attributes[$name] = "";
  329. } else {
  330. $this->deleteMetadata($name);
  331. }
  332. }
  333. /**
  334. * Set metadata on this entity.
  335. *
  336. * Plugin developers usually want to use the magic set method ($entity->name = 'value').
  337. * Use this method if you want to explicitly set the owner or access of the metadata.
  338. * You cannot set the owner/access before the entity has been saved.
  339. *
  340. * @param string $name Name of the metadata
  341. * @param mixed $value Value of the metadata (doesn't support assoc arrays)
  342. * @param string $value_type 'text', 'integer', or '' for automatic detection
  343. * @param bool $multiple Allow multiple values for a single name.
  344. * Does not support associative arrays.
  345. * @param int $owner_guid GUID of entity that owns the metadata.
  346. * Default is owner of entity.
  347. * @param int $access_id Who can read the metadata relative to the owner.
  348. * Default is the access level of the entity.
  349. *
  350. * @return bool
  351. * @throws InvalidArgumentException
  352. */
  353. public function setMetadata($name, $value, $value_type = '', $multiple = false, $owner_guid = 0, $access_id = null) {
  354. // normalize value to an array that we will loop over
  355. // remove indexes if value already an array.
  356. if (is_array($value)) {
  357. $value = array_values($value);
  358. } else {
  359. $value = array($value);
  360. }
  361. // saved entity. persist md to db.
  362. if ($this->guid) {
  363. // if overwriting, delete first.
  364. if (!$multiple) {
  365. $options = array(
  366. 'guid' => $this->getGUID(),
  367. 'metadata_name' => $name,
  368. 'limit' => 0
  369. );
  370. // @todo in 1.9 make this return false if can't add metadata
  371. // https://github.com/elgg/elgg/issues/4520
  372. //
  373. // need to remove access restrictions right now to delete
  374. // because this is the expected behavior
  375. $ia = elgg_set_ignore_access(true);
  376. if (false === elgg_delete_metadata($options)) {
  377. return false;
  378. }
  379. elgg_set_ignore_access($ia);
  380. }
  381. $owner_guid = (int)$owner_guid;
  382. $access_id = ($access_id === null) ? $this->getAccessId() : (int)$access_id;
  383. $owner_guid = $owner_guid ? $owner_guid : $this->getOwnerGUID();
  384. // add new md
  385. $result = true;
  386. foreach ($value as $value_tmp) {
  387. // at this point $value is appended because it was cleared above if needed.
  388. $md_id = create_metadata($this->getGUID(), $name, $value_tmp, $value_type,
  389. $owner_guid, $access_id, true);
  390. if (!$md_id) {
  391. return false;
  392. }
  393. }
  394. return $result;
  395. } else {
  396. // unsaved entity. store in temp array
  397. // returning single entries instead of an array of 1 element is decided in
  398. // getMetaData(), just like pulling from the db.
  399. if ($owner_guid != 0 || $access_id !== null) {
  400. $msg = "owner guid and access id cannot be used in \ElggEntity::setMetadata() until entity is saved.";
  401. throw new \InvalidArgumentException($msg);
  402. }
  403. // if overwrite, delete first
  404. if (!$multiple || !isset($this->temp_metadata[$name])) {
  405. $this->temp_metadata[$name] = array();
  406. }
  407. // add new md
  408. $this->temp_metadata[$name] = array_merge($this->temp_metadata[$name], $value);
  409. return true;
  410. }
  411. }
  412. /**
  413. * Deletes all metadata on this object (metadata.entity_guid = $this->guid).
  414. * If you pass a name, only metadata matching that name will be deleted.
  415. *
  416. * @warning Calling this with no $name will clear all metadata on the entity.
  417. *
  418. * @param null|string $name The name of the metadata to remove.
  419. * @return bool
  420. * @since 1.8
  421. */
  422. public function deleteMetadata($name = null) {
  423. if (!$this->guid) {
  424. return false;
  425. }
  426. $options = array(
  427. 'guid' => $this->guid,
  428. 'limit' => 0
  429. );
  430. if ($name) {
  431. $options['metadata_name'] = $name;
  432. }
  433. return elgg_delete_metadata($options);
  434. }
  435. /**
  436. * Deletes all metadata owned by this object (metadata.owner_guid = $this->guid).
  437. * If you pass a name, only metadata matching that name will be deleted.
  438. *
  439. * @param null|string $name The name of metadata to delete.
  440. * @return bool
  441. * @since 1.8
  442. */
  443. public function deleteOwnedMetadata($name = null) {
  444. // access is turned off for this because they might
  445. // no longer have access to an entity they created metadata on.
  446. $ia = elgg_set_ignore_access(true);
  447. $options = array(
  448. 'metadata_owner_guid' => $this->guid,
  449. 'limit' => 0
  450. );
  451. if ($name) {
  452. $options['metadata_name'] = $name;
  453. }
  454. $r = elgg_delete_metadata($options);
  455. elgg_set_ignore_access($ia);
  456. return $r;
  457. }
  458. /**
  459. * Remove metadata
  460. *
  461. * @warning Calling this with no or empty arguments will clear all metadata on the entity.
  462. *
  463. * @param string $name The name of the metadata to clear
  464. * @return mixed bool
  465. * @deprecated 1.8 Use deleteMetadata()
  466. */
  467. public function clearMetadata($name = '') {
  468. elgg_deprecated_notice('\ElggEntity->clearMetadata() is deprecated by ->deleteMetadata()', 1.8);
  469. return $this->deleteMetadata($name);
  470. }
  471. /**
  472. * Disables metadata for this entity, optionally based on name.
  473. *
  474. * @param string $name An options name of metadata to disable.
  475. * @return bool
  476. * @since 1.8
  477. */
  478. public function disableMetadata($name = '') {
  479. $options = array(
  480. 'guid' => $this->guid,
  481. 'limit' => 0
  482. );
  483. if ($name) {
  484. $options['metadata_name'] = $name;
  485. }
  486. return elgg_disable_metadata($options);
  487. }
  488. /**
  489. * Enables metadata for this entity, optionally based on name.
  490. *
  491. * @warning Before calling this, you must use {@link access_show_hidden_entities()}
  492. *
  493. * @param string $name An options name of metadata to enable.
  494. * @return bool
  495. * @since 1.8
  496. */
  497. public function enableMetadata($name = '') {
  498. $options = array(
  499. 'guid' => $this->guid,
  500. 'limit' => 0
  501. );
  502. if ($name) {
  503. $options['metadata_name'] = $name;
  504. }
  505. return elgg_enable_metadata($options);
  506. }
  507. /**
  508. * Get a piece of volatile (non-persisted) data on this entity.
  509. *
  510. * @param string $name The name of the volatile data
  511. *
  512. * @return mixed The value or null if not found.
  513. */
  514. public function getVolatileData($name) {
  515. if (!is_array($this->volatile)) {
  516. $this->volatile = array();
  517. }
  518. if (array_key_exists($name, $this->volatile)) {
  519. return $this->volatile[$name];
  520. } else {
  521. return null;
  522. }
  523. }
  524. /**
  525. * Set a piece of volatile (non-persisted) data on this entity
  526. *
  527. * @param string $name Name
  528. * @param mixed $value Value
  529. *
  530. * @return void
  531. */
  532. public function setVolatileData($name, $value) {
  533. if (!is_array($this->volatile)) {
  534. $this->volatile = array();
  535. }
  536. $this->volatile[$name] = $value;
  537. }
  538. /**
  539. * Remove all relationships to and from this entity.
  540. * If you pass a relationship name, only relationships matching that name
  541. * will be deleted.
  542. *
  543. * @warning Calling this with no $relationship will clear all relationships
  544. * for this entity.
  545. *
  546. * @param null|string $relationship The name of the relationship to remove.
  547. * @return bool
  548. * @see \ElggEntity::addRelationship()
  549. * @see \ElggEntity::removeRelationship()
  550. */
  551. public function deleteRelationships($relationship = null) {
  552. $relationship = (string)$relationship;
  553. $result = remove_entity_relationships($this->getGUID(), $relationship);
  554. return $result && remove_entity_relationships($this->getGUID(), $relationship, true);
  555. }
  556. /**
  557. * Remove all relationships to and from this entity.
  558. *
  559. * @return bool
  560. * @see \ElggEntity::addRelationship()
  561. * @see \ElggEntity::removeRelationship()
  562. * @deprecated 1.8 Use \ElggEntity::deleteRelationships()
  563. */
  564. public function clearRelationships() {
  565. elgg_deprecated_notice('\ElggEntity->clearRelationships() is deprecated by ->deleteRelationships()', 1.8);
  566. return $this->deleteRelationships();
  567. }
  568. /**
  569. * Add a relationship between this an another entity.
  570. *
  571. * @tip Read the relationship like "This entity is a $relationship of $guid_two."
  572. *
  573. * @param int $guid_two GUID of the target entity of the relationship.
  574. * @param string $relationship The type of relationship.
  575. *
  576. * @return bool
  577. * @see \ElggEntity::removeRelationship()
  578. * @see \ElggEntity::deleteRelationships()
  579. */
  580. public function addRelationship($guid_two, $relationship) {
  581. return add_entity_relationship($this->getGUID(), $relationship, $guid_two);
  582. }
  583. /**
  584. * Remove a relationship
  585. *
  586. * @param int $guid_two GUID of the target entity of the relationship.
  587. * @param string $relationship The type of relationship.
  588. *
  589. * @return bool
  590. * @see \ElggEntity::addRelationship()
  591. * @see \ElggEntity::deleteRelationships()
  592. */
  593. public function removeRelationship($guid_two, $relationship) {
  594. return remove_entity_relationship($this->getGUID(), $relationship, $guid_two);
  595. }
  596. /**
  597. * Adds a private setting to this entity.
  598. *
  599. * Private settings are similar to metadata but will not
  600. * be searched and there are fewer helper functions for them.
  601. *
  602. * @param string $name Name of private setting
  603. * @param mixed $value Value of private setting
  604. *
  605. * @return bool
  606. */
  607. public function setPrivateSetting($name, $value) {
  608. if ((int) $this->guid > 0) {
  609. return set_private_setting($this->getGUID(), $name, $value);
  610. } else {
  611. $this->temp_private_settings[$name] = $value;
  612. return true;
  613. }
  614. }
  615. /**
  616. * Returns a private setting value
  617. *
  618. * @param string $name Name of the private setting
  619. *
  620. * @return mixed Null if the setting does not exist
  621. */
  622. public function getPrivateSetting($name) {
  623. if ((int) ($this->guid) > 0) {
  624. return get_private_setting($this->getGUID(), $name);
  625. } else {
  626. if (isset($this->temp_private_settings[$name])) {
  627. return $this->temp_private_settings[$name];
  628. }
  629. }
  630. return null;
  631. }
  632. /**
  633. * Removes private setting
  634. *
  635. * @param string $name Name of the private setting
  636. *
  637. * @return bool
  638. */
  639. public function removePrivateSetting($name) {
  640. return remove_private_setting($this->getGUID(), $name);
  641. }
  642. /**
  643. * Deletes all annotations on this object (annotations.entity_guid = $this->guid).
  644. * If you pass a name, only annotations matching that name will be deleted.
  645. *
  646. * @warning Calling this with no or empty arguments will clear all annotations on the entity.
  647. *
  648. * @param null|string $name The annotations name to remove.
  649. * @return bool
  650. * @since 1.8
  651. */
  652. public function deleteAnnotations($name = null) {
  653. $options = array(
  654. 'guid' => $this->guid,
  655. 'limit' => 0
  656. );
  657. if ($name) {
  658. $options['annotation_name'] = $name;
  659. }
  660. return elgg_delete_annotations($options);
  661. }
  662. /**
  663. * Deletes all annotations owned by this object (annotations.owner_guid = $this->guid).
  664. * If you pass a name, only annotations matching that name will be deleted.
  665. *
  666. * @param null|string $name The name of annotations to delete.
  667. * @return bool
  668. * @since 1.8
  669. */
  670. public function deleteOwnedAnnotations($name = null) {
  671. // access is turned off for this because they might
  672. // no longer have access to an entity they created annotations on.
  673. $ia = elgg_set_ignore_access(true);
  674. $options = array(
  675. 'annotation_owner_guid' => $this->guid,
  676. 'limit' => 0
  677. );
  678. if ($name) {
  679. $options['annotation_name'] = $name;
  680. }
  681. $r = elgg_delete_annotations($options);
  682. elgg_set_ignore_access($ia);
  683. return $r;
  684. }
  685. /**
  686. * Disables annotations for this entity, optionally based on name.
  687. *
  688. * @param string $name An options name of annotations to disable.
  689. * @return bool
  690. * @since 1.8
  691. */
  692. public function disableAnnotations($name = '') {
  693. $options = array(
  694. 'guid' => $this->guid,
  695. 'limit' => 0
  696. );
  697. if ($name) {
  698. $options['annotation_name'] = $name;
  699. }
  700. return elgg_disable_annotations($options);
  701. }
  702. /**
  703. * Enables annotations for this entity, optionally based on name.
  704. *
  705. * @warning Before calling this, you must use {@link access_show_hidden_entities()}
  706. *
  707. * @param string $name An options name of annotations to enable.
  708. * @return bool
  709. * @since 1.8
  710. */
  711. public function enableAnnotations($name = '') {
  712. $options = array(
  713. 'guid' => $this->guid,
  714. 'limit' => 0
  715. );
  716. if ($name) {
  717. $options['annotation_name'] = $name;
  718. }
  719. return elgg_enable_annotations($options);
  720. }
  721. /**
  722. * Helper function to return annotation calculation results
  723. *
  724. * @param string $name The annotation name.
  725. * @param string $calculation A valid MySQL function to run its values through
  726. * @return mixed
  727. */
  728. private function getAnnotationCalculation($name, $calculation) {
  729. $options = array(
  730. 'guid' => $this->getGUID(),
  731. 'distinct' => false,
  732. 'annotation_name' => $name,
  733. 'annotation_calculation' => $calculation
  734. );
  735. return elgg_get_annotations($options);
  736. }
  737. /**
  738. * Adds an annotation to an entity.
  739. *
  740. * @warning By default, annotations are private.
  741. *
  742. * @warning Annotating an unsaved entity more than once with the same name
  743. * will only save the last annotation.
  744. *
  745. * @param string $name Annotation name
  746. * @param mixed $value Annotation value
  747. * @param int $access_id Access ID
  748. * @param int $owner_guid GUID of the annotation owner
  749. * @param string $vartype The type of annotation value
  750. *
  751. * @return bool|int Returns int if an annotation is saved
  752. */
  753. public function annotate($name, $value, $access_id = ACCESS_PRIVATE, $owner_guid = 0, $vartype = "") {
  754. if ((int) $this->guid > 0) {
  755. return create_annotation($this->getGUID(), $name, $value, $vartype, $owner_guid, $access_id);
  756. } else {
  757. $this->temp_annotations[$name] = $value;
  758. }
  759. return true;
  760. }
  761. /**
  762. * Gets an array of annotations.
  763. *
  764. * To retrieve annotations on an unsaved entity, pass array('name' => [annotation name])
  765. * as the options array.
  766. *
  767. * @param array $options Array of options for elgg_get_annotations() except guid. This
  768. * may be passed a string annotation name, but this usage is deprecated.
  769. * @param int $limit Limit (deprecated)
  770. * @param int $offset Offset (deprecated)
  771. * @param string $order Order by time: asc or desc (deprecated)
  772. *
  773. * @return array
  774. * @see elgg_get_annotations()
  775. */
  776. public function getAnnotations($options = array(), $limit = 50, $offset = 0, $order = "asc") {
  777. if (!is_array($options)) {
  778. elgg_deprecated_notice("\ElggEntity::getAnnotations() takes an array of options.", 1.9);
  779. }
  780. if ((int) ($this->guid) > 0) {
  781. if (!is_array($options)) {
  782. $options = array(
  783. 'guid' => $this->guid,
  784. 'annotation_name' => $options,
  785. 'limit' => $limit,
  786. 'offset' => $offset,
  787. );
  788. if ($order != 'asc') {
  789. $options['reverse_order_by'] = true;
  790. }
  791. } else {
  792. $options['guid'] = $this->guid;
  793. }
  794. return elgg_get_annotations($options);
  795. } else {
  796. if (!is_array($options)) {
  797. $name = $options;
  798. } else {
  799. $name = elgg_extract('annotation_name', $options, '');
  800. }
  801. if (isset($this->temp_annotations[$name])) {
  802. return array($this->temp_annotations[$name]);
  803. }
  804. }
  805. return array();
  806. }
  807. /**
  808. * Remove an annotation or all annotations for this entity.
  809. *
  810. * @warning Calling this method with no or an empty argument will remove
  811. * all annotations on the entity.
  812. *
  813. * @param string $name Annotation name
  814. * @return bool
  815. * @deprecated 1.8 Use ->deleteAnnotations()
  816. */
  817. public function clearAnnotations($name = "") {
  818. elgg_deprecated_notice('\ElggEntity->clearAnnotations() is deprecated by ->deleteAnnotations()', 1.8);
  819. return $this->deleteAnnotations($name);
  820. }
  821. /**
  822. * Count annotations.
  823. *
  824. * @param string $name The type of annotation.
  825. *
  826. * @return int
  827. */
  828. public function countAnnotations($name = "") {
  829. return $this->getAnnotationCalculation($name, 'count');
  830. }
  831. /**
  832. * Get the average of an integer type annotation.
  833. *
  834. * @param string $name Annotation name
  835. *
  836. * @return int
  837. */
  838. public function getAnnotationsAvg($name) {
  839. return $this->getAnnotationCalculation($name, 'avg');
  840. }
  841. /**
  842. * Get the sum of integer type annotations of a given name.
  843. *
  844. * @param string $name Annotation name
  845. *
  846. * @return int
  847. */
  848. public function getAnnotationsSum($name) {
  849. return $this->getAnnotationCalculation($name, 'sum');
  850. }
  851. /**
  852. * Get the minimum of integer type annotations of given name.
  853. *
  854. * @param string $name Annotation name
  855. *
  856. * @return int
  857. */
  858. public function getAnnotationsMin($name) {
  859. return $this->getAnnotationCalculation($name, 'min');
  860. }
  861. /**
  862. * Get the maximum of integer type annotations of a given name.
  863. *
  864. * @param string $name Annotation name
  865. *
  866. * @return int
  867. */
  868. public function getAnnotationsMax($name) {
  869. return $this->getAnnotationCalculation($name, 'max');
  870. }
  871. /**
  872. * Count the number of comments attached to this entity.
  873. *
  874. * @return int Number of comments
  875. * @since 1.8.0
  876. */
  877. public function countComments() {
  878. $params = array('entity' => $this);
  879. $num = _elgg_services()->hooks->trigger('comments:count', $this->getType(), $params);
  880. if (is_int($num)) {
  881. return $num;
  882. } else {
  883. return elgg_get_entities(array(
  884. 'type' => 'object',
  885. 'subtype' => 'comment',
  886. 'container_guid' => $this->getGUID(),
  887. 'count' => true,
  888. 'distinct' => false,
  889. ));
  890. }
  891. }
  892. /**
  893. * Gets an array of entities with a relationship to this entity.
  894. *
  895. * @param array $options Options array. See elgg_get_entities_from_relationship()
  896. * for a list of options. 'relationship_guid' is set to
  897. * this entity.
  898. * @param bool $inverse Is this an inverse relationship? (deprecated)
  899. * @param int $limit Number of elements to return (deprecated)
  900. * @param int $offset Indexing offset (deprecated)
  901. *
  902. * @return array|false An array of entities or false on failure
  903. * @see elgg_get_entities_from_relationship()
  904. */
  905. public function getEntitiesFromRelationship($options = array(), $inverse = false, $limit = 50, $offset = 0) {
  906. if (is_array($options)) {
  907. $options['relationship_guid'] = $this->getGUID();
  908. return elgg_get_entities_from_relationship($options);
  909. } else {
  910. elgg_deprecated_notice("\ElggEntity::getEntitiesFromRelationship takes an options array", 1.9);
  911. return elgg_get_entities_from_relationship(array(
  912. 'relationship' => $options,
  913. 'relationship_guid' => $this->getGUID(),
  914. 'inverse_relationship' => $inverse,
  915. 'limit' => $limit,
  916. 'offset' => $offset
  917. ));
  918. }
  919. }
  920. /**
  921. * Gets the number of entities from a specific relationship type
  922. *
  923. * @param string $relationship Relationship type (eg "friends")
  924. * @param bool $inverse_relationship Invert relationship
  925. *
  926. * @return int|false The number of entities or false on failure
  927. */
  928. public function countEntitiesFromRelationship($relationship, $inverse_relationship = false) {
  929. return elgg_get_entities_from_relationship(array(
  930. 'relationship' => $relationship,
  931. 'relationship_guid' => $this->getGUID(),
  932. 'inverse_relationship' => $inverse_relationship,
  933. 'count' => true
  934. ));
  935. }
  936. /**
  937. * Can a user edit this entity?
  938. *
  939. * @tip Can be overridden by registering for the permissions_check plugin hook.
  940. *
  941. * @param int $user_guid The user GUID, optionally (default: logged in user)
  942. *
  943. * @return bool Whether this entity is editable by the given user.
  944. * @see elgg_set_ignore_access()
  945. */
  946. public function canEdit($user_guid = 0) {
  947. $user_guid = (int)$user_guid;
  948. $user = get_entity($user_guid);
  949. if (!$user) {
  950. $user = _elgg_services()->session->getLoggedInUser();
  951. }
  952. $return = false;
  953. // Test user if possible - should default to false unless a plugin hook says otherwise
  954. if ($user) {
  955. if ($this->getOwnerGUID() == $user->getGUID()) {
  956. $return = true;
  957. }
  958. if ($this->getContainerGUID() == $user->getGUID()) {
  959. $return = true;
  960. }
  961. if ($this->getGUID() == $user->getGUID()) {
  962. $return = true;
  963. }
  964. $container = $this->getContainerEntity();
  965. if ($container && $container->canEdit($user->getGUID())) {
  966. $return = true;
  967. }
  968. }
  969. $params = array('entity' => $this, 'user' => $user);
  970. return _elgg_services()->hooks->trigger('permissions_check', $this->type, $params, $return);
  971. }
  972. /**
  973. * Can a user delete this entity?
  974. *
  975. * @tip Can be overridden by registering for the permissions_check:delete plugin hook.
  976. *
  977. * @param int $user_guid The user GUID, optionally (default: logged in user)
  978. *
  979. * @return bool Whether this entity is deletable by the given user.
  980. * @since 1.11
  981. * @see elgg_set_ignore_access()
  982. */
  983. public function canDelete($user_guid = 0) {
  984. $user_guid = (int) $user_guid;
  985. if (!$user_guid) {
  986. $user_guid = _elgg_services()->session->getLoggedInUserGuid();
  987. }
  988. // need to ignore access and show hidden entities for potential hidden/disabled users
  989. $ia = elgg_set_ignore_access(true);
  990. $show_hidden = access_show_hidden_entities(true);
  991. $user = _elgg_services()->entityTable->get($user_guid, 'user');
  992. elgg_set_ignore_access($ia);
  993. access_show_hidden_entities($show_hidden);
  994. if ($user_guid & !$user) {
  995. // requested to check access for a specific user_guid, but there is no user entity, so return false
  996. $message = _elgg_services()->translator->translate('entity:can_delete:invaliduser', array($user_guid));
  997. _elgg_services()->logger->warning($message);
  998. return false;
  999. }
  1000. $return = $this->canEdit($user_guid);
  1001. $params = array('entity' => $this, 'user' => $user);
  1002. return _elgg_services()->hooks->trigger('permissions_check:delete', $this->type, $params, $return);
  1003. }
  1004. /**
  1005. * Can a user edit metadata on this entity?
  1006. *
  1007. * If no specific metadata is passed, it returns whether the user can
  1008. * edit any metadata on the entity.
  1009. *
  1010. * @tip Can be overridden by by registering for the permissions_check:metadata
  1011. * plugin hook.
  1012. *
  1013. * @param \ElggMetadata $metadata The piece of metadata to specifically check or null for any metadata
  1014. * @param int $user_guid The user GUID, optionally (default: logged in user)
  1015. *
  1016. * @return bool
  1017. * @see elgg_set_ignore_access()
  1018. */
  1019. public function canEditMetadata($metadata = null, $user_guid = 0) {
  1020. if (!$this->guid) {
  1021. // @todo cannot edit metadata on unsaved entity?
  1022. return false;
  1023. }
  1024. if ($user_guid) {
  1025. $user = get_user($user_guid);
  1026. if (!$user) {
  1027. return false;
  1028. }
  1029. } else {
  1030. $user = _elgg_services()->session->getLoggedInUser();
  1031. $user_guid = $user->guid;
  1032. }
  1033. $return = null;
  1034. // if metadata is not owned or owned by the user, then can edit
  1035. if ($metadata && ($metadata->owner_guid == 0 || $metadata->owner_guid == $user_guid)) {
  1036. $return = true;
  1037. }
  1038. if (is_null($return)) {
  1039. $return = $this->canEdit($user_guid);
  1040. }
  1041. // metadata and user may be null
  1042. $params = array('entity' => $this, 'user' => $user, 'metadata' => $metadata);
  1043. return _elgg_services()->hooks->trigger('permissions_check:metadata', $this->type, $params, $return);
  1044. }
  1045. /**
  1046. * Can a user add an entity to this container
  1047. *
  1048. * @param int $user_guid The GUID of the user creating the entity (0 for logged in user).
  1049. * @param string $type The type of entity we're looking to write
  1050. * @param string $subtype The subtype of the entity we're looking to write
  1051. *
  1052. * @return bool
  1053. * @see elgg_set_ignore_access()
  1054. */
  1055. public function canWriteToContainer($user_guid = 0, $type = 'all', $subtype = 'all') {
  1056. return can_write_to_container($user_guid, $this->guid, $type, $subtype);
  1057. }
  1058. /**
  1059. * Can a user comment on an entity?
  1060. *
  1061. * @tip Can be overridden by registering for the permissions_check:comment,
  1062. * <entity type> plugin hook.
  1063. *
  1064. * @param int $user_guid User guid (default is logged in user)
  1065. *
  1066. * @return bool
  1067. */
  1068. public function canComment($user_guid = 0) {
  1069. if ($user_guid == 0) {
  1070. $user_guid = _elgg_services()->session->getLoggedInUserGuid();
  1071. }
  1072. $user = get_entity($user_guid);
  1073. // By default, we don't take a position of whether commenting is allowed
  1074. // because it is handled by the subclasses of \ElggEntity
  1075. $params = array('entity' => $this, 'user' => $user);
  1076. return _elgg_services()->hooks->trigger('permissions_check:comment', $this->type, $params, null);
  1077. }
  1078. /**
  1079. * Can a user annotate an entity?
  1080. *
  1081. * @tip Can be overridden by registering for the plugin hook [permissions_check:annotate:<name>,
  1082. * <entity type>] or [permissions_check:annotate, <entity type>]. The hooks are called in that order.
  1083. *
  1084. * @tip If you want logged out users to annotate an object, do not call
  1085. * canAnnotate(). It's easier than using the plugin hook.
  1086. *
  1087. * @param int $user_guid User guid (default is logged in user)
  1088. * @param string $annotation_name The name of the annotation (default is unspecified)
  1089. *
  1090. * @return bool
  1091. */
  1092. public function canAnnotate($user_guid = 0, $annotation_name = '') {
  1093. if ($user_guid == 0) {
  1094. $user_guid = _elgg_services()->session->getLoggedInUserGuid();
  1095. }
  1096. $user = get_entity($user_guid);
  1097. $return = true;
  1098. if (!$user) {
  1099. $return = false;
  1100. }
  1101. $hooks = _elgg_services()->hooks;
  1102. $params = array(
  1103. 'entity' => $this,
  1104. 'user' => $user,
  1105. 'annotation_name' => $annotation_name,
  1106. );
  1107. if ($annotation_name !== '') {
  1108. $return = $hooks->trigger("permissions_check:annotate:$annotation_name", $this->type, $params, $return);
  1109. }
  1110. $return = $hooks->trigger('permissions_check:annotate', $this->type, $params, $return);
  1111. return $return;
  1112. }
  1113. /**
  1114. * Returns the access_id.
  1115. *
  1116. * @return int The access ID
  1117. */
  1118. public function getAccessID() {
  1119. return $this->access_id;
  1120. }
  1121. /**
  1122. * Returns the guid.
  1123. *
  1124. * @return int|null GUID
  1125. */
  1126. public function getGUID() {
  1127. return $this->guid;
  1128. }
  1129. /**
  1130. * Returns the entity type
  1131. *
  1132. * @return string The entity type
  1133. */
  1134. public function getType() {
  1135. return $this->type;
  1136. }
  1137. /**
  1138. * Get the entity subtype
  1139. *
  1140. * @return string The entity subtype
  1141. */
  1142. public function getSubtype() {
  1143. // If this object hasn't been saved, then return the subtype string.
  1144. if ($this->attributes['guid']) {
  1145. return get_subtype_from_id($this->attributes['subtype']);
  1146. }
  1147. return $this->attributes['subtype'];
  1148. }
  1149. /**
  1150. * Get the guid of the entity's owner.
  1151. *
  1152. * @return int The owner GUID
  1153. */
  1154. public function getOwnerGUID() {
  1155. return (int)$this->owner_guid;
  1156. }
  1157. /**
  1158. * Return the guid of the entity's owner.
  1159. *
  1160. * @return int The owner GUID
  1161. * @deprecated 1.8 Use getOwnerGUID()
  1162. */
  1163. public function getOwner() {
  1164. elgg_deprecated_notice("\ElggEntity::getOwner deprecated for \ElggEntity::getOwnerGUID", 1.8);
  1165. return $this->getOwnerGUID();
  1166. }
  1167. /**
  1168. * Gets the \ElggEntity that owns this entity.
  1169. *
  1170. * @return \ElggEntity The owning entity
  1171. */
  1172. public function getOwnerEntity() {
  1173. return get_entity($this->owner_guid);
  1174. }
  1175. /**
  1176. * Set the container for this object.
  1177. *
  1178. * @param int $container_guid The ID of the container.
  1179. *
  1180. * @return bool
  1181. */
  1182. public function setContainerGUID($container_guid) {
  1183. return $this->container_guid = (int)$container_guid;
  1184. }
  1185. /**
  1186. * Set the container for this object.
  1187. *
  1188. * @param int $container_guid The ID of the container.
  1189. *
  1190. * @return bool
  1191. * @deprecated 1.8 use setContainerGUID()
  1192. */
  1193. public function setContainer($container_guid) {
  1194. elgg_deprecated_notice("\ElggObject::setContainer deprecated for \ElggEntity::setContainerGUID", 1.8);
  1195. return $this->setContainerGUID('container_guid', $container_guid);
  1196. }
  1197. /**
  1198. * Gets the container GUID for this entity.
  1199. *
  1200. * @return int
  1201. */
  1202. public function getContainerGUID() {
  1203. return (int)$this->container_guid;
  1204. }
  1205. /**
  1206. * Gets the container GUID for this entity.
  1207. *
  1208. * @return int
  1209. * @deprecated 1.8 Use getContainerGUID()
  1210. */
  1211. public function getContainer() {
  1212. elgg_deprecated_notice("\ElggObject::getContainer deprecated for \ElggEntity::getContainerGUID", 1.8);
  1213. return $this->getContainerGUID();
  1214. }
  1215. /**
  1216. * Get the container entity for this object.
  1217. *
  1218. * @return \ElggEntity
  1219. * @since 1.8.0
  1220. */
  1221. public function getContainerEntity() {
  1222. return get_entity($this->getContainerGUID());
  1223. }
  1224. /**
  1225. * Returns the UNIX epoch time that this entity was last updated
  1226. *
  1227. * @return int UNIX epoch time
  1228. */
  1229. public function getTimeUpdated() {
  1230. return $this->time_updated;
  1231. }
  1232. /**
  1233. * Gets the URL for this entity.
  1234. *
  1235. * Plugins can register for the 'entity:url', <type> plugin hook to
  1236. * customize the url for an entity.
  1237. *
  1238. * @return string The URL of the entity
  1239. */
  1240. public function getURL() {
  1241. $url = "";
  1242. // @todo remove when elgg_register_entity_url_handler() has been removed
  1243. if ($this->guid) {
  1244. global $CONFIG;
  1245. if (isset($CONFIG->entity_url_handler[$this->getType()][$this->getSubtype()])) {
  1246. $function = $CONFIG->entity_url_handler[$this->getType()][$this->getSubtype()];
  1247. if (is_callable($function)) {
  1248. $url = call_user_func($function, $this);
  1249. }
  1250. } elseif (isset($CONFIG->entity_url_handler[$this->getType()]['all'])) {
  1251. $function = $CONFIG->entity_url_handler[$this->getType()]['all'];
  1252. if (is_callable($function)) {
  1253. $url = call_user_func($function, $this);
  1254. }
  1255. } elseif (isset($CONFIG->entity_url_handler['all']['all'])) {
  1256. $function = $CONFIG->entity_url_handler['all']['all'];
  1257. if (is_callable($function)) {
  1258. $url = call_user_func($function, $this);
  1259. }
  1260. }
  1261. if ($url) {
  1262. $url = elgg_normalize_url($url);
  1263. }
  1264. }
  1265. $type = $this->getType();
  1266. $params = array('entity' => $this);
  1267. $url = _elgg_services()->hooks->trigger('entity:url', $type, $params, $url);
  1268. // @todo remove when \ElggEntity::setURL() has been removed
  1269. if (!empty($this->url_override)) {
  1270. $url = $this->url_override;
  1271. }
  1272. return elgg_normalize_url($url);
  1273. }
  1274. /**
  1275. * Overrides the URL returned by getURL()
  1276. *
  1277. * @warning This override exists only for the life of the object.
  1278. *
  1279. * @param string $url The new item URL
  1280. *
  1281. * @return string The URL
  1282. * @deprecated 1.9.0 See \ElggEntity::getURL() for details on the plugin hook
  1283. */
  1284. public function setURL($url) {
  1285. elgg_deprecated_notice('\ElggEntity::setURL() has been replaced by the "entity:url" plugin hook', 1.9);
  1286. $this->url_override = $url;
  1287. return $url;
  1288. }
  1289. /**
  1290. * Get the URL for this entity's icon
  1291. *
  1292. * Plugins can register for the 'entity:icon:url', <type> plugin hook
  1293. * to customize the icon for an entity.
  1294. *
  1295. * @param mixed $params A string defining the size of the icon (e.g. tiny, small, medium, large)
  1296. * or an array of parameters including 'size'
  1297. * @return string The URL
  1298. * @since 1.8.0
  1299. */
  1300. public function getIconURL($params = array()) {
  1301. if (is_array($params)) {
  1302. $size = elgg_extract('size', $params, 'medium');
  1303. } else {
  1304. $size = is_string($params) ? $params : 'medium';
  1305. $params = array();
  1306. }
  1307. $size = elgg_strtolower($size);
  1308. if (isset($this->icon_override[$size])) {
  1309. elgg_deprecated_notice("icon_override on an individual entity is deprecated", 1.8);
  1310. return $this->icon_override[$size];
  1311. }
  1312. $params['entity'] = $this;
  1313. $params['size'] = $size;
  1314. $type = $this->getType();
  1315. $url = _elgg_services()->hooks->trigger('entity:icon:url', $type, $params, null);
  1316. if ($url == null) {
  1317. $url = "_graphics/icons/default/$size.png";
  1318. }
  1319. return elgg_normalize_url($url);
  1320. }
  1321. /**
  1322. * Returns a URL for the entity's icon.
  1323. *
  1324. * @param string $size Either 'large', 'medium', 'small' or 'tiny'
  1325. *
  1326. * @return string The url or false if no url could be worked out.
  1327. * @deprecated 1.8 Use getIconURL()
  1328. */
  1329. public function getIcon($size = 'medium') {
  1330. elgg_deprecated_notice("getIcon() deprecated by getIconURL()", 1.8);
  1331. return $this->getIconURL($size);
  1332. }
  1333. /**
  1334. * Set an icon override for an icon and size.
  1335. *
  1336. * @warning This override exists only for the life of the object.
  1337. *
  1338. * @param string $url The url of the icon.
  1339. * @param string $size The size its for.
  1340. *
  1341. * @return bool
  1342. * @deprecated 1.8 See getIconURL() for the plugin hook to use
  1343. */
  1344. public function setIcon($url, $size = 'medium') {
  1345. elgg_deprecated_notice("icon_override on an individual entity is deprecated", 1.8);
  1346. $url = sanitise_string($url);
  1347. $size = sanitise_string($size);
  1348. if (!$this->icon_override) {
  1349. $this->icon_override = array();
  1350. }
  1351. $this->icon_override[$size] = $url;
  1352. return true;
  1353. }
  1354. /**
  1355. * Add this entity to a site
  1356. *
  1357. * This creates a 'member_of_site' relationship.
  1358. *
  1359. * @param \ElggSite $site The site to add this entity to
  1360. *
  1361. * @return bool
  1362. * @todo add \ElggSite type hint once we have removed addToSite() from \ElggUser
  1363. * and \ElggObject
  1364. */
  1365. public function addToSite($site) {
  1366. if (!elgg_instanceof($site, 'site')) {
  1367. return false;
  1368. }
  1369. return $site->addEntity($this);
  1370. }
  1371. /**
  1372. * Remove this entity from a site
  1373. *
  1374. * This deletes the 'member_of_site' relationship.
  1375. *
  1376. * @param \ElggSite $site The site to remove this entity from
  1377. *
  1378. * @return bool
  1379. * @todo add \ElggSite type hint once we have removed addToSite() from \ElggUser
  1380. */
  1381. public function removeFromSite($site) {
  1382. if (!elgg_instanceof($site, 'site')) {
  1383. return false;
  1384. }
  1385. return $site->removeEntity($this);
  1386. }
  1387. /**
  1388. * Gets the sites this entity is a member of
  1389. *
  1390. * Site membership is determined by relationships and not site_guid.
  1391. *
  1392. * @param array $options Options array for elgg_get_entities_from_relationship()
  1393. * Parameters set automatically by this method:
  1394. * 'relationship', 'relationship_guid', 'inverse_relationship'
  1395. *
  1396. * @return array
  1397. * @todo add type hint when \ElggUser and \ElggObject have been updates
  1398. */
  1399. public function getSites($options = array()) {
  1400. $options['relationship'] = 'member_of_site';
  1401. $options['relationship_guid'] = $this->guid;
  1402. $options['inverse_relationship'] = false;
  1403. if (!isset($options['site_guid']) || !isset($options['site_guids'])) {
  1404. $options['site_guids'] = ELGG_ENTITIES_ANY_VALUE;
  1405. }
  1406. return elgg_get_entities_from_relationship($options);
  1407. }
  1408. /**
  1409. * Tests to see whether the object has been fully loaded.
  1410. *
  1411. * @return bool
  1412. */
  1413. public function isFullyLoaded() {
  1414. return ! ($this->tables_loaded < $this->tables_split);
  1415. }
  1416. /**
  1417. * Save an entity.
  1418. *
  1419. * @return bool|int
  1420. * @throws InvalidParameterException
  1421. * @throws IOException
  1422. */
  1423. public function save() {
  1424. $guid = $this->getGUID();
  1425. if ($guid > 0) {
  1426. return $this->update();
  1427. } else {
  1428. $guid = $this->create();
  1429. if ($guid) {
  1430. if (_elgg_services()->events->trigger('create', $this->type, $this)) {
  1431. return $guid;
  1432. } else {
  1433. // plugins that return false to event don't need to override the access system
  1434. $ia = elgg_set_ignore_access(true);
  1435. $this->delete();
  1436. elgg_set_ignore_access($ia);
  1437. }
  1438. }
  1439. }
  1440. return false;
  1441. }
  1442. /**
  1443. * Create a new entry in the entities table.
  1444. *
  1445. * Saves the base information in the entities table for the entity. Saving
  1446. * the type-specific information is handled in the calling class method.
  1447. *
  1448. * @warning Entities must have an entry in both the entities table and their type table
  1449. * or they will throw an exception when loaded.
  1450. *
  1451. * @return int The new entity's GUID
  1452. * @throws InvalidParameterException If the entity's type has not been set.
  1453. * @throws IOException If the new row fails to write to the DB.
  1454. */
  1455. protected function create() {
  1456. global $CONFIG;
  1457. // Using attribute array directly; get function does something special!
  1458. $type = $this->getDatabase()->sanitizeString($this->attributes['type']);
  1459. if ($type == "") {
  1460. throw new \InvalidParameterException("Entity type must be set.");
  1461. }
  1462. $subtype = $this->attributes['subtype'];
  1463. $subtype_id = add_subtype($type, $subtype);
  1464. $owner_guid = (int)$this->attributes['owner_guid'];
  1465. $access_id = (int)$this->attributes['access_id'];
  1466. $now = (string)time();
  1467. $time_created = isset($this->attributes['time_created']) ? (int)$this->attributes['time_created'] : $now;
  1468. $site_guid = $this->attributes['site_guid'];
  1469. if ($site_guid == 0) {
  1470. $site_guid = $CONFIG->site_guid;
  1471. }
  1472. $site_guid = (int)$site_guid;
  1473. $container_guid = $this->attributes['container_guid'];
  1474. if ($container_guid == 0) {
  1475. $container_guid = $owner_guid;
  1476. $this->attributes['container_guid'] = $container_guid;
  1477. }
  1478. $container_guid = (int)$container_guid;
  1479. if ($access_id == ACCESS_DEFAULT) {
  1480. throw new \InvalidParameterException('ACCESS_DEFAULT is not a valid access level. See its documentation in elgglib.h');
  1481. }
  1482. $owner = $this->getOwnerEntity();
  1483. if ($owner && !$owner->canWriteToContainer(0, $type, $subtype)) {
  1484. return false;
  1485. }
  1486. if ($owner_guid != $container_guid) {
  1487. $container = $this->getContainerEntity();
  1488. if ($container && !$container->canWriteToContainer(0, $type, $subtype)) {
  1489. return false;
  1490. }
  1491. }
  1492. $result = $this->getDatabase()->insertData("INSERT into {$CONFIG->dbprefix}entities
  1493. (type, subtype, owner_guid, site_guid, container_guid,
  1494. access_id, time_created, time_updated, last_action)
  1495. values
  1496. ('$type', $subtype_id, $owner_guid, $site_guid, $container_guid,
  1497. $access_id, $time_created, $now, $now)");
  1498. if (!$result) {
  1499. throw new \IOException("Unable to save new object's base entity information!");
  1500. }
  1501. // for BC with 1.8, ->subtype always returns ID, ->getSubtype() the string
  1502. $this->attributes['subtype'] = (int)$subtype_id;
  1503. $this->attributes['guid'] = (int)$result;
  1504. $this->attributes['time_created'] = (int)$time_created;
  1505. $this->attributes['time_updated'] = (int)$now;
  1506. $this->attributes['last_action'] = (int)$now;
  1507. $this->attributes['site_guid'] = (int)$site_guid;
  1508. $this->attributes['container_guid'] = (int)$container_guid;
  1509. // Save any unsaved metadata
  1510. if (sizeof($this->temp_metadata) > 0) {
  1511. foreach ($this->temp_metadata as $name => $value) {
  1512. $this->$name = $value;
  1513. }
  1514. $this->temp_metadata = array();
  1515. }
  1516. // Save any unsaved annotations.
  1517. if (sizeof($this->temp_annotations) > 0) {
  1518. foreach ($this->temp_annotations as $name => $value) {
  1519. $this->annotate($name, $value);
  1520. }
  1521. $this->temp_annotations = array();
  1522. }
  1523. // Save any unsaved private settings.
  1524. if (sizeof($this->temp_private_settings) > 0) {
  1525. foreach ($this->temp_private_settings as $name => $value) {
  1526. $this->setPrivateSetting($name, $value);
  1527. }
  1528. $this->temp_private_settings = array();
  1529. }
  1530. _elgg_cache_entity($this);
  1531. return $result;
  1532. }
  1533. /**
  1534. * Update the entity in the database.
  1535. *
  1536. * @return bool Whether the update was successful.
  1537. *
  1538. * @throws InvalidParameterException
  1539. */
  1540. protected function update() {
  1541. global $CONFIG;
  1542. // See #5600. This ensures canEdit() checks the BD persisted entity so it sees the
  1543. // persisted owner_guid, container_guid, etc.
  1544. _elgg_disable_caching_for_entity($this->guid);
  1545. $persisted_entity = get_entity($this->guid);
  1546. if (!$persisted_entity) {
  1547. // Why worry about this case? If access control was off when the user fetched this object but
  1548. // was turned back on again. Better to just bail than to turn access control off again.
  1549. return false;
  1550. }
  1551. $allow_edit = $persisted_entity->canEdit();
  1552. unset($persisted_entity);
  1553. if ($allow_edit) {
  1554. // give old update event a chance to stop the update
  1555. $allow_edit = _elgg_services()->events->trigger('update', $this->type, $this);
  1556. }
  1557. _elgg_enable_caching_for_entity($this->guid);
  1558. if (!$allow_edit) {
  1559. return false;
  1560. }
  1561. // See #6225. We copy these after the update event in case a handler changed one of them.
  1562. $guid = (int)$this->guid;
  1563. $owner_guid = (int)$this->owner_guid;
  1564. $access_id = (int)$this->access_id;
  1565. $container_guid = (int)$this->container_guid;
  1566. $time_created = (int)$this->time_created;
  1567. $time = time();
  1568. if ($access_id == ACCESS_DEFAULT) {
  1569. throw new \InvalidParameterException('ACCESS_DEFAULT is not a valid access level. See its documentation in elgglib.php');
  1570. }
  1571. $ret = $this->getDatabase()->updateData("UPDATE {$CONFIG->dbprefix}entities
  1572. set owner_guid='$owner_guid', access_id='$access_id',
  1573. container_guid='$container_guid', time_created='$time_created',
  1574. time_updated='$time' WHERE guid=$guid");
  1575. elgg_trigger_after_event('update', $this->type, $this);
  1576. // TODO(evan): Move this to \ElggObject?
  1577. if ($this instanceof \ElggObject) {
  1578. update_river_access_by_object($guid, $access_id);
  1579. }
  1580. // If memcache is available then delete this entry from the cache
  1581. static $newentity_cache;
  1582. if ((!$newentity_cache) && (is_memcache_available())) {
  1583. $newentity_cache = new \ElggMemcache('new_entity_cache');
  1584. }
  1585. if ($newentity_cache) {
  1586. $newentity_cache->delete($guid);
  1587. }
  1588. if ($ret !== false) {
  1589. $this->attributes['time_updated'] = $time;
  1590. }
  1591. _elgg_cache_entity($this);
  1592. // Handle cases where there was no error BUT no rows were updated!
  1593. return $ret !== false;
  1594. }
  1595. /**
  1596. * Loads attributes from the entities table into the object.
  1597. *
  1598. * @param mixed $guid GUID of entity or \stdClass object from entities table
  1599. *
  1600. * @return bool
  1601. */
  1602. protected function load($guid) {
  1603. if ($guid instanceof \stdClass) {
  1604. $row = $guid;
  1605. } else {
  1606. $row = get_entity_as_row($guid);
  1607. }
  1608. if ($row) {
  1609. // Create the array if necessary - all subclasses should test before creating
  1610. if (!is_array($this->attributes)) {
  1611. $this->attributes = array();
  1612. }
  1613. // Now put these into the attributes array as core values
  1614. $objarray = (array) $row;
  1615. foreach ($objarray as $key => $value) {
  1616. $this->attributes[$key] = $value;
  1617. }
  1618. // Increment the portion counter
  1619. if (!$this->isFullyLoaded()) {
  1620. $this->tables_loaded++;
  1621. }
  1622. // guid needs to be an int https://github.com/elgg/elgg/issues/4111
  1623. $this->attributes['guid'] = (int)$this->attributes['guid'];
  1624. // for BC with 1.8, ->subtype always returns ID, ->getSubtype() the string
  1625. $this->attributes['subtype'] = (int)$this->attributes['subtype'];
  1626. // Cache object handle
  1627. if ($this->attributes['guid']) {
  1628. _elgg_cache_entity($this);
  1629. }
  1630. return true;
  1631. }
  1632. return false;
  1633. }
  1634. /**
  1635. * Stores non-attributes from the loading of the entity as volatile data
  1636. *
  1637. * @param array $data Key value array
  1638. * @return void
  1639. */
  1640. protected function loadAdditionalSelectValues(array $data) {
  1641. foreach ($data as $name => $value) {
  1642. $this->setVolatileData("select:$name", $value);
  1643. }
  1644. }
  1645. /**
  1646. * Load new data from database into existing entity. Overwrites data but
  1647. * does not change values not included in the latest data.
  1648. *
  1649. * @internal This is used when the same entity is selected twice during a
  1650. * request in case different select clauses were used to load different data
  1651. * into volatile data.
  1652. *
  1653. * @param \stdClass $row DB row with new entity data
  1654. * @return bool
  1655. * @access private
  1656. */
  1657. public function refresh(\stdClass $row) {
  1658. if ($row instanceof \stdClass) {
  1659. return $this->load($row);
  1660. }
  1661. return false;
  1662. }
  1663. /**
  1664. * Disable this entity.
  1665. *
  1666. * Disabled entities are not returned by getter functions.
  1667. * To enable an entity, use {@link \ElggEntity::enable()}.
  1668. *
  1669. * Recursively disabling an entity will disable all entities
  1670. * owned or contained by the parent entity.
  1671. *
  1672. * You can ignore the disabled field by using {@link access_show_hidden_entities()}.
  1673. *
  1674. * @note Internal: Disabling an entity sets the 'enabled' column to 'no'.
  1675. *
  1676. * @param string $reason Optional reason
  1677. * @param bool $recursive Recursively disable all contained entities?
  1678. *
  1679. * @return bool
  1680. * @see \ElggEntity::enable()
  1681. */
  1682. public function disable($reason = "", $recursive = true) {
  1683. if (!$this->guid) {
  1684. return false;
  1685. }
  1686. if (!_elgg_services()->events->trigger('disable', $this->type, $this)) {
  1687. return false;
  1688. }
  1689. if (!$this->canEdit()) {
  1690. return false;
  1691. }
  1692. _elgg_invalidate_cache_for_entity($this->guid);
  1693. if ($reason) {
  1694. $this->disable_reason = $reason;
  1695. }
  1696. global $CONFIG;
  1697. $guid = (int)$this->guid;
  1698. if ($recursive) {
  1699. $hidden = access_get_show_hidden_status();
  1700. access_show_hidden_entities(true);
  1701. $ia = elgg_set_ignore_access(true);
  1702. $sub_entities = $this->getDatabase()->getData("SELECT * FROM {$CONFIG->dbprefix}entities
  1703. WHERE (
  1704. container_guid = $guid
  1705. OR owner_guid = $guid
  1706. OR site_guid = $guid
  1707. ) AND enabled='yes'", 'entity_row_to_elggstar');
  1708. if ($sub_entities) {
  1709. foreach ($sub_entities as $e) {
  1710. add_entity_relationship($e->guid, 'disabled_with', $this->guid);
  1711. $e->disable($reason);
  1712. }
  1713. }
  1714. access_show_hidden_entities($hidden);
  1715. elgg_set_ignore_access($ia);
  1716. }
  1717. $this->disableMetadata();
  1718. $this->disableAnnotations();
  1719. $res = $this->getDatabase()->updateData("UPDATE {$CONFIG->dbprefix}entities
  1720. SET enabled = 'no'
  1721. WHERE guid = $guid");
  1722. if ($res) {
  1723. $this->attributes['enabled'] = 'no';
  1724. _elgg_services()->events->trigger('disable:after', $this->type, $this);
  1725. }
  1726. return $res;
  1727. }
  1728. /**
  1729. * Enable the entity
  1730. *
  1731. * @warning Disabled entities can't be loaded unless
  1732. * {@link access_show_hidden_entities(true)} has been called.
  1733. *
  1734. * @param bool $recursive Recursively enable all entities disabled with the entity?
  1735. * @see access_show_hiden_entities()
  1736. * @return bool
  1737. */
  1738. public function enable($recursive = true) {
  1739. $guid = (int)$this->guid;
  1740. if (!$guid) {
  1741. return false;
  1742. }
  1743. if (!_elgg_services()->events->trigger('enable', $this->type, $this)) {
  1744. return false;
  1745. }
  1746. if (!$this->canEdit()) {
  1747. return false;
  1748. }
  1749. global $CONFIG;
  1750. // Override access only visible entities
  1751. $old_access_status = access_get_show_hidden_status();
  1752. access_show_hidden_entities(true);
  1753. $result = $this->getDatabase()->updateData("UPDATE {$CONFIG->dbprefix}entities
  1754. SET enabled = 'yes'
  1755. WHERE guid = $guid");
  1756. $this->deleteMetadata('disable_reason');
  1757. $this->enableMetadata();
  1758. $this->enableAnnotations();
  1759. if ($recursive) {
  1760. $disabled_with_it = elgg_get_entities_from_relationship(array(
  1761. 'relationship' => 'disabled_with',
  1762. 'relationship_guid' => $guid,
  1763. 'inverse_relationship' => true,
  1764. 'limit' => 0,
  1765. ));
  1766. foreach ($disabled_with_it as $e) {
  1767. $e->enable();
  1768. remove_entity_relationship($e->guid, 'disabled_with', $guid);
  1769. }
  1770. }
  1771. access_show_hidden_entities($old_access_status);
  1772. if ($result) {
  1773. $this->attributes['enabled'] = 'yes';
  1774. _elgg_services()->events->trigger('enable:after', $this->type, $this);
  1775. }
  1776. return $result;
  1777. }
  1778. /**
  1779. * Is this entity enabled?
  1780. *
  1781. * @return boolean Whether this entity is enabled.
  1782. */
  1783. public function isEnabled() {
  1784. return $this->enabled == 'yes';
  1785. }
  1786. /**
  1787. * Deletes the entity.
  1788. *
  1789. * Removes the entity and its metadata, annotations, relationships,
  1790. * river entries, and private data.
  1791. *
  1792. * Optionally can remove entities contained and owned by this entity.
  1793. *
  1794. * @warning If deleting recursively, this bypasses ownership of items contained by
  1795. * the entity. That means that if the container_guid = $this->guid, the item will
  1796. * be deleted regardless of who owns it.
  1797. *
  1798. * @param bool $recursive If true (default) then all entities which are
  1799. * owned or contained by $this will also be deleted.
  1800. *
  1801. * @return bool
  1802. */
  1803. public function delete($recursive = true) {
  1804. global $CONFIG;
  1805. $guid = $this->guid;
  1806. if (!$guid) {
  1807. return false;
  1808. }
  1809. // first check if we can delete this entity
  1810. // NOTE: in Elgg <= 1.10.3 this was after the delete event,
  1811. // which could potentially remove some content if the user didn't have access
  1812. if (!$this->canDelete()) {
  1813. return false;
  1814. }
  1815. // now trigger an event to let others know this entity is about to be deleted
  1816. // so they can prevent it or take their own actions
  1817. if (!_elgg_services()->events->trigger('delete', $this->type, $this)) {
  1818. return false;
  1819. }
  1820. _elgg_invalidate_cache_for_entity($guid);
  1821. // If memcache is available then delete this entry from the cache
  1822. static $newentity_cache;
  1823. if ((!$newentity_cache) && (is_memcache_available())) {
  1824. $newentity_cache = new \ElggMemcache('new_entity_cache');
  1825. }
  1826. if ($newentity_cache) {
  1827. $newentity_cache->delete($guid);
  1828. }
  1829. // Delete contained owned and otherwise releated objects (depth first)
  1830. if ($recursive) {
  1831. // Temporarily overriding access controls
  1832. $entity_disable_override = access_get_show_hidden_status();
  1833. access_show_hidden_entities(true);
  1834. $ia = elgg_set_ignore_access(true);
  1835. // @todo there was logic in the original code that ignored
  1836. // entities with owner or container guids of themselves.
  1837. // this should probably be prevented in \ElggEntity instead of checked for here
  1838. $options = array(
  1839. 'wheres' => array(
  1840. "((container_guid = $guid OR owner_guid = $guid OR site_guid = $guid)"
  1841. . " AND guid != $guid)"
  1842. ),
  1843. 'limit' => 0
  1844. );
  1845. $batch = new \ElggBatch('elgg_get_entities', $options);
  1846. $batch->setIncrementOffset(false);
  1847. foreach ($batch as $e) {
  1848. $e->delete(true);
  1849. }
  1850. access_show_hidden_entities($entity_disable_override);
  1851. elgg_set_ignore_access($ia);
  1852. }
  1853. $entity_disable_override = access_get_show_hidden_status();
  1854. access_show_hidden_entities(true);
  1855. $ia = elgg_set_ignore_access(true);
  1856. // Now delete the entity itself
  1857. $this->deleteMetadata();
  1858. $this->deleteOwnedMetadata();
  1859. $this->deleteAnnotations();
  1860. $this->deleteOwnedAnnotations();
  1861. $this->deleteRelationships();
  1862. $this->deleteAccessCollectionMemberships();
  1863. $this->deleteOwnedAccessCollections();
  1864. access_show_hidden_entities($entity_disable_override);
  1865. elgg_set_ignore_access($ia);
  1866. elgg_delete_river(array('subject_guid' => $guid));
  1867. elgg_delete_river(array('object_guid' => $guid));
  1868. elgg_delete_river(array('target_guid' => $guid));
  1869. remove_all_private_settings($guid);
  1870. $res = $this->getDatabase()->deleteData("DELETE FROM {$CONFIG->dbprefix}entities WHERE guid = $guid");
  1871. if ($res) {
  1872. $sub_table = "";
  1873. // Where appropriate delete the sub table
  1874. switch ($this->type) {
  1875. case 'object' :
  1876. $sub_table = $CONFIG->dbprefix . 'objects_entity';
  1877. break;
  1878. case 'user' :
  1879. $sub_table = $CONFIG->dbprefix . 'users_entity';
  1880. break;
  1881. case 'group' :
  1882. $sub_table = $CONFIG->dbprefix . 'groups_entity';
  1883. break;
  1884. case 'site' :
  1885. $sub_table = $CONFIG->dbprefix . 'sites_entity';
  1886. break;
  1887. }
  1888. if ($sub_table) {
  1889. $this->getDatabase()->deleteData("DELETE FROM $sub_table WHERE guid = $guid");
  1890. }
  1891. }
  1892. _elgg_clear_entity_files($this);
  1893. return (bool)$res;
  1894. }
  1895. /**
  1896. * {@inheritdoc}
  1897. */
  1898. public function toObject() {
  1899. $object = $this->prepareObject(new \stdClass());
  1900. $params = array('entity' => $this);
  1901. $object = _elgg_services()->hooks->trigger('to:object', 'entity', $params, $object);
  1902. return $object;
  1903. }
  1904. /**
  1905. * Prepare an object copy for toObject()
  1906. *
  1907. * @param \stdClass $object Object representation of the entity
  1908. * @return \stdClass
  1909. */
  1910. protected function prepareObject($object) {
  1911. $object->guid = $this->guid;
  1912. $object->type = $this->getType();
  1913. $object->subtype = $this->getSubtype();
  1914. $object->owner_guid = $this->getOwnerGUID();
  1915. $object->container_guid = $this->getContainerGUID();
  1916. $object->site_guid = (int)$this->site_guid;
  1917. $object->time_created = date('c', $this->getTimeCreated());
  1918. $object->time_updated = date('c', $this->getTimeUpdated());
  1919. $object->url = $this->getURL();
  1920. $object->read_access = (int)$this->access_id;
  1921. return $object;
  1922. }
  1923. /*
  1924. * LOCATABLE INTERFACE
  1925. */
  1926. /**
  1927. * Gets the 'location' metadata for the entity
  1928. *
  1929. * @return string The location
  1930. */
  1931. public function getLocation() {
  1932. return $this->location;
  1933. }
  1934. /**
  1935. * Sets the 'location' metadata for the entity
  1936. *
  1937. * @param string $location String representation of the location
  1938. *
  1939. * @return void
  1940. */
  1941. public function setLocation($location) {
  1942. $this->location = $location;
  1943. }
  1944. /**
  1945. * Set latitude and longitude metadata tags for a given entity.
  1946. *
  1947. * @param float $lat Latitude
  1948. * @param float $long Longitude
  1949. *
  1950. * @return void
  1951. * @todo Unimplemented
  1952. */
  1953. public function setLatLong($lat, $long) {
  1954. $this->{"geo:lat"} = $lat;
  1955. $this->{"geo:long"} = $long;
  1956. }
  1957. /**
  1958. * Return the entity's latitude.
  1959. *
  1960. * @return float
  1961. * @todo Unimplemented
  1962. */
  1963. public function getLatitude() {
  1964. return (float)$this->{"geo:lat"};
  1965. }
  1966. /**
  1967. * Return the entity's longitude
  1968. *
  1969. * @return float
  1970. * @todo Unimplemented
  1971. */
  1972. public function getLongitude() {
  1973. return (float)$this->{"geo:long"};
  1974. }
  1975. /*
  1976. * NOTABLE INTERFACE
  1977. */
  1978. /**
  1979. * Set the time and duration of an object
  1980. *
  1981. * @param int $hour If ommitted, now is assumed.
  1982. * @param int $minute If ommitted, now is assumed.
  1983. * @param int $second If ommitted, now is assumed.
  1984. * @param int $day If ommitted, now is assumed.
  1985. * @param int $month If ommitted, now is assumed.
  1986. * @param int $year If ommitted, now is assumed.
  1987. * @param int $duration Duration of event, remainder of the day is assumed.
  1988. *
  1989. * @return true
  1990. * @deprecated 1.9
  1991. */
  1992. public function setCalendarTimeAndDuration($hour = null, $minute = null, $second = null,
  1993. $day = null, $month = null, $year = null, $duration = null) {
  1994. elgg_deprecated_notice(__METHOD__ . ' has been deprecated', 1.9);
  1995. $start = mktime($hour, $minute, $second, $month, $day, $year);
  1996. $end = $start + abs($duration);
  1997. if (!$duration) {
  1998. $end = get_day_end($day, $month, $year);
  1999. }
  2000. $this->calendar_start = $start;
  2001. $this->calendar_end = $end;
  2002. return true;
  2003. }
  2004. /**
  2005. * Returns the start timestamp.
  2006. *
  2007. * @return int
  2008. * @deprecated 1.9
  2009. */
  2010. public function getCalendarStartTime() {
  2011. elgg_deprecated_notice(__METHOD__ . ' has been deprecated', 1.9);
  2012. return (int)$this->calendar_start;
  2013. }
  2014. /**
  2015. * Returns the end timestamp.
  2016. *
  2017. * @return int
  2018. * @deprecated 1.9
  2019. */
  2020. public function getCalendarEndTime() {
  2021. elgg_deprecated_notice(__METHOD__ . ' has been deprecated', 1.9);
  2022. return (int)$this->calendar_end;
  2023. }
  2024. /*
  2025. * EXPORTABLE INTERFACE
  2026. */
  2027. /**
  2028. * Returns an array of fields which can be exported.
  2029. *
  2030. * @return array
  2031. * @deprecated 1.9 Use toObject()
  2032. */
  2033. public function getExportableValues() {
  2034. elgg_deprecated_notice(__METHOD__ . ' has been deprecated by toObject()', 1.9);
  2035. return array(
  2036. 'guid',
  2037. 'type',
  2038. 'subtype',
  2039. 'time_created',
  2040. 'time_updated',
  2041. 'container_guid',
  2042. 'owner_guid',
  2043. 'site_guid'
  2044. );
  2045. }
  2046. /**
  2047. * Export this class into an array of ODD Elements containing all necessary fields.
  2048. * Override if you wish to return more information than can be found in
  2049. * $this->attributes (shouldn't happen)
  2050. *
  2051. * @return array
  2052. * @deprecated 1.9
  2053. */
  2054. public function export() {
  2055. elgg_deprecated_notice(__METHOD__ . ' has been deprecated', 1.9);
  2056. $tmp = array();
  2057. // Generate uuid
  2058. $uuid = guid_to_uuid($this->getGUID());
  2059. // Create entity
  2060. $odd = new ODDEntity(
  2061. $uuid,
  2062. $this->attributes['type'],
  2063. get_subtype_from_id($this->attributes['subtype'])
  2064. );
  2065. $tmp[] = $odd;
  2066. $exportable_values = $this->getExportableValues();
  2067. // Now add its attributes
  2068. foreach ($this->attributes as $k => $v) {
  2069. $meta = null;
  2070. if (in_array($k, $exportable_values)) {
  2071. switch ($k) {
  2072. case 'guid': // Dont use guid in OpenDD
  2073. case 'type': // Type and subtype already taken care of
  2074. case 'subtype':
  2075. break;
  2076. case 'time_created': // Created = published
  2077. $odd->setAttribute('published', date("r", $v));
  2078. break;
  2079. case 'site_guid': // Container
  2080. $k = 'site_uuid';
  2081. $v = guid_to_uuid($v);
  2082. $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
  2083. break;
  2084. case 'container_guid': // Container
  2085. $k = 'container_uuid';
  2086. $v = guid_to_uuid($v);
  2087. $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
  2088. break;
  2089. case 'owner_guid': // Convert owner guid to uuid, this will be stored in metadata
  2090. $k = 'owner_uuid';
  2091. $v = guid_to_uuid($v);
  2092. $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
  2093. break;
  2094. default:
  2095. $meta = new ODDMetaData($uuid . "attr/$k/", $uuid, $k, $v);
  2096. }
  2097. // set the time of any metadata created
  2098. if ($meta) {
  2099. $meta->setAttribute('published', date("r", $this->time_created));
  2100. $tmp[] = $meta;
  2101. }
  2102. }
  2103. }
  2104. // Now we do something a bit special.
  2105. /*
  2106. * This provides a rendered view of the entity to foreign sites.
  2107. */
  2108. elgg_set_viewtype('default');
  2109. $view = elgg_view_entity($this, array('full_view' => true));
  2110. elgg_set_viewtype();
  2111. $tmp[] = new ODDMetaData($uuid . "volatile/renderedentity/", $uuid,
  2112. 'renderedentity', $view, 'volatile');
  2113. return $tmp;
  2114. }
  2115. /*
  2116. * IMPORTABLE INTERFACE
  2117. */
  2118. /**
  2119. * Import data from an parsed ODD xml data array.
  2120. *
  2121. * @param ODD $data XML data
  2122. *
  2123. * @return true
  2124. *
  2125. * @throws InvalidParameterException
  2126. * @deprecated 1.9 Use toObject()
  2127. */
  2128. public function import(ODD $data) {
  2129. elgg_deprecated_notice(__METHOD__ . ' has been deprecated', 1.9);
  2130. if (!($data instanceof ODDEntity)) {
  2131. throw new \InvalidParameterException("import() passed an unexpected ODD class");
  2132. }
  2133. // Set type and subtype
  2134. $this->attributes['type'] = $data->getAttribute('class');
  2135. $this->attributes['subtype'] = $data->getAttribute('subclass');
  2136. // Set owner
  2137. $this->attributes['owner_guid'] = _elgg_services()->session->getLoggedInUserGuid(); // Import as belonging to importer.
  2138. // Set time
  2139. $this->attributes['time_created'] = strtotime($data->getAttribute('published'));
  2140. $this->attributes['time_updated'] = time();
  2141. return true;
  2142. }
  2143. /*
  2144. * SYSTEM LOG INTERFACE
  2145. */
  2146. /**
  2147. * Return an identification for the object for storage in the system log.
  2148. * This id must be an integer.
  2149. *
  2150. * @return int
  2151. */
  2152. public function getSystemLogID() {
  2153. return $this->getGUID();
  2154. }
  2155. /**
  2156. * For a given ID, return the object associated with it.
  2157. * This is used by the system log. It can be called on any Loggable object.
  2158. *
  2159. * @param int $id GUID.
  2160. * @return int GUID
  2161. */
  2162. public function getObjectFromID($id) {
  2163. return get_entity($id);
  2164. }
  2165. /**
  2166. * Returns tags for this entity.
  2167. *
  2168. * @warning Tags must be registered by {@link elgg_register_tag_metadata_name()}.
  2169. *
  2170. * @param array $tag_names Optionally restrict by tag metadata names.
  2171. *
  2172. * @return array
  2173. */
  2174. public function getTags($tag_names = null) {
  2175. if ($tag_names && !is_array($tag_names)) {
  2176. $tag_names = array($tag_names);
  2177. }
  2178. $valid_tags = elgg_get_registered_tag_metadata_names();
  2179. $entity_tags = array();
  2180. foreach ($valid_tags as $tag_name) {
  2181. if (is_array($tag_names) && !in_array($tag_name, $tag_names)) {
  2182. continue;
  2183. }
  2184. if ($tags = $this->$tag_name) {
  2185. // if a single tag, metadata returns a string.
  2186. // if multiple tags, metadata returns an array.
  2187. if (is_array($tags)) {
  2188. $entity_tags = array_merge($entity_tags, $tags);
  2189. } else {
  2190. $entity_tags[] = $tags;
  2191. }
  2192. }
  2193. }
  2194. return $entity_tags;
  2195. }
  2196. /**
  2197. * Remove the membership of all access collections for this entity (if the entity is a user)
  2198. *
  2199. * @return bool
  2200. * @since 1.11
  2201. */
  2202. public function deleteAccessCollectionMemberships() {
  2203. if (!$this->guid) {
  2204. return false;
  2205. }
  2206. if ($this->type !== 'user') {
  2207. return true;
  2208. }
  2209. $ac = _elgg_services()->accessCollections;
  2210. $collections = $ac->getCollectionsByMember($this->guid);
  2211. if (empty($collections)) {
  2212. return true;
  2213. }
  2214. $result = true;
  2215. foreach ($collections as $collection) {
  2216. $result = $result & $ac->removeUser($this->guid, $collection->id);
  2217. }
  2218. return $result;
  2219. }
  2220. /**
  2221. * Remove all access collections owned by this entity
  2222. *
  2223. * @return bool
  2224. * @since 1.11
  2225. */
  2226. public function deleteOwnedAccessCollections() {
  2227. if (!$this->guid) {
  2228. return false;
  2229. }
  2230. $ac = _elgg_services()->accessCollections;
  2231. $collections = $ac->getEntityCollections($this->guid);
  2232. if (empty($collections)) {
  2233. return true;
  2234. }
  2235. $result = true;
  2236. foreach ($collections as $collection) {
  2237. $result = $result & $ac->delete($collection->id);
  2238. }
  2239. return $result;
  2240. }
  2241. }