Example #1
0
 /**
  * Constructor
  *
  * @param EntityCache $entity_cache Entity cache
  * @param EntityTable $entity_table Entity service
  */
 public function __construct(EntityCache $entity_cache, EntityTable $entity_table)
 {
     $this->_callable_cache_checker = function ($guid) use($entity_cache) {
         return $entity_cache->get($guid);
     };
     $this->_callable_entity_loader = function ($options) use($entity_table) {
         return $entity_table->getEntities($options);
     };
 }
Example #2
0
 /**
  * {@inheritdoc}
  */
 public function updateRow($guid, stdClass $row)
 {
     $attributes = array_merge((array) $this->rows[$guid], (array) $row);
     // Rebuild query specs for the udpated row
     $this->addQuerySpecs((object) $attributes);
     return parent::updateRow($guid, $row);
 }
Example #3
0
 /**
  * Deletes all private settings for an entity
  *
  * @param int $entity_guid The Entity GUID
  * @return bool
  */
 function removeAllForEntity($entity_guid)
 {
     $entity_guid = (int) $entity_guid;
     $entity = $this->entities->get($entity_guid);
     if (!$entity instanceof \ElggEntity) {
         return false;
     }
     return $this->db->deleteData("DELETE FROM {$this->table}\n\t\t\tWHERE entity_guid = {$entity_guid}");
 }
