ElggGroup.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. <?php
  2. /**
  3. * Class representing a container for other elgg entities.
  4. *
  5. * @package Elgg.Core
  6. * @subpackage Groups
  7. *
  8. * @property string $name A short name that captures the purpose of the group
  9. * @property string $description A longer body of content that gives more details about the group
  10. */
  11. class ElggGroup extends \ElggEntity
  12. implements Friendable {
  13. const CONTENT_ACCESS_MODE_UNRESTRICTED = 'unrestricted';
  14. const CONTENT_ACCESS_MODE_MEMBERS_ONLY = 'members_only';
  15. /**
  16. * Sets the type to group.
  17. *
  18. * @return void
  19. */
  20. protected function initializeAttributes() {
  21. parent::initializeAttributes();
  22. $this->attributes['type'] = "group";
  23. $this->attributes += self::getExternalAttributes();
  24. $this->tables_split = 2;
  25. }
  26. /**
  27. * Get default values for attributes stored in a separate table
  28. *
  29. * @return array
  30. * @access private
  31. *
  32. * @see \Elgg\Database\EntityTable::getEntities
  33. */
  34. final public static function getExternalAttributes() {
  35. return [
  36. 'name' => null,
  37. 'description' => null,
  38. ];
  39. }
  40. /**
  41. * Construct a new group entity
  42. *
  43. * Plugin developers should only use the constructor to create a new entity.
  44. * To retrieve entities, use get_entity() and the elgg_get_entities* functions.
  45. *
  46. * @param \stdClass $row Database row result. Default is null to create a new group.
  47. *
  48. * @throws IOException|InvalidParameterException if there was a problem creating the group.
  49. */
  50. public function __construct($row = null) {
  51. $this->initializeAttributes();
  52. // compatibility for 1.7 api.
  53. $this->initialise_attributes(false);
  54. if (!empty($row)) {
  55. // Is $guid is a entity table DB row
  56. if ($row instanceof \stdClass) {
  57. // Load the rest
  58. if (!$this->load($row)) {
  59. $msg = "Failed to load new " . get_class() . " for GUID:" . $row->guid;
  60. throw new \IOException($msg);
  61. }
  62. } else if ($row instanceof \ElggGroup) {
  63. // $row is an \ElggGroup so this is a copy constructor
  64. elgg_deprecated_notice('This type of usage of the \ElggGroup constructor was deprecated. Please use the clone method.', 1.7);
  65. foreach ($row->attributes as $key => $value) {
  66. $this->attributes[$key] = $value;
  67. }
  68. } else if (is_numeric($row)) {
  69. // $row is a GUID so load entity
  70. elgg_deprecated_notice('Passing a GUID to constructor is deprecated. Use get_entity()', 1.9);
  71. if (!$this->load($row)) {
  72. throw new \IOException("Failed to load new " . get_class() . " from GUID:" . $row);
  73. }
  74. } else {
  75. throw new \InvalidParameterException("Unrecognized value passed to constuctor.");
  76. }
  77. }
  78. }
  79. /**
  80. * {@inheritdoc}
  81. */
  82. public function getDisplayName() {
  83. return $this->name;
  84. }
  85. /**
  86. * {@inheritdoc}
  87. */
  88. public function setDisplayName($displayName) {
  89. $this->name = $displayName;
  90. }
  91. /**
  92. * Add an \ElggObject to this group.
  93. *
  94. * @param \ElggObject $object The object.
  95. *
  96. * @return bool
  97. */
  98. public function addObjectToGroup(\ElggObject $object) {
  99. $object->container_guid = $this->guid;
  100. return $object->save();
  101. }
  102. /**
  103. * Remove an object from this containing group and sets the container to be
  104. * object's owner
  105. *
  106. * @param \ElggObject $object The object.
  107. *
  108. * @return bool
  109. */
  110. public function removeObjectFromGroup($object) {
  111. if (is_numeric($object)) {
  112. elgg_deprecated_notice('\ElggGroup::removeObjectFromGroup() takes an \ElggObject not a guid.', 1.9);
  113. $object = get_entity($object);
  114. if (!elgg_instanceof($object, 'object')) {
  115. return false;
  116. }
  117. }
  118. $object->container_guid = $object->owner_guid;
  119. return $object->save();
  120. }
  121. /**
  122. * Wrapper around \ElggEntity::__get()
  123. *
  124. * @see \ElggEntity::__get()
  125. *
  126. * @param string $name Name
  127. * @return mixed
  128. * @todo deprecate appending group to username. Was a hack used for creating
  129. * URLs for group content. We stopped using the hack in 1.8.
  130. */
  131. public function __get($name) {
  132. if ($name == 'username') {
  133. return 'group:' . $this->getGUID();
  134. }
  135. return parent::__get($name);
  136. }
  137. /**
  138. * Wrapper around \ElggEntity::get()
  139. *
  140. * @param string $name Name
  141. * @return mixed
  142. * @deprecated 1.9
  143. */
  144. public function get($name) {
  145. elgg_deprecated_notice("Use -> instead of get()", 1.9);
  146. return $this->__get($name);
  147. }
  148. /**
  149. * Start friendable compatibility block:
  150. *
  151. * public function addFriend($friend_guid);
  152. public function removeFriend($friend_guid);
  153. public function isFriend();
  154. public function isFriendsWith($user_guid);
  155. public function isFriendOf($user_guid);
  156. public function getFriends($subtype = "", $limit = 10, $offset = 0);
  157. public function getFriendsOf($subtype = "", $limit = 10, $offset = 0);
  158. public function getObjects($subtype="", $limit = 10, $offset = 0);
  159. public function getFriendsObjects($subtype = "", $limit = 10, $offset = 0);
  160. public function countObjects($subtype = "");
  161. */
  162. /**
  163. * For compatibility with Friendable.
  164. *
  165. * Join a group when you friend \ElggGroup.
  166. *
  167. * @param int $friend_guid The GUID of the user joining the group.
  168. *
  169. * @return bool
  170. * @deprecated 1.9 Use \ElggGroup::join()
  171. */
  172. public function addFriend($friend_guid) {
  173. elgg_deprecated_notice("\ElggGroup::addFriend() is deprecated. Use \ElggGroup::join()", 1.9);
  174. $user = get_user($friend_guid);
  175. return $user ? $this->join($user) : false;
  176. }
  177. /**
  178. * For compatibility with Friendable
  179. *
  180. * Leave group when you unfriend \ElggGroup.
  181. *
  182. * @param int $friend_guid The GUID of the user leaving.
  183. *
  184. * @return bool
  185. * @deprecated 1.9 Use \ElggGroup::leave()
  186. */
  187. public function removeFriend($friend_guid) {
  188. elgg_deprecated_notice("\ElggGroup::removeFriend() is deprecated. Use \ElggGroup::leave()", 1.9);
  189. $user = get_user($friend_guid);
  190. return $user ? $this->leave($user) : false;
  191. }
  192. /**
  193. * For compatibility with Friendable
  194. *
  195. * Friending a group adds you as a member
  196. *
  197. * @return bool
  198. * @deprecated 1.9 Use \ElggGroup::isMember()
  199. */
  200. public function isFriend() {
  201. elgg_deprecated_notice("\ElggGroup::isFriend() is deprecated. Use \ElggGroup::isMember()", 1.9);
  202. return $this->isMember();
  203. }
  204. /**
  205. * For compatibility with Friendable
  206. *
  207. * @param int $user_guid The GUID of a user to check.
  208. *
  209. * @return bool
  210. * @deprecated 1.9 Use \ElggGroup::isMember()
  211. */
  212. public function isFriendsWith($user_guid) {
  213. elgg_deprecated_notice("\ElggGroup::isFriendsWith() is deprecated. Use \ElggGroup::isMember()", 1.9);
  214. $user = get_user($user_guid);
  215. return $user ? $this->isMember($user) : false;
  216. }
  217. /**
  218. * For compatibility with Friendable
  219. *
  220. * @param int $user_guid The GUID of a user to check.
  221. *
  222. * @return bool
  223. * @deprecated 1.9 Use \ElggGroup::isMember()
  224. */
  225. public function isFriendOf($user_guid) {
  226. elgg_deprecated_notice("\ElggGroup::isFriendOf() is deprecated. Use \ElggGroup::isMember()", 1.9);
  227. $user = get_user($user_guid);
  228. return $user ? $this->isMember($user) : false;
  229. }
  230. /**
  231. * For compatibility with Friendable
  232. *
  233. * @param string $subtype The GUID of a user to check.
  234. * @param int $limit Limit
  235. * @param int $offset Offset
  236. *
  237. * @return bool
  238. * @deprecated 1.9 Use \ElggGroup::getMembers()
  239. */
  240. public function getFriends($subtype = "", $limit = 10, $offset = 0) {
  241. elgg_deprecated_notice("\ElggGroup::getFriends() is deprecated. Use \ElggGroup::getMembers()", 1.9);
  242. return get_group_members($this->getGUID(), $limit, $offset);
  243. }
  244. /**
  245. * For compatibility with Friendable
  246. *
  247. * @param string $subtype The GUID of a user to check.
  248. * @param int $limit Limit
  249. * @param int $offset Offset
  250. *
  251. * @return bool
  252. * @deprecated 1.9 Use \ElggGroup::getMembers()
  253. */
  254. public function getFriendsOf($subtype = "", $limit = 10, $offset = 0) {
  255. elgg_deprecated_notice("\ElggGroup::getFriendsOf() is deprecated. Use \ElggGroup::getMembers()", 1.9);
  256. return get_group_members($this->getGUID(), $limit, $offset);
  257. }
  258. /**
  259. * Get objects contained in this group.
  260. *
  261. * @param string $subtype Entity subtype
  262. * @param int $limit Limit
  263. * @param int $offset Offset
  264. *
  265. * @return array|false
  266. * @deprecated 1.9 Use elgg_get_entities()
  267. */
  268. public function getObjects($subtype = "", $limit = 10, $offset = 0) {
  269. elgg_deprecated_notice("\ElggGroup::getObjects() is deprecated. Use elgg_get_entities()", 1.9);
  270. return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", $limit, $offset, false);
  271. }
  272. /**
  273. * For compatibility with Friendable
  274. *
  275. * @param string $subtype Entity subtype
  276. * @param int $limit Limit
  277. * @param int $offset Offset
  278. *
  279. * @return array|false
  280. * @deprecated 1.9 Use elgg_get_entities()
  281. */
  282. public function getFriendsObjects($subtype = "", $limit = 10, $offset = 0) {
  283. elgg_deprecated_notice("\ElggGroup::getFriendsObjects() is deprecated. Use elgg_get_entities()", 1.9);
  284. return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", $limit, $offset, false);
  285. }
  286. /**
  287. * For compatibility with Friendable
  288. *
  289. * @param string $subtype Subtype of entities
  290. *
  291. * @return array|false
  292. * @deprecated 1.9 Use elgg_get_entities()
  293. */
  294. public function countObjects($subtype = "") {
  295. elgg_deprecated_notice("\ElggGroup::countObjects() is deprecated. Use elgg_get_entities()", 1.9);
  296. return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", 10, 0, true);
  297. }
  298. /**
  299. * End friendable compatibility block
  300. */
  301. /**
  302. * Get an array of group members.
  303. *
  304. * @param array $options Options array. See elgg_get_entities_from_relationships
  305. * for a complete list. Common ones are 'limit', 'offset',
  306. * and 'count'. Options set automatically are 'relationship',
  307. * 'relationship_guid', 'inverse_relationship', and 'type'. This argument
  308. * used to set the limit (deprecated usage)
  309. * @param int $offset Offset (deprecated)
  310. * @param bool $count Count (deprecated)
  311. *
  312. * @return array
  313. */
  314. public function getMembers($options = array(), $offset = 0, $count = false) {
  315. if (!is_array($options)) {
  316. elgg_deprecated_notice('\ElggGroup::getMembers() takes an options array.', 1.9);
  317. $options = array(
  318. 'relationship' => 'member',
  319. 'relationship_guid' => $this->getGUID(),
  320. 'inverse_relationship' => true,
  321. 'type' => 'user',
  322. 'limit' => $options,
  323. 'offset' => $offset,
  324. 'count' => $count,
  325. );
  326. } else {
  327. $options['relationship'] = 'member';
  328. $options['relationship_guid'] = $this->getGUID();
  329. $options['inverse_relationship'] = true;
  330. $options['type'] = 'user';
  331. }
  332. return elgg_get_entities_from_relationship($options);
  333. }
  334. /**
  335. * Returns whether the current group has open membership or not.
  336. *
  337. * @return bool
  338. */
  339. public function isPublicMembership() {
  340. return ($this->membership == ACCESS_PUBLIC);
  341. }
  342. /**
  343. * Return the content access mode used by group_gatekeeper()
  344. *
  345. * @return string One of CONTENT_ACCESS_MODE_* constants
  346. * @access private
  347. * @since 1.9.0
  348. */
  349. public function getContentAccessMode() {
  350. $mode = $this->content_access_mode;
  351. if (!is_string($mode)) {
  352. // fallback to 1.8 default behavior
  353. if ($this->isPublicMembership()) {
  354. $mode = self::CONTENT_ACCESS_MODE_UNRESTRICTED;
  355. } else {
  356. $mode = self::CONTENT_ACCESS_MODE_MEMBERS_ONLY;
  357. }
  358. $this->content_access_mode = $mode;
  359. }
  360. // only support two modes for now
  361. if ($mode === self::CONTENT_ACCESS_MODE_MEMBERS_ONLY) {
  362. return $mode;
  363. }
  364. return self::CONTENT_ACCESS_MODE_UNRESTRICTED;
  365. }
  366. /**
  367. * Set the content access mode used by group_gatekeeper()
  368. *
  369. * @param string $mode One of CONTENT_ACCESS_MODE_* constants
  370. * @return void
  371. * @access private
  372. * @since 1.9.0
  373. */
  374. public function setContentAccessMode($mode) {
  375. // only support two modes for now
  376. if ($mode !== self::CONTENT_ACCESS_MODE_MEMBERS_ONLY) {
  377. $mode = self::CONTENT_ACCESS_MODE_UNRESTRICTED;
  378. }
  379. $this->content_access_mode = $mode;
  380. }
  381. /**
  382. * Is the given user a member of this group?
  383. *
  384. * @param \ElggUser $user The user. Default is logged in user.
  385. *
  386. * @return bool
  387. */
  388. public function isMember(\ElggUser $user = null) {
  389. if ($user == null) {
  390. $user = _elgg_services()->session->getLoggedInUser();
  391. }
  392. if (!$user) {
  393. return false;
  394. }
  395. $result = (bool)check_entity_relationship($user->guid, 'member', $this->guid);
  396. $params = array(
  397. 'user' => $user,
  398. 'group' => $this,
  399. );
  400. return _elgg_services()->hooks->trigger('is_member', 'group', $params, $result);
  401. }
  402. /**
  403. * Join a user to this group.
  404. *
  405. * @param \ElggUser $user User joining the group.
  406. *
  407. * @return bool Whether joining was successful.
  408. */
  409. public function join(\ElggUser $user) {
  410. $result = add_entity_relationship($user->guid, 'member', $this->guid);
  411. if ($result) {
  412. $params = array('group' => $this, 'user' => $user);
  413. _elgg_services()->events->trigger('join', 'group', $params);
  414. }
  415. return $result;
  416. }
  417. /**
  418. * Remove a user from the group.
  419. *
  420. * @param \ElggUser $user User to remove from the group.
  421. *
  422. * @return bool Whether the user was removed from the group.
  423. */
  424. public function leave(\ElggUser $user) {
  425. // event needs to be triggered while user is still member of group to have access to group acl
  426. $params = array('group' => $this, 'user' => $user);
  427. _elgg_services()->events->trigger('leave', 'group', $params);
  428. return remove_entity_relationship($user->guid, 'member', $this->guid);
  429. }
  430. /**
  431. * Load the \ElggGroup data from the database
  432. *
  433. * @param mixed $guid GUID of an \ElggGroup entity or database row from entity table
  434. *
  435. * @return bool
  436. */
  437. protected function load($guid) {
  438. $attr_loader = new \Elgg\AttributeLoader(get_class(), 'group', $this->attributes);
  439. $attr_loader->requires_access_control = !($this instanceof \ElggPlugin);
  440. $attr_loader->secondary_loader = 'get_group_entity_as_row';
  441. $attrs = $attr_loader->getRequiredAttributes($guid);
  442. if (!$attrs) {
  443. return false;
  444. }
  445. $this->attributes = $attrs;
  446. $this->tables_loaded = 2;
  447. $this->loadAdditionalSelectValues($attr_loader->getAdditionalSelectValues());
  448. _elgg_cache_entity($this);
  449. return true;
  450. }
  451. /**
  452. * {@inheritdoc}
  453. */
  454. protected function update() {
  455. global $CONFIG;
  456. if (!parent::update()) {
  457. return false;
  458. }
  459. $guid = (int)$this->guid;
  460. $name = sanitize_string($this->name);
  461. $description = sanitize_string($this->description);
  462. $query = "UPDATE {$CONFIG->dbprefix}groups_entity set"
  463. . " name='$name', description='$description' where guid=$guid";
  464. return $this->getDatabase()->updateData($query) !== false;
  465. }
  466. /**
  467. * {@inheritdoc}
  468. */
  469. protected function create() {
  470. global $CONFIG;
  471. $guid = parent::create();
  472. if (!$guid) {
  473. // @todo this probably means permission to create entity was denied
  474. // Is returning false the correct thing to do
  475. return false;
  476. }
  477. $name = sanitize_string($this->name);
  478. $description = sanitize_string($this->description);
  479. $query = "INSERT into {$CONFIG->dbprefix}groups_entity"
  480. . " (guid, name, description) values ($guid, '$name', '$description')";
  481. $result = $this->getDatabase()->insertData($query);
  482. if ($result === false) {
  483. // TODO(evan): Throw an exception here?
  484. return false;
  485. }
  486. return $guid;
  487. }
  488. /**
  489. * {@inheritdoc}
  490. */
  491. protected function prepareObject($object) {
  492. $object = parent::prepareObject($object);
  493. $object->name = $this->getDisplayName();
  494. $object->description = $this->description;
  495. unset($object->read_access);
  496. return $object;
  497. }
  498. // EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
  499. /**
  500. * Return an array of fields which can be exported.
  501. *
  502. * @return array
  503. * @deprecated 1.9 Use toObject()
  504. */
  505. public function getExportableValues() {
  506. return array_merge(parent::getExportableValues(), array(
  507. 'name',
  508. 'description',
  509. ));
  510. }
  511. /**
  512. * Can a user comment on this group?
  513. *
  514. * @see \ElggEntity::canComment()
  515. *
  516. * @param int $user_guid User guid (default is logged in user)
  517. * @return bool
  518. * @since 1.8.0
  519. */
  520. public function canComment($user_guid = 0) {
  521. $result = parent::canComment($user_guid);
  522. if ($result !== null) {
  523. return $result;
  524. }
  525. return false;
  526. }
  527. }