ElggSite.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. <?php
  2. /**
  3. * A Site entity.
  4. *
  5. * \ElggSite represents a single site entity.
  6. *
  7. * An \ElggSite object is an \ElggEntity child class with the subtype
  8. * of "site." It is created upon installation and holds information about a site:
  9. * - name
  10. * - description
  11. * - url
  12. *
  13. * Every \ElggEntity belongs to a site.
  14. *
  15. * @note Internal: \ElggSite represents a single row from the sites_entity
  16. * table, as well as the corresponding \ElggEntity row from the entities table.
  17. *
  18. * @warning Multiple site support isn't fully developed.
  19. *
  20. * @package Elgg.Core
  21. * @subpackage DataMode.Site
  22. * @link http://learn.elgg.org/en/stable/design/database.html
  23. *
  24. * @property string $name The name or title of the website
  25. * @property string $description A motto, mission statement, or description of the website
  26. * @property string $url The root web address for the site, including trailing slash
  27. */
  28. class ElggSite extends \ElggEntity {
  29. /**
  30. * Initialize the attributes array.
  31. * This is vital to distinguish between metadata and base attributes.
  32. *
  33. * @return void
  34. */
  35. protected function initializeAttributes() {
  36. parent::initializeAttributes();
  37. $this->attributes['type'] = "site";
  38. $this->attributes += self::getExternalAttributes();
  39. $this->tables_split = 2;
  40. }
  41. /**
  42. * Get default values for attributes stored in a separate table
  43. *
  44. * @return array
  45. * @access private
  46. *
  47. * @see \Elgg\Database\EntityTable::getEntities
  48. */
  49. final public static function getExternalAttributes() {
  50. return [
  51. 'name' => null,
  52. 'description' => null,
  53. 'url' => null,
  54. ];
  55. }
  56. /**
  57. * Create a new \ElggSite.
  58. *
  59. * Plugin developers should only use the constructor to create a new entity.
  60. * To retrieve entities, use get_entity() and the elgg_get_entities* functions.
  61. *
  62. * @param \stdClass $row Database row result. Default is null to create a new site.
  63. *
  64. * @throws IOException If cannot load remaining data from db
  65. * @throws InvalidParameterException If not passed a db result
  66. */
  67. public function __construct($row = null) {
  68. $this->initializeAttributes();
  69. // compatibility for 1.7 api.
  70. $this->initialise_attributes(false);
  71. if (!empty($row)) {
  72. // Is $row is a DB entity table row
  73. if ($row instanceof \stdClass) {
  74. // Load the rest
  75. if (!$this->load($row)) {
  76. $msg = "Failed to load new " . get_class() . " for GUID:" . $row->guid;
  77. throw new \IOException($msg);
  78. }
  79. } else if ($row instanceof \ElggSite) {
  80. // $row is an \ElggSite so this is a copy constructor
  81. elgg_deprecated_notice('This type of usage of the \ElggSite constructor was deprecated. Please use the clone method.', 1.7);
  82. foreach ($row->attributes as $key => $value) {
  83. $this->attributes[$key] = $value;
  84. }
  85. } else if (strpos($row, "http") !== false) {
  86. // url so retrieve by url
  87. elgg_deprecated_notice("Passing URL to constructor is deprecated. Use get_site_by_url()", 1.9);
  88. $row = get_site_by_url($row);
  89. foreach ($row->attributes as $key => $value) {
  90. $this->attributes[$key] = $value;
  91. }
  92. } else if (is_numeric($row)) {
  93. // $row is a GUID so load
  94. elgg_deprecated_notice('Passing a GUID to constructor is deprecated. Use get_entity()', 1.9);
  95. if (!$this->load($row)) {
  96. throw new \IOException("Failed to load new " . get_class() . " from GUID:" . $row);
  97. }
  98. } else {
  99. throw new \InvalidParameterException("Unrecognized value passed to constuctor.");
  100. }
  101. }
  102. }
  103. /**
  104. * Loads the full \ElggSite when given a guid.
  105. *
  106. * @param mixed $guid GUID of \ElggSite entity or database row object
  107. *
  108. * @return bool
  109. * @throws InvalidClassException
  110. */
  111. protected function load($guid) {
  112. $attr_loader = new \Elgg\AttributeLoader(get_class(), 'site', $this->attributes);
  113. $attr_loader->requires_access_control = !($this instanceof \ElggPlugin);
  114. $attr_loader->secondary_loader = 'get_site_entity_as_row';
  115. $attrs = $attr_loader->getRequiredAttributes($guid);
  116. if (!$attrs) {
  117. return false;
  118. }
  119. $this->attributes = $attrs;
  120. $this->tables_loaded = 2;
  121. $this->loadAdditionalSelectValues($attr_loader->getAdditionalSelectValues());
  122. _elgg_cache_entity($this);
  123. return true;
  124. }
  125. /**
  126. * {@inheritdoc}
  127. */
  128. protected function create() {
  129. global $CONFIG;
  130. $guid = parent::create();
  131. $name = sanitize_string($this->attributes['name']);
  132. $description = sanitize_string($this->attributes['description']);
  133. $url = sanitize_string($this->attributes['url']);
  134. $query = "INSERT into {$CONFIG->dbprefix}sites_entity
  135. (guid, name, description, url) values ($guid, '$name', '$description', '$url')";
  136. $result = $this->getDatabase()->insertData($query);
  137. if ($result === false) {
  138. // TODO(evan): Throw an exception here?
  139. return false;
  140. }
  141. // make sure the site guid is set to self if not already set
  142. if (!$this->site_guid) {
  143. $this->site_guid = $guid;
  144. $this->getDatabase()->updateData("UPDATE {$CONFIG->dbprefix}entities
  145. SET site_guid = $guid WHERE guid = $guid");
  146. }
  147. return $guid;
  148. }
  149. /**
  150. * {@inheritdoc}
  151. */
  152. protected function update() {
  153. global $CONFIG;
  154. if (!parent::update()) {
  155. return false;
  156. }
  157. $guid = (int)$this->guid;
  158. $name = sanitize_string($this->name);
  159. $description = sanitize_string($this->description);
  160. $url = sanitize_string($this->url);
  161. $query = "UPDATE {$CONFIG->dbprefix}sites_entity
  162. SET name='$name', description='$description', url='$url' WHERE guid=$guid";
  163. return $this->getDatabase()->updateData($query) !== false;
  164. }
  165. /**
  166. * Delete the site.
  167. *
  168. * @note You cannot delete the current site.
  169. *
  170. * @return bool
  171. * @throws SecurityException
  172. */
  173. public function delete() {
  174. global $CONFIG;
  175. if ($CONFIG->site->getGUID() == $this->guid) {
  176. throw new \SecurityException('You cannot delete the current site');
  177. }
  178. return parent::delete();
  179. }
  180. /**
  181. * Disable the site
  182. *
  183. * @note You cannot disable the current site.
  184. *
  185. * @param string $reason Optional reason for disabling
  186. * @param bool $recursive Recursively disable all contained entities?
  187. *
  188. * @return bool
  189. * @throws SecurityException
  190. */
  191. public function disable($reason = "", $recursive = true) {
  192. global $CONFIG;
  193. if ($CONFIG->site->getGUID() == $this->guid) {
  194. throw new \SecurityException('You cannot disable the current site');
  195. }
  196. return parent::disable($reason, $recursive);
  197. }
  198. /**
  199. * Returns the URL for this site
  200. *
  201. * @return string The URL
  202. */
  203. public function getURL() {
  204. return $this->url;
  205. }
  206. /**
  207. * {@inheritdoc}
  208. */
  209. public function getDisplayName() {
  210. return $this->name;
  211. }
  212. /**
  213. * {@inheritdoc}
  214. */
  215. public function setDisplayName($displayName) {
  216. $this->name = $displayName;
  217. }
  218. /**
  219. * Gets an array of \ElggUser entities who are members of the site.
  220. *
  221. * @param array $options An associative array for key => value parameters
  222. * accepted by elgg_get_entities(). Common parameters
  223. * include 'limit', and 'offset'.
  224. * Note: this was $limit before version 1.8
  225. * @param int $offset Offset @deprecated parameter
  226. *
  227. * @return array of \ElggUsers
  228. * @deprecated 1.9 Use \ElggSite::getEntities()
  229. */
  230. public function getMembers($options = array(), $offset = 0) {
  231. elgg_deprecated_notice('\ElggSite::getMembers() is deprecated. Use \ElggSite::getEntities()', 1.9);
  232. if (!is_array($options)) {
  233. elgg_deprecated_notice("\ElggSite::getMembers uses different arguments!", 1.8);
  234. $options = array(
  235. 'limit' => $options,
  236. 'offset' => $offset,
  237. );
  238. }
  239. $defaults = array(
  240. 'site_guids' => ELGG_ENTITIES_ANY_VALUE,
  241. 'relationship' => 'member_of_site',
  242. 'relationship_guid' => $this->getGUID(),
  243. 'inverse_relationship' => true,
  244. 'type' => 'user',
  245. );
  246. $options = array_merge($defaults, $options);
  247. return elgg_get_entities_from_relationship($options);
  248. }
  249. /**
  250. * List the members of this site
  251. *
  252. * @param array $options An associative array for key => value parameters
  253. * accepted by elgg_list_entities(). Common parameters
  254. * include 'full_view', 'limit', and 'offset'.
  255. *
  256. * @return string
  257. * @since 1.8.0
  258. * @deprecated 1.9 Use elgg_list_entities_from_relationship()
  259. */
  260. public function listMembers($options = array()) {
  261. elgg_deprecated_notice('\ElggSite::listMembers() is deprecated. Use elgg_list_entities_from_relationship()', 1.9);
  262. $defaults = array(
  263. 'site_guids' => ELGG_ENTITIES_ANY_VALUE,
  264. 'relationship' => 'member_of_site',
  265. 'relationship_guid' => $this->getGUID(),
  266. 'inverse_relationship' => true,
  267. 'type' => 'user',
  268. );
  269. $options = array_merge($defaults, $options);
  270. return elgg_list_entities_from_relationship($options);
  271. }
  272. /**
  273. * Adds an entity to the site.
  274. *
  275. * This adds a 'member_of_site' relationship between between the entity and
  276. * the site. It does not change the site_guid of the entity.
  277. *
  278. * @param \ElggEntity $entity User, group, or object entity
  279. *
  280. * @return bool
  281. */
  282. public function addEntity(\ElggEntity $entity) {
  283. if (elgg_instanceof($entity, 'site')) {
  284. return false;
  285. }
  286. return add_entity_relationship($entity->guid, "member_of_site", $this->guid);
  287. }
  288. /**
  289. * Removes an entity from this site
  290. *
  291. * @param \ElggEntity $entity User, group, or object entity
  292. *
  293. * @return bool
  294. */
  295. public function removeEntity($entity) {
  296. if (elgg_instanceof($entity, 'site')) {
  297. return false;
  298. }
  299. return remove_entity_relationship($entity->guid, "member_of_site", $this->guid);
  300. }
  301. /**
  302. * Get an array of entities that belong to the site.
  303. *
  304. * This only returns entities that have been explicitly added to the
  305. * site through addEntity().
  306. *
  307. * @param array $options Options array for elgg_get_entities_from_relationship()
  308. * Parameters set automatically by this method:
  309. * 'relationship', 'relationship_guid', 'inverse_relationship'
  310. * @return array
  311. */
  312. public function getEntities(array $options = array()) {
  313. $options['relationship'] = 'member_of_site';
  314. $options['relationship_guid'] = $this->guid;
  315. $options['inverse_relationship'] = true;
  316. if (!isset($options['site_guid']) || !isset($options['site_guids'])) {
  317. $options['site_guids'] = ELGG_ENTITIES_ANY_VALUE;
  318. }
  319. return elgg_get_entities_from_relationship($options);
  320. }
  321. /**
  322. * Adds a user to the site.
  323. *
  324. * @param int $user_guid GUID
  325. *
  326. * @return bool
  327. * @deprecated 1.9 Use \ElggSite::addEntity()
  328. */
  329. public function addUser($user_guid) {
  330. elgg_deprecated_notice('\ElggSite::addUser() is deprecated. Use \ElggEntity::addEntity()', 1.9);
  331. return add_site_user($this->getGUID(), $user_guid);
  332. }
  333. /**
  334. * Removes a user from the site.
  335. *
  336. * @param int $user_guid GUID
  337. *
  338. * @return bool
  339. * @deprecated 1.9 Use \ElggSite::removeEntity()
  340. */
  341. public function removeUser($user_guid) {
  342. elgg_deprecated_notice('\ElggSite::removeUser() is deprecated. Use \ElggEntity::removeEntity()', 1.9);
  343. return remove_site_user($this->getGUID(), $user_guid);
  344. }
  345. /**
  346. * Returns an array of \ElggObject entities that belong to the site.
  347. *
  348. * @warning This only returns objects that have been explicitly added to the
  349. * site through addObject()
  350. *
  351. * @param string $subtype Entity subtype
  352. * @param int $limit Limit
  353. * @param int $offset Offset
  354. *
  355. * @return array
  356. * @deprecated 1.9 Use \ElggSite:getEntities()
  357. */
  358. public function getObjects($subtype = "", $limit = 10, $offset = 0) {
  359. elgg_deprecated_notice('\ElggSite::getObjects() is deprecated. Use \ElggSite::getEntities()', 1.9);
  360. return get_site_objects($this->getGUID(), $subtype, $limit, $offset);
  361. }
  362. /**
  363. * Adds an object to the site.
  364. *
  365. * @param int $object_guid GUID
  366. *
  367. * @return bool
  368. * @deprecated 1.9 Use \ElggSite::addEntity()
  369. */
  370. public function addObject($object_guid) {
  371. elgg_deprecated_notice('\ElggSite::addObject() is deprecated. Use \ElggEntity::addEntity()', 1.9);
  372. return add_site_object($this->getGUID(), $object_guid);
  373. }
  374. /**
  375. * Remvoes an object from the site.
  376. *
  377. * @param int $object_guid GUID
  378. *
  379. * @return bool
  380. * @deprecated 1.9 Use \ElggSite::removeEntity()
  381. */
  382. public function removeObject($object_guid) {
  383. elgg_deprecated_notice('\ElggSite::removeObject() is deprecated. Use \ElggEntity::removeEntity()', 1.9);
  384. return remove_site_object($this->getGUID(), $object_guid);
  385. }
  386. /**
  387. * Get the collections associated with a site.
  388. *
  389. * @param string $subtype Subtype
  390. * @param int $limit Limit
  391. * @param int $offset Offset
  392. *
  393. * @return mixed
  394. * @deprecated 1.8 Was never implemented
  395. */
  396. public function getCollections($subtype = "", $limit = 10, $offset = 0) {
  397. elgg_deprecated_notice("ElggSite::getCollections() is deprecated", 1.8);
  398. get_site_collections($this->getGUID(), $subtype, $limit, $offset);
  399. }
  400. /**
  401. * {@inheritdoc}
  402. */
  403. protected function prepareObject($object) {
  404. $object = parent::prepareObject($object);
  405. $object->name = $this->getDisplayName();
  406. $object->description = $this->description;
  407. unset($object->read_access);
  408. return $object;
  409. }
  410. /*
  411. * EXPORTABLE INTERFACE
  412. */
  413. /**
  414. * Return an array of fields which can be exported.
  415. *
  416. * @return array
  417. * @deprecated 1.9 Use toObject()
  418. */
  419. public function getExportableValues() {
  420. return array_merge(parent::getExportableValues(), array(
  421. 'name',
  422. 'description',
  423. 'url',
  424. ));
  425. }
  426. /**
  427. * Get the domain for this site
  428. *
  429. * @return string
  430. * @since 1.9
  431. */
  432. public function getDomain() {
  433. $breakdown = parse_url($this->url);
  434. return $breakdown['host'];
  435. }
  436. /**
  437. * Halts bootup and redirects to the site front page
  438. * if site is in walled garden mode, no user is logged in,
  439. * and the URL is not a public page.
  440. *
  441. * @return void
  442. * @since 1.8.0
  443. */
  444. public function checkWalledGarden() {
  445. global $CONFIG;
  446. // command line calls should not invoke the walled garden check
  447. if (PHP_SAPI === 'cli') {
  448. return;
  449. }
  450. if ($CONFIG->walled_garden) {
  451. if ($CONFIG->default_access == ACCESS_PUBLIC) {
  452. $CONFIG->default_access = ACCESS_LOGGED_IN;
  453. }
  454. _elgg_services()->hooks->registerHandler(
  455. 'access:collections:write',
  456. 'all',
  457. '_elgg_walled_garden_remove_public_access',
  458. 9999);
  459. if (!_elgg_services()->session->isLoggedIn()) {
  460. // override the front page
  461. elgg_register_page_handler('', '_elgg_walled_garden_index');
  462. if (!$this->isPublicPage()) {
  463. if (!elgg_is_xhr()) {
  464. _elgg_services()->session->set('last_forward_from', current_page_url());
  465. }
  466. register_error(_elgg_services()->translator->translate('loggedinrequired'));
  467. forward('', 'walled_garden');
  468. }
  469. }
  470. }
  471. }
  472. /**
  473. * Returns if a URL is public for this site when in Walled Garden mode.
  474. *
  475. * Pages are registered to be public by {@elgg_plugin_hook public_pages walled_garden}.
  476. *
  477. * @param string $url Defaults to the current URL.
  478. *
  479. * @return bool
  480. * @since 1.8.0
  481. */
  482. public function isPublicPage($url = '') {
  483. global $CONFIG;
  484. if (empty($url)) {
  485. $url = current_page_url();
  486. // do not check against URL queries
  487. if ($pos = strpos($url, '?')) {
  488. $url = substr($url, 0, $pos);
  489. }
  490. }
  491. // always allow index page
  492. if ($url == _elgg_services()->config->getSiteUrl($this->guid)) {
  493. return true;
  494. }
  495. // default public pages
  496. $defaults = array(
  497. 'walled_garden/.*',
  498. 'action/.*',
  499. 'login',
  500. 'register',
  501. 'forgotpassword',
  502. 'changepassword',
  503. 'refresh_token',
  504. 'ajax/view/js/languages',
  505. 'upgrade\.php',
  506. 'css/.*',
  507. 'js/.*',
  508. 'cache/[0-9]+/\w+/js|css/.*',
  509. 'cron/.*',
  510. 'services/.*',
  511. );
  512. // include a hook for plugin authors to include public pages
  513. $plugins = _elgg_services()->hooks->trigger('public_pages', 'walled_garden', null, array());
  514. // allow public pages
  515. foreach (array_merge($defaults, $plugins) as $public) {
  516. $pattern = "`^{$CONFIG->url}$public/*$`i";
  517. if (preg_match($pattern, $url)) {
  518. return true;
  519. }
  520. }
  521. // non-public page
  522. return false;
  523. }
  524. }