Example #4
0
 /**
  * Returns entities based upon metadata.  Also accepts all
  * options available to elgg_get_entities().  Supports
  * the singular option shortcut.
  *
  * @note Using metadata_names and metadata_values results in a
  * "names IN (...) AND values IN (...)" clause.  This is subtly
  * differently than default multiple metadata_name_value_pairs, which use
  * "(name = value) AND (name = value)" clauses.
  *
  * When in doubt, use name_value_pairs.
  *
  * To ask for entities that do not have a metadata value, use a custom
  * where clause like this:
  *
  * 	$options['wheres'][] = "NOT EXISTS (
  *			SELECT 1 FROM {$dbprefix}metadata md
  *			WHERE md.entity_guid = e.guid
  *				AND md.name_id = $name_metastring_id
  *				AND md.value_id = $value_metastring_id)";
  *
  * Note the metadata name and value has been denormalized in the above example.
  *
  * @see elgg_get_entities
  *
  * @param array $options Array in format:
  *
  * 	metadata_names => null|ARR metadata names
  *
  * 	metadata_values => null|ARR metadata values
  *
  * 	metadata_name_value_pairs => null|ARR (
  *                                         name => 'name',
  *                                         value => 'value',
  *                                         'operand' => '=',
  *                                         'case_sensitive' => true
  *                                        )
  *                               Currently if multiple values are sent via
  *                               an array (value => array('value1', 'value2')
  *                               the pair's operand will be forced to "IN".
  *                               If passing "IN" as the operand and a string as the value, 
  *                               the value must be a properly quoted and escaped string.
  *
  * 	metadata_name_value_pairs_operator => null|STR The operator to use for combining
  *                                        (name = value) OPERATOR (name = value); default AND
  *
  * 	metadata_case_sensitive => BOOL Overall Case sensitive
  *
  *  order_by_metadata => null|ARR array(
  *                                      'name' => 'metadata_text1',
  *                                      'direction' => ASC|DESC,
  *                                      'as' => text|integer
  *                                     )
  *                                Also supports array('name' => 'metadata_text1')
  *
  *  metadata_owner_guids => null|ARR guids for metadata owners
  *
  * @return \ElggEntity[]|mixed If count, int. If not count, array. false on errors.
  */
 function getEntities(array $options = array())
 {
     $defaults = array('metadata_names' => ELGG_ENTITIES_ANY_VALUE, 'metadata_values' => ELGG_ENTITIES_ANY_VALUE, 'metadata_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE, 'metadata_name_value_pairs_operator' => 'AND', 'metadata_case_sensitive' => true, 'order_by_metadata' => array(), 'metadata_owner_guids' => ELGG_ENTITIES_ANY_VALUE);
     $options = array_merge($defaults, $options);
     $singulars = array('metadata_name', 'metadata_value', 'metadata_name_value_pair', 'metadata_owner_guid');
     $options = _elgg_normalize_plural_options_array($options, $singulars);
     if (!($options = _elgg_entities_get_metastrings_options('metadata', $options))) {
         return false;
     }
     return $this->entityTable->getEntities($options);
 }
Example #5
0
 /**
  * Sets a private setting for an entity.
  *
  * @param int    $entity_guid The entity GUID
  * @param string $name        The name of the setting
  * @param string $value       The value of the setting
  * @return bool
  */
 public function set($entity_guid, $name, $value)
 {
     $this->cache->clear($entity_guid);
     _elgg_services()->boot->invalidateCache();
     if (!$this->entities->exists($entity_guid)) {
         return false;
     }
     $query = "\n\t\t\tINSERT into {$this->table}\n\t\t\t(entity_guid, name, value) VALUES\n\t\t\t(:entity_guid, :name, :value)\n\t\t\tON DUPLICATE KEY UPDATE value = :value\n\t\t";
     $params = [':entity_guid' => (int) $entity_guid, ':name' => (string) $name, ':value' => (string) $value];
     $result = $this->db->insertData($query, $params);
     return $result !== false;
 }
 /**
  * Send a notification to a subscriber
  *
  * @param \Elgg\Notifications\Event $event  The notification event
  * @param int                      $guid   The guid of the subscriber
  * @param string                   $method The notification method
  * @return bool
  * @access private
  */
 protected function sendNotification(\Elgg\Notifications\Event $event, $guid, $method)
 {
     $recipient = $this->entities->get($guid, 'user');
     /* @var \ElggUser $recipient */
     if (!$recipient || $recipient->isBanned()) {
         return false;
     }
     // don't notify the creator of the content
     if ($recipient->getGUID() == $event->getActorGUID()) {
         return false;
     }
     $actor = $event->getActor();
     $object = $event->getObject();
     if (!$actor || !$object) {
         return false;
     }
     if ($object instanceof ElggEntity && !has_access_to_entity($object, $recipient)) {
         return false;
     }
     $language = $recipient->language;
     $params = array('event' => $event, 'method' => $method, 'recipient' => $recipient, 'language' => $language, 'object' => $object);
     $subject = $this->getNotificationSubject($event, $recipient);
     $body = $this->getNotificationBody($event, $recipient);
     $notification = new \Elgg\Notifications\Notification($event->getActor(), $recipient, $language, $subject, $body, '', $params);
     $type = 'notification:' . $event->getDescription();
     if ($this->hooks->hasHandler('prepare', $type)) {
         $notification = $this->hooks->trigger('prepare', $type, $params, $notification);
     } else {
         // pre Elgg 1.9 notification message generation
         $notification = $this->getDeprecatedNotificationBody($notification, $event, $method);
     }
     if ($this->hooks->hasHandler('send', "notification:{$method}")) {
         // return true to indicate the notification has been sent
         $params = array('notification' => $notification, 'event' => $event);
         return $this->hooks->trigger('send', "notification:{$method}", $params, false);
     } else {
         // pre Elgg 1.9 notification handler
         $userGuid = $notification->getRecipientGUID();
         $senderGuid = $notification->getSenderGUID();
         $subject = $notification->subject;
         $body = $notification->body;
         $params = $notification->params;
         return (bool) _elgg_notify_user($userGuid, $senderGuid, $subject, $body, $params, array($method));
     }
 }
Example #7
0
 /**
  * Can the user change this access collection?
  *
  * Use the plugin hook of 'access:collections:write', 'user' to change this.
  * @see get_write_access_array() for details on the hook.
  *
  * Respects access control disabling for admin users and {@link elgg_set_ignore_access()}
  *
  * @see get_write_access_array()
  *
  * @param int   $collection_id The collection id
  * @param mixed $user_guid     The user GUID to check for. Defaults to logged in user.
  * @return bool
  */
 function canEdit($collection_id, $user_guid = null)
 {
     try {
         $user = $this->entities->getUserForPermissionsCheck($user_guid);
     } catch (UserFetchFailureException $e) {
         return false;
     }
     $collection = $this->get($collection_id);
     if (!$user || !$collection) {
         return false;
     }
     $write_access = $this->getWriteAccessArray($user->guid, true);
     // don't ignore access when checking users.
     if ($user_guid) {
         return array_key_exists($collection_id, $write_access);
     } else {
         return elgg_get_ignore_access() || array_key_exists($collection_id, $write_access);
     }
 }
Example #8
0
 /**
  * Handle request to /serve-icon handler
  *
  * @param bool $allow_removing_headers Alter PHP's global headers to allow caching
  * @return BinaryFileResponse
  */
 public function handleServeIconRequest($allow_removing_headers = true)
 {
     $response = new Response();
     $response->setExpires($this->getCurrentTime('-1 day'));
     $response->prepare($this->request);
     if ($allow_removing_headers) {
         // clear cache-boosting headers set by PHP session
         header_remove('Cache-Control');
         header_remove('Pragma');
         header_remove('Expires');
     }
     $path = implode('/', $this->request->getUrlSegments());
     if (!preg_match('~serve-icon/(\\d+)/(.*+)$~', $path, $m)) {
         return $response->setStatusCode(400)->setContent('Malformatted request URL');
     }
     list(, $guid, $size) = $m;
     $entity = $this->entities->get($guid);
     if (!$entity instanceof \ElggEntity) {
         return $response->setStatusCode(404)->setContent('Item does not exist');
     }
     $thumbnail = $entity->getIcon($size);
     if (!$thumbnail->exists()) {
         return $response->setStatusCode(404)->setContent('Icon does not exist');
     }
     $if_none_match = $this->request->headers->get('if_none_match');
     if (!empty($if_none_match)) {
         // strip mod_deflate suffixes
         $this->request->headers->set('if_none_match', str_replace('-gzip', '', $if_none_match));
     }
     $filenameonfilestore = $thumbnail->getFilenameOnFilestore();
     $last_updated = filemtime($filenameonfilestore);
     $etag = '"' . $last_updated . '"';
     $response->setPrivate()->setEtag($etag)->setExpires($this->getCurrentTime('+1 day'))->setMaxAge(86400);
     if ($response->isNotModified($this->request)) {
         return $response;
     }
     $headers = ['Content-Type' => (new MimeTypeDetector())->getType($filenameonfilestore)];
     $response = new BinaryFileResponse($filenameonfilestore, 200, $headers, false, 'inline');
     $response->prepare($this->request);
     $response->setPrivate()->setEtag($etag)->setExpires($this->getCurrentTime('+1 day'))->setMaxAge(86400);
     return $response;
 }
Example #9
0
 /**
  * Can a user annotate an entity?
  *
  * @tip Can be overridden by registering for the plugin hook [permissions_check:annotate:<name>,
  * <entity type>] or [permissions_check:annotate, <entity type>]. The hooks are called in that order.
  *
  * @tip If you want logged out users to annotate an object, do not call
  * canAnnotate(). It's easier than using the plugin hook.
  *
  * @param ElggEntity $entity          Objet entity
  * @param int        $user_guid       User guid (default is logged in user)
  * @param string     $annotation_name The name of the annotation (default is unspecified)
  *
  * @return bool
  */
 public function canAnnotate(ElggEntity $entity, $user_guid = 0, $annotation_name = '')
 {
     if ($annotation_name === null || $annotation_name === false) {
         // accepting these for BC
         $annotation_name = '';
     } elseif (!is_string($annotation_name)) {
         throw new InvalidArgumentException(__METHOD__ . ' expects \\$annotation_name to be a string');
     }
     try {
         $user = $this->entities->getUserForPermissionsCheck($user_guid);
     } catch (UserFetchFailureException $e) {
         return false;
     }
     $return = (bool) $user;
     $params = ['entity' => $entity, 'user' => $user, 'annotation_name' => $annotation_name];
     if (!empty($annotation_name)) {
         $return = $this->hooks->trigger("permissions_check:annotate:{$annotation_name}", $entity->getType(), $params, $return);
     }
     return $this->hooks->trigger('permissions_check:annotate', $entity->getType(), $params, $return);
 }
Example #10
0
 /**
  * Return entities matching a given query joining against a relationship.
  * Also accepts all options available to elgg_get_entities() and
  * elgg_get_entities_from_metadata().
  *
  * To ask for entities that do not have a particular relationship to an entity,
  * use a custom where clause like the following:
  *
  * 	$options['wheres'][] = "NOT EXISTS (
  *			SELECT 1 FROM {$db_prefix}entity_relationships
  *				WHERE guid_one = e.guid
  *				AND relationship = '$relationship'
  *		)";
  *
  * @see elgg_get_entities
  * @see elgg_get_entities_from_metadata
  *
  * @param array $options Array in format:
  *
  *  relationship => null|STR Type of the relationship. E.g. "member"
  *
  *  relationship_guid => null|INT GUID of the subject of the relationship, unless "inverse_relationship" is set
  *                                to true, in which case this will specify the target.
  *
  *  inverse_relationship => false|BOOL Are we searching for relationship subjects? By default, the query finds
  *                                     targets of relationships.
  *
  *  relationship_join_on => null|STR How the entities relate: guid (default), container_guid, or owner_guid
  *                                   Examples using the relationship 'friend':
  *                                   1. use 'guid' if you want the user's friends
  *                                   2. use 'owner_guid' if you want the entities the user's friends own
  *                                      (including in groups)
  *                                   3. use 'container_guid' if you want the entities in the user's personal
  *                                      space (non-group)
  *
  * 	relationship_created_time_lower => null|INT Relationship created time lower boundary in epoch time
  *
  * 	relationship_created_time_upper => null|INT Relationship created time upper boundary in epoch time
  *
  * @return \ElggEntity[]|mixed If count, int. If not count, array. false on errors.
  */
 public function getEntities($options)
 {
     $defaults = array('relationship' => null, 'relationship_guid' => null, 'inverse_relationship' => false, 'relationship_join_on' => 'guid', 'relationship_created_time_lower' => ELGG_ENTITIES_ANY_VALUE, 'relationship_created_time_upper' => ELGG_ENTITIES_ANY_VALUE);
     $options = array_merge($defaults, $options);
     $join_column = "e.{$options['relationship_join_on']}";
     $clauses = $this->getEntityRelationshipWhereSql($join_column, $options['relationship'], $options['relationship_guid'], $options['inverse_relationship']);
     if ($clauses) {
         // merge wheres to pass to get_entities()
         if (isset($options['wheres']) && !is_array($options['wheres'])) {
             $options['wheres'] = array($options['wheres']);
         } elseif (!isset($options['wheres'])) {
             $options['wheres'] = array();
         }
         $options['wheres'] = array_merge($options['wheres'], $clauses['wheres']);
         // limit based on time created
         $time_wheres = $this->entities->getEntityTimeWhereSql('r', $options['relationship_created_time_upper'], $options['relationship_created_time_lower']);
         if ($time_wheres) {
             $options['wheres'] = array_merge($options['wheres'], array($time_wheres));
         }
         // merge joins to pass to get_entities()
         if (isset($options['joins']) && !is_array($options['joins'])) {
             $options['joins'] = array($options['joins']);
         } elseif (!isset($options['joins'])) {
             $options['joins'] = array();
         }
         $options['joins'] = array_merge($options['joins'], $clauses['joins']);
         if (isset($options['selects']) && !is_array($options['selects'])) {
             $options['selects'] = array($options['selects']);
         } elseif (!isset($options['selects'])) {
             $options['selects'] = array();
         }
         $select = array('r.id');
         $options['selects'] = array_merge($options['selects'], $select);
         if (!isset($options['group_by'])) {
             $options['group_by'] = $clauses['group_by'];
         }
     }
     return $this->metadata->getEntities($options);
 }
Example #11
0
 /**
  * Sets the last action time of the given user to right now.
  *
  * @see _elgg_session_boot The session boot calls this at the beginning of every request
  *
  * @param ElggUser $user User entity
  * @return void
  */
 public function setLastAction(ElggUser $user)
 {
     $time = $this->getCurrentTime()->getTimestamp();
     if ($user->last_action == $time) {
         // no change required
         return;
     }
     $query = "\n\t\t\tUPDATE {$this->table}\n\t\t\tSET\n\t\t\t\tprev_last_action = last_action,\n\t\t\t\tlast_action = :last_action\n\t\t\tWHERE guid = :guid\n\t\t";
     $params = [':last_action' => $time, ':guid' => (int) $user->guid];
     $user->prev_last_action = $user->last_action;
     $user->last_action = $time;
     execute_delayed_write_query($query, null, $params);
     $this->entity_cache->set($user);
     // If we save the user to memcache during this request, then we'll end up with the
     // old (incorrect) attributes cached (notice the above query is delayed). So it's
     // simplest to just resave the user after all plugin code runs.
     register_shutdown_function(function () use($user, $time) {
         $this->entities->updateLastAction($user, $time);
         // keep entity table in sync
         $user->storeInPersistedCache(_elgg_get_memcache('new_entity_cache'), $time);
     });
 }
Example #12
0
 /**
  * Send a notification to a subscriber
  *
  * @param NotificationEvent $event  The notification event
  * @param int               $guid   The guid of the subscriber
  * @param string            $method The notification method
  * @param array             $params Default notification params
  * @return bool
  * @access private
  */
 protected function sendNotification(NotificationEvent $event, $guid, $method, array $params = [])
 {
     $actor = $event->getActor();
     $object = $event->getObject();
     if ($event instanceof InstantNotificationEvent) {
         $recipient = $this->entities->get($guid);
         /* @var \ElggEntity $recipient */
         $subject = elgg_extract('subject', $params, '');
         $body = elgg_extract('body', $params, '');
         $summary = elgg_extract('summary', $params, '');
     } else {
         $recipient = $this->entities->get($guid, 'user');
         /* @var \ElggUser $recipient */
         if (!$recipient || $recipient->isBanned()) {
             return false;
         }
         if ($recipient->getGUID() == $event->getActorGUID()) {
             // Content creators should not be receiving subscription
             // notifications about their own content
             return false;
         }
         if (!$actor || !$object) {
             return false;
         }
         if ($object instanceof ElggEntity && !has_access_to_entity($object, $recipient)) {
             // Recipient does not have access to the notification object
             // The access level may have changed since the event was enqueued
             return false;
         }
         $subject = $this->getNotificationSubject($event, $recipient);
         $body = $this->getNotificationBody($event, $recipient);
         $summary = '';
         $params['origin'] = Notification::ORIGIN_SUBSCRIPTIONS;
     }
     $language = $recipient->language;
     $params['event'] = $event;
     $params['method'] = $method;
     $params['sender'] = $actor;
     $params['recipient'] = $recipient;
     $params['language'] = $language;
     $params['object'] = $object;
     $params['action'] = $event->getAction();
     $notification = new Notification($actor, $recipient, $language, $subject, $body, $summary, $params);
     $notification = $this->hooks->trigger('prepare', 'notification', $params, $notification);
     if (!$notification instanceof Notification) {
         throw new RuntimeException("'prepare','notification' hook must return an instance of " . Notification::class);
     }
     $type = 'notification:' . $event->getDescription();
     if ($this->hooks->hasHandler('prepare', $type)) {
         $notification = $this->hooks->trigger('prepare', $type, $params, $notification);
         if (!$notification instanceof Notification) {
             throw new RuntimeException("'prepare','{$type}' hook must return an instance of " . Notification::class);
         }
     } else {
         // pre Elgg 1.9 notification message generation
         $notification = $this->getDeprecatedNotificationBody($notification, $event, $method);
     }
     $notification = $this->hooks->trigger('format', "notification:{$method}", [], $notification);
     if (!$notification instanceof Notification) {
         throw new RuntimeException("'format','notification:{$method}' hook must return an instance of " . Notification::class);
     }
     if ($this->hooks->hasHandler('send', "notification:{$method}")) {
         // return true to indicate the notification has been sent
         $params = array('notification' => $notification, 'event' => $event);
         $result = $this->hooks->trigger('send', "notification:{$method}", $params, false);
         if ($this->logger->getLevel() == Logger::INFO) {
             $logger_data = print_r((array) $notification->toObject(), true);
             if ($result) {
                 $this->logger->info("Notification sent: " . $logger_data);
             } else {
                 $this->logger->info("Notification was not sent: " . $logger_data);
             }
         }
         return $result;
     } else {
         // pre Elgg 1.9 notification handler
         $userGuid = $notification->getRecipientGUID();
         $senderGuid = $notification->getSenderGUID();
         $subject = $notification->subject;
         $body = $notification->body;
         $params = $notification->params;
         return (bool) _elgg_notify_user($userGuid, $senderGuid, $subject, $body, $params, array($method));
     }
 }
Example #13
0
 /**
  * Populate the boot data
  *
  * @param \stdClass      $config   Elgg CONFIG object
  * @param \Elgg\Database $db       Elgg database
  * @param EntityTable    $entities Entities service
  * @param Plugins        $plugins  Plugins service
  *
  * @return void
  * @throws \InstallationException
  */
 public function populate(\stdClass $config, Database $db, EntityTable $entities, Plugins $plugins)
 {
     // get subtypes
     $rows = $db->getData("\n\t\t\tSELECT *\n\t\t\tFROM {$db->prefix}entity_subtypes\n\t\t");
     foreach ($rows as $row) {
         $this->subtype_data[$row->id] = $row;
     }
     // get config
     $rows = $db->getData("\n\t\t\tSELECT *\n\t\t\tFROM {$db->prefix}config\n\t\t");
     foreach ($rows as $row) {
         $this->config_values[$row->name] = unserialize($row->value);
     }
     if (!array_key_exists('installed', $this->config_values)) {
         // try to fetch from old pre 3.0 datalists table
         // need to do this to be able to perform an upgrade from 2.x to 3.0
         try {
             $rows = $db->getData("\n\t\t\t\t\tSELECT *\n\t\t\t\t\tFROM {$db->prefix}datalists\n\t\t\t\t");
             foreach ($rows as $row) {
                 $value = $row->value;
                 if ($row->name == 'processed_upgrades') {
                     // config table already serializes data so no need to double serialize
                     $value = unserialize($value);
                 }
                 $this->config_values[$row->name] = $value;
             }
         } catch (\Exception $e) {
         }
     }
     // get site entity
     $this->site = $entities->get($this->config_values['default_site'], 'site');
     if (!$this->site) {
         throw new \InstallationException("Unable to handle this request. This site is not configured or the database is down.");
     }
     // get plugins
     $this->active_plugins = $plugins->find('active');
     // get plugin settings
     if (!$this->active_plugins) {
         return;
     }
     // find GUIDs with not too many private settings
     $guids = array_map(function (\ElggPlugin $plugin) {
         return $plugin->guid;
     }, $this->active_plugins);
     // find plugin GUIDs with not too many settings
     $limit = 40;
     $set = implode(',', $guids);
     $sql = "\n\t\t\tSELECT entity_guid\n\t\t\tFROM {$db->prefix}private_settings\n\t\t\tWHERE entity_guid IN ({$set})\n\t\t\t  AND name NOT LIKE 'plugin:user_setting:%'\n\t\t\t  AND name NOT LIKE 'elgg:internal:%'\n\t\t\tGROUP BY entity_guid\n\t\t\tHAVING COUNT(*) > {$limit}\n\t\t";
     $unsuitable_guids = $db->getData($sql, function ($row) {
         return (int) $row->entity_guid;
     });
     $guids = array_values($guids);
     $guids = array_diff($guids, $unsuitable_guids);
     if ($guids) {
         // get the settings
         $set = implode(',', $guids);
         $rows = $db->getData("\n\t\t\t\tSELECT entity_guid, `name`, `value`\n\t\t\t\tFROM {$db->prefix}private_settings\n\t\t\t\tWHERE entity_guid IN ({$set})\n\t\t\t\t  AND name NOT LIKE 'plugin:user_setting:%'\n\t\t\t\t  AND name NOT LIKE 'elgg:internal:%'\n\t\t\t\tORDER BY entity_guid\n\t\t\t");
         // make sure we show all entities as loaded
         $this->plugin_settings = array_fill_keys($guids, []);
         foreach ($rows as $i => $row) {
             $this->plugin_settings[$row->entity_guid][$row->name] = $row->value;
         }
     }
 }