diff options
Diffstat (limited to 'engine/lib/river.php')
| -rw-r--r-- | engine/lib/river.php | 932 |
1 files changed, 572 insertions, 360 deletions
diff --git a/engine/lib/river.php b/engine/lib/river.php index 61d40f6ef..e92040eb7 100644 --- a/engine/lib/river.php +++ b/engine/lib/river.php @@ -1,27 +1,31 @@ <?php /** - * Elgg river 2.0. - * Functions for listening for and generating the river separately from the system log. + * Elgg river. + * Activity stream functions. * - * @package Elgg - * @subpackage Core - * @author Curverider Ltd - * @link http://elgg.org/ + * @package Elgg.Core + * @subpackage SocialModel.River */ /** * Adds an item to the river. * - * @param string $view The view that will handle the river item (must exist) - * @param string $action_type An arbitrary one-word string to define the action (eg 'comment', 'create') - * @param int $subject_guid The GUID of the entity doing the action - * @param int $object_guid The GUID of the entity being acted upon - * @param int $access_id The access ID of the river item (default: same as the object) - * @param int $posted The UNIX epoch timestamp of the river item (default: now) - * @return true|false Depending on success + * @param string $view The view that will handle the river item (must exist) + * @param string $action_type An arbitrary string to define the action (eg 'comment', 'create') + * @param int $subject_guid The GUID of the entity doing the action + * @param int $object_guid The GUID of the entity being acted upon + * @param int $access_id The access ID of the river item (default: same as the object) + * @param int $posted The UNIX epoch timestamp of the river item (default: now) + * @param int $annotation_id The annotation ID associated with this river entry + * + * @return int/bool River ID or false on failure */ -function add_to_river($view,$action_type,$subject_guid,$object_guid,$access_id = "",$posted = 0, $annotation_id = 0) { - // use default viewtype for when called from REST api +function add_to_river($view, $action_type, $subject_guid, $object_guid, $access_id = "", +$posted = 0, $annotation_id = 0) { + + global $CONFIG; + + // use default viewtype for when called from web services api if (!elgg_view_exists($view, 'default')) { return false; } @@ -40,452 +44,660 @@ function add_to_river($view,$action_type,$subject_guid,$object_guid,$access_id = if ($access_id === "") { $access_id = $object->access_id; } - $annotation_id = (int)$annotation_id; $type = $object->getType(); $subtype = $object->getSubtype(); + + $view = sanitise_string($view); $action_type = sanitise_string($action_type); + $subject_guid = sanitise_int($subject_guid); + $object_guid = sanitise_int($object_guid); + $access_id = sanitise_int($access_id); + $posted = sanitise_int($posted); + $annotation_id = sanitise_int($annotation_id); + + $values = array( + 'type' => $type, + 'subtype' => $subtype, + 'action_type' => $action_type, + 'access_id' => $access_id, + 'view' => $view, + 'subject_guid' => $subject_guid, + 'object_guid' => $object_guid, + 'annotation_id' => $annotation_id, + 'posted' => $posted, + ); + + // return false to stop insert + $values = elgg_trigger_plugin_hook('creating', 'river', null, $values); + if ($values == false) { + // inserting did not fail - it was just prevented + return true; + } - // Load config - global $CONFIG; + extract($values); // Attempt to save river item; return success status - return insert_data("insert into {$CONFIG->dbprefix}river " . - " set type = '{$type}', " . - " subtype = '{$subtype}', " . - " action_type = '{$action_type}', " . - " access_id = {$access_id}, " . - " view = '{$view}', " . - " subject_guid = {$subject_guid}, " . - " object_guid = {$object_guid}, " . - " annotation_id = {$annotation_id}, " . - " posted = {$posted} "); + $id = insert_data("insert into {$CONFIG->dbprefix}river " . + " set type = '$type', " . + " subtype = '$subtype', " . + " action_type = '$action_type', " . + " access_id = $access_id, " . + " view = '$view', " . + " subject_guid = $subject_guid, " . + " object_guid = $object_guid, " . + " annotation_id = $annotation_id, " . + " posted = $posted"); + + // update the entities which had the action carried out on it + // @todo shouldn't this be down elsewhere? Like when an annotation is saved? + if ($id) { + update_entity_last_action($object_guid, $posted); + + $river_items = elgg_get_river(array('id' => $id)); + if ($river_items) { + elgg_trigger_event('created', 'river', $river_items[0]); + } + return $id; + } else { + return false; + } } /** - * Removes all items relating to a particular acting entity from the river + * Delete river items + * + * @warning not checking access (should we?) + * + * @param array $options Parameters: + * ids => INT|ARR River item id(s) + * subject_guids => INT|ARR Subject guid(s) + * object_guids => INT|ARR Object guid(s) + * annotation_ids => INT|ARR The identifier of the annotation(s) + * action_types => STR|ARR The river action type(s) identifier + * views => STR|ARR River view(s) * - * @param int $subject_guid The GUID of the entity - * @return true|false Depending on success + * types => STR|ARR Entity type string(s) + * subtypes => STR|ARR Entity subtype string(s) + * type_subtype_pairs => ARR Array of type => subtype pairs where subtype + * can be an array of subtype strings + * + * posted_time_lower => INT The lower bound on the time posted + * posted_time_upper => INT The upper bound on the time posted + * + * @return bool + * @since 1.8.0 */ -function remove_from_river_by_subject($subject_guid) { - // Sanitise - $subject_guid = (int) $subject_guid; - - // Load config +function elgg_delete_river(array $options = array()) { global $CONFIG; - // Remove - return delete_data("delete from {$CONFIG->dbprefix}river where subject_guid = {$subject_guid}"); -} + $defaults = array( + 'ids' => ELGG_ENTITIES_ANY_VALUE, -/** - * Removes all items relating to a particular entity being acted upon from the river - * - * @param int $object_guid The GUID of the entity - * @return true|false Depending on success - */ -function remove_from_river_by_object($object_guid) { - // Sanitise - $object_guid = (int) $object_guid; + 'subject_guids' => ELGG_ENTITIES_ANY_VALUE, + 'object_guids' => ELGG_ENTITIES_ANY_VALUE, + 'annotation_ids' => ELGG_ENTITIES_ANY_VALUE, - // Load config - global $CONFIG; + 'views' => ELGG_ENTITIES_ANY_VALUE, + 'action_types' => ELGG_ENTITIES_ANY_VALUE, - // Remove - return delete_data("delete from {$CONFIG->dbprefix}river where object_guid = {$object_guid}"); -} + 'types' => ELGG_ENTITIES_ANY_VALUE, + 'subtypes' => ELGG_ENTITIES_ANY_VALUE, + 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE, -/** - * Removes all items relating to a particular annotation being acted upon from the river - * - * @param int annotation_id The ID of the annotation - * @return true|false Depending on success - */ -function remove_from_river_by_annotation($annotation_id) { - // Sanitise - $annotation_id = (int) $annotation_id; + 'posted_time_lower' => ELGG_ENTITIES_ANY_VALUE, + 'posted_time_upper' => ELGG_ENTITIES_ANY_VALUE, - // Load config - global $CONFIG; + 'wheres' => array(), + 'joins' => array(), - // Remove - return delete_data("delete from {$CONFIG->dbprefix}river where annotation_id = {$annotation_id}"); + ); + + $options = array_merge($defaults, $options); + + $singulars = array('id', 'subject_guid', 'object_guid', 'annotation_id', 'action_type', 'view', 'type', 'subtype'); + $options = elgg_normalise_plural_options_array($options, $singulars); + + $wheres = $options['wheres']; + + $wheres[] = elgg_get_guid_based_where_sql('rv.id', $options['ids']); + $wheres[] = elgg_get_guid_based_where_sql('rv.subject_guid', $options['subject_guids']); + $wheres[] = elgg_get_guid_based_where_sql('rv.object_guid', $options['object_guids']); + $wheres[] = elgg_get_guid_based_where_sql('rv.annotation_id', $options['annotation_ids']); + $wheres[] = elgg_river_get_action_where_sql($options['action_types']); + $wheres[] = elgg_river_get_view_where_sql($options['views']); + $wheres[] = elgg_get_river_type_subtype_where_sql('rv', $options['types'], + $options['subtypes'], $options['type_subtype_pairs']); + + if ($options['posted_time_lower'] && is_int($options['posted_time_lower'])) { + $wheres[] = "rv.posted >= {$options['posted_time_lower']}"; + } + + if ($options['posted_time_upper'] && is_int($options['posted_time_upper'])) { + $wheres[] = "rv.posted <= {$options['posted_time_upper']}"; + } + + // see if any functions failed + // remove empty strings on successful functions + foreach ($wheres as $i => $where) { + if ($where === FALSE) { + return FALSE; + } elseif (empty($where)) { + unset($wheres[$i]); + } + } + + // remove identical where clauses + $wheres = array_unique($wheres); + + $query = "DELETE rv.* FROM {$CONFIG->dbprefix}river rv "; + + // remove identical join clauses + $joins = array_unique($options['joins']); + + // add joins + foreach ($joins as $j) { + $query .= " $j "; + } + + // add wheres + $query .= ' WHERE '; + + foreach ($wheres as $w) { + $query .= " $w AND "; + } + $query .= "1=1"; + + return delete_data($query); } /** - * Removes a single river entry + * Get river items + * + * @note If using types and subtypes in a query, they are joined with an AND. + * + * @param array $options Parameters: + * ids => INT|ARR River item id(s) + * subject_guids => INT|ARR Subject guid(s) + * object_guids => INT|ARR Object guid(s) + * annotation_ids => INT|ARR The identifier of the annotation(s) + * action_types => STR|ARR The river action type(s) identifier + * posted_time_lower => INT The lower bound on the time posted + * posted_time_upper => INT The upper bound on the time posted + * + * types => STR|ARR Entity type string(s) + * subtypes => STR|ARR Entity subtype string(s) + * type_subtype_pairs => ARR Array of type => subtype pairs where subtype + * can be an array of subtype strings + * + * relationship => STR Relationship identifier + * relationship_guid => INT|ARR Entity guid(s) + * inverse_relationship => BOOL Subject or object of the relationship (false) * - * @param int $id The ID of the river entry - * @return true|false Depending on success - * @since 1.7.2 + * limit => INT Number to show per page (20) + * offset => INT Offset in list (0) + * count => BOOL Count the river items? (false) + * order_by => STR Order by clause (rv.posted desc) + * group_by => STR Group by clause + * + * @return array|int + * @since 1.8.0 */ -function remove_from_river_by_id($id) { +function elgg_get_river(array $options = array()) { global $CONFIG; - // Sanitise - $id = (int) $id; + $defaults = array( + 'ids' => ELGG_ENTITIES_ANY_VALUE, - return delete_data("delete from {$CONFIG->dbprefix}river where id = {$id}"); -} + 'subject_guids' => ELGG_ENTITIES_ANY_VALUE, + 'object_guids' => ELGG_ENTITIES_ANY_VALUE, + 'annotation_ids' => ELGG_ENTITIES_ANY_VALUE, + 'action_types' => ELGG_ENTITIES_ANY_VALUE, + 'relationship' => NULL, + 'relationship_guid' => NULL, + 'inverse_relationship' => FALSE, -/** - * Sets the access ID on river items for a particular object - * - * @param int $object_guid The GUID of the entity - * @param int $access_id The access ID - * @return true|false Depending on success - */ -function update_river_access_by_object($object_guid, $access_id) { - // Sanitise - $object_guid = (int) $object_guid; - $access_id = (int) $access_id; + 'types' => ELGG_ENTITIES_ANY_VALUE, + 'subtypes' => ELGG_ENTITIES_ANY_VALUE, + 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE, - // Load config - global $CONFIG; + 'posted_time_lower' => ELGG_ENTITIES_ANY_VALUE, + 'posted_time_upper' => ELGG_ENTITIES_ANY_VALUE, - // Remove - return update_data("update {$CONFIG->dbprefix}river set access_id = {$access_id} where object_guid = {$object_guid}"); -} + 'limit' => 20, + 'offset' => 0, + 'count' => FALSE, -/** - * Retrieves items from the river. All parameters are optional. - * - * @param int|array $subject_guid Acting entity to restrict to. Default: all - * @param int|array $object_guid Entity being acted on to restrict to. Default: all - * @param string $subject_relationship If set to a relationship type, this will use - * $subject_guid as the starting point and set the subjects to be all users this - * entity has this relationship with (eg 'friend'). Default: blank - * @param string $type The type of entity to restrict to. Default: all - * @param string $subtype The subtype of entity to restrict to. Default: all - * @param string $action_type The type of river action to restrict to. Default: all - * @param int $limit The number of items to retrieve. Default: 20 - * @param int $offset The page offset. Default: 0 - * @param int $posted_min The minimum time period to look at. Default: none - * @param int $posted_max The maximum time period to look at. Default: none - * @return array|false Depending on success - */ -function get_river_items($subject_guid = 0, $object_guid = 0, $subject_relationship = '', $type = '', - $subtype = '', $action_type = '', $limit = 20, $offset = 0, $posted_min = 0, $posted_max = 0) { + 'order_by' => 'rv.posted desc', + 'group_by' => ELGG_ENTITIES_ANY_VALUE, - // Get config - global $CONFIG; + 'wheres' => array(), + 'joins' => array(), + ); - // Sanitise variables - if (!is_array($subject_guid)) { - $subject_guid = (int) $subject_guid; - } else { - foreach($subject_guid as $key => $temp) { - $subject_guid[$key] = (int) $temp; + $options = array_merge($defaults, $options); + + $singulars = array('id', 'subject_guid', 'object_guid', 'annotation_id', 'action_type', 'type', 'subtype'); + $options = elgg_normalise_plural_options_array($options, $singulars); + + $wheres = $options['wheres']; + + $wheres[] = elgg_get_guid_based_where_sql('rv.id', $options['ids']); + $wheres[] = elgg_get_guid_based_where_sql('rv.subject_guid', $options['subject_guids']); + $wheres[] = elgg_get_guid_based_where_sql('rv.object_guid', $options['object_guids']); + $wheres[] = elgg_get_guid_based_where_sql('rv.annotation_id', $options['annotation_ids']); + $wheres[] = elgg_river_get_action_where_sql($options['action_types']); + $wheres[] = elgg_get_river_type_subtype_where_sql('rv', $options['types'], + $options['subtypes'], $options['type_subtype_pairs']); + + if ($options['posted_time_lower'] && is_int($options['posted_time_lower'])) { + $wheres[] = "rv.posted >= {$options['posted_time_lower']}"; + } + + if ($options['posted_time_upper'] && is_int($options['posted_time_upper'])) { + $wheres[] = "rv.posted <= {$options['posted_time_upper']}"; + } + + $joins = $options['joins']; + + if ($options['relationship_guid']) { + $clauses = elgg_get_entity_relationship_where_sql( + 'rv.subject_guid', + $options['relationship'], + $options['relationship_guid'], + $options['inverse_relationship']); + if ($clauses) { + $wheres = array_merge($wheres, $clauses['wheres']); + $joins = array_merge($joins, $clauses['joins']); } } - if (!is_array($object_guid)) { - $object_guid = (int) $object_guid; - } else { - foreach($object_guid as $key => $temp) { - $object_guid[$key] = (int) $temp; + + // see if any functions failed + // remove empty strings on successful functions + foreach ($wheres as $i => $where) { + if ($where === FALSE) { + return FALSE; + } elseif (empty($where)) { + unset($wheres[$i]); } } - if (!empty($type)) { - $type = sanitise_string($type); + + // remove identical where clauses + $wheres = array_unique($wheres); + + if (!$options['count']) { + $query = "SELECT DISTINCT rv.* FROM {$CONFIG->dbprefix}river rv "; + } else { + $query = "SELECT count(DISTINCT rv.id) as total FROM {$CONFIG->dbprefix}river rv "; } - if (!empty($subtype)) { - $subtype = sanitise_string($subtype); + + // add joins + foreach ($joins as $j) { + $query .= " $j "; } - if (!empty($action_type)) { - $action_type = sanitise_string($action_type); + + // add wheres + $query .= ' WHERE '; + + foreach ($wheres as $w) { + $query .= " $w AND "; } - $limit = (int) $limit; - $offset = (int) $offset; - $posted_min = (int) $posted_min; - $posted_max = (int) $posted_max; - // Construct 'where' clauses for the river - $where = array(); - // river table does not have columns expected by get_access_sql_suffix so we modify its output - $where[] = str_replace("and enabled='yes'",'',str_replace('owner_guid','subject_guid',get_access_sql_suffix())); + $query .= elgg_river_get_access_sql(); - if (empty($subject_relationship)) { - if (!empty($subject_guid)) { - if (!is_array($subject_guid)) { - $where[] = " subject_guid = {$subject_guid} "; - } else { - $where[] = " subject_guid in (" . implode(',',$subject_guid) . ") "; - } + if (!$options['count']) { + $options['group_by'] = sanitise_string($options['group_by']); + if ($options['group_by']) { + $query .= " GROUP BY {$options['group_by']}"; } - } else { - if (!is_array($subject_guid)) { - if ($entities = elgg_get_entities_from_relationship(array( - 'relationship' => $subject_relationship, - 'relationship_guid' => $subject_guid, - 'limit' => 9999)) - ) { - $guids = array(); - foreach($entities as $entity) { - $guids[] = (int) $entity->guid; - } - // $guids[] = $subject_guid; - $where[] = " subject_guid in (" . implode(',',$guids) . ") "; - } else { - return array(); - } + + $options['order_by'] = sanitise_string($options['order_by']); + $query .= " ORDER BY {$options['order_by']}"; + + if ($options['limit']) { + $limit = sanitise_int($options['limit']); + $offset = sanitise_int($options['offset'], false); + $query .= " LIMIT $offset, $limit"; } + + $river_items = get_data($query, 'elgg_row_to_elgg_river_item'); + _elgg_prefetch_river_entities($river_items); + + return $river_items; + } else { + $total = get_data_row($query); + return (int)$total->total; } - if (!empty($object_guid)) - if (!is_array($object_guid)) { - $where[] = " object_guid = {$object_guid} "; - } else { - $where[] = " object_guid in (" . implode(',',$object_guid) . ") "; +} + +/** + * Prefetch entities that will be displayed in the river. + * + * @param ElggRiverItem[] $river_items + * @access private + */ +function _elgg_prefetch_river_entities(array $river_items) { + // prefetch objects and subjects + $guids = array(); + foreach ($river_items as $item) { + if ($item->subject_guid && !_elgg_retrieve_cached_entity($item->subject_guid)) { + $guids[$item->subject_guid] = true; + } + if ($item->object_guid && !_elgg_retrieve_cached_entity($item->object_guid)) { + $guids[$item->object_guid] = true; } - if (!empty($type)) { - $where[] = " type = '{$type}' "; } - if (!empty($subtype)) { - $where[] = " subtype = '{$subtype}' "; + if ($guids) { + // avoid creating oversized query + // @todo how to better handle this? + $guids = array_slice($guids, 0, 300, true); + // return value unneeded, just priming cache + elgg_get_entities(array( + 'guids' => array_keys($guids), + 'limit' => 0, + )); } - if (!empty($action_type)) { - $where[] = " action_type = '{$action_type}' "; + + // prefetch object containers + $guids = array(); + foreach ($river_items as $item) { + $object = $item->getObjectEntity(); + if ($object->container_guid && !_elgg_retrieve_cached_entity($object->container_guid)) { + $guids[$object->container_guid] = true; + } } - if (!empty($posted_min)) { - $where[] = " posted > {$posted_min} "; + if ($guids) { + $guids = array_slice($guids, 0, 300, true); + elgg_get_entities(array( + 'guids' => array_keys($guids), + 'limit' => 0, + )); } - if (!empty($posted_max)) { - $where[] = " posted < {$posted_max} "; +} + +/** + * List river items + * + * @param array $options Any options from elgg_get_river() plus: + * pagination => BOOL Display pagination links (true) + * + * @return string + * @since 1.8.0 + */ +function elgg_list_river(array $options = array()) { + global $autofeed; + $autofeed = true; + + $defaults = array( + 'offset' => (int) max(get_input('offset', 0), 0), + 'limit' => (int) max(get_input('limit', 20), 0), + 'pagination' => TRUE, + 'list_class' => 'elgg-list-river elgg-river', // @todo remove elgg-river in Elgg 1.9 + ); + + $options = array_merge($defaults, $options); + + if (!$options["limit"] && !$options["offset"]) {
+ // no need for pagination if listing is unlimited
+ $options["pagination"] = false;
} - $whereclause = implode(' and ', $where); + $options['count'] = TRUE; + $count = elgg_get_river($options); - // Construct main SQL - $sql = "select id,type,subtype,action_type,access_id,view,subject_guid,object_guid,annotation_id,posted" . - " from {$CONFIG->dbprefix}river where {$whereclause} order by posted desc limit {$offset},{$limit}"; + $options['count'] = FALSE; + $items = elgg_get_river($options); - // Get data - return get_data($sql); + $options['count'] = $count; + $options['items'] = $items; + + return elgg_view('page/components/list', $options); } /** - * Returns a human-readable representation of a river item + * Convert a database row to a new ElggRiverItem * - * @see get_river_items + * @param stdClass $row Database row from the river table * - * @param stdClass $item A river item object as returned from get_river_items - * @return string|false Depending on success + * @return ElggRiverItem + * @since 1.8.0 + * @access private */ -function elgg_view_river_item($item) { - if (isset($item->view)) { - $object = get_entity($item->object_guid); - $subject = get_entity($item->subject_guid); - if (!$object || !$subject) { - // probably means an entity is disabled - return false; - } else { - if (elgg_view_exists($item->view)) { - $body = elgg_view($item->view,array( - 'item' => $item - )); - } - } - return elgg_view('river/item/wrapper',array( - 'item' => $item, - 'body' => $body - )); +function elgg_row_to_elgg_river_item($row) { + if (!($row instanceof stdClass)) { + return NULL; } - return false; + + return new ElggRiverItem($row); } /** - * Returns a human-readable version of the river. + * Get the river's access where clause * - * @param int|array $subject_guid Acting entity to restrict to. Default: all - * @param int|array $object_guid Entity being acted on to restrict to. Default: all - * @param string $subject_relationship If set to a relationship type, this will use - * $subject_guid as the starting point and set the subjects to be all users this - * entity has this relationship with (eg 'friend'). Default: blank - * @param string $type The type of entity to restrict to. Default: all - * @param string $subtype The subtype of entity to restrict to. Default: all - * @param string $action_type The type of river action to restrict to. Default: all - * @param int $limit The number of items to retrieve. Default: 20 - * @param int $posted_min The minimum time period to look at. Default: none - * @param int $posted_max The maximum time period to look at. Default: none - * @return string Human-readable river. + * @return string + * @since 1.8.0 + * @access private */ -function elgg_view_river_items($subject_guid = 0, $object_guid = 0, $subject_relationship = '', - $type = '', $subtype = '', $action_type = '', $limit = 20, $posted_min = 0, $posted_max = 0, $pagination = true) { - - // Get input from outside world and sanitise it - $offset = (int) get_input('offset',0); - - // Get river items, if they exist - if ($riveritems = get_river_items($subject_guid,$object_guid,$subject_relationship,$type,$subtype,$action_type,($limit + 1),$offset,$posted_min,$posted_max)) { - - return elgg_view('river/item/list',array( - 'limit' => $limit, - 'offset' => $offset, - 'items' => $riveritems, - 'pagination' => $pagination - )); - - } - - return ''; +function elgg_river_get_access_sql() { + // rewrite default access where clause to work with river table + return str_replace("and enabled='yes'", '', + str_replace('owner_guid', 'rv.subject_guid', + str_replace('access_id', 'rv.access_id', get_access_sql_suffix()))); } /** - * Construct and execute the query required for the activity stream. + * Returns SQL where clause for type and subtype on river table * - * @deprecated 1.8 + * @internal This is a simplified version of elgg_get_entity_type_subtype_where_sql() + * which could be used for all queries once the subtypes have been denormalized. * - * @param int $limit Limit the query. - * @param int $offset Execute from the given object - * @param mixed $type A type, or array of types to look for. Note: This is how they appear in the SYSTEM LOG. - * @param mixed $subtype A subtype, or array of types to look for. Note: This is how they appear in the SYSTEM LOG. - * @param mixed $owner_guid The guid or a collection of GUIDs - * @param string $owner_relationship If defined, the relationship between $owner_guid and the entity owner_guid - so "is $owner_guid $owner_relationship with $entity->owner_guid" - * @return array An array of system log entries. + * @param string $table 'rv' + * @param NULL|array $types Array of types or NULL if none. + * @param NULL|array $subtypes Array of subtypes or NULL if none + * @param NULL|array $pairs Array of pairs of types and subtypes + * + * @return string + * @since 1.8.0 + * @access private */ -function get_activity_stream_data($limit = 10, $offset = 0, $type = "", $subtype = "", $owner_guid = "", $owner_relationship = "") { - global $CONFIG; +function elgg_get_river_type_subtype_where_sql($table, $types, $subtypes, $pairs) { + // short circuit if nothing is requested + if (!$types && !$subtypes && !$pairs) { + return ''; + } - $limit = (int)$limit; - $offset = (int)$offset; + $wheres = array(); + $types_wheres = array(); + $subtypes_wheres = array(); - if ($type) { - if (!is_array($type)) { - $type = array(sanitise_string($type)); - } else { - foreach ($type as $k => $v) { - $type[$k] = sanitise_string($v); + // if no pairs, use types and subtypes + if (!is_array($pairs)) { + if ($types) { + if (!is_array($types)) { + $types = array($types); + } + foreach ($types as $type) { + $type = sanitise_string($type); + $types_wheres[] = "({$table}.type = '$type')"; } } - } - if ($subtype) { - if (!is_array($subtype)) { - $subtype = array(sanitise_string($subtype)); - } else { - foreach ($subtype as $k => $v) { - $subtype[$k] = sanitise_string($v); + if ($subtypes) { + if (!is_array($subtypes)) { + $subtypes = array($subtypes); + } + foreach ($subtypes as $subtype) { + $subtype = sanitise_string($subtype); + $subtypes_wheres[] = "({$table}.subtype = '$subtype')"; } } - } - if ($owner_guid) { - if (is_array($owner_guid)) { - foreach ($owner_guid as $k => $v) { - $owner_guid[$k] = (int)$v; + if (is_array($types_wheres) && count($types_wheres)) { + $types_wheres = array(implode(' OR ', $types_wheres)); + } + + if (is_array($subtypes_wheres) && count($subtypes_wheres)) { + $subtypes_wheres = array('(' . implode(' OR ', $subtypes_wheres) . ')'); + } + + $wheres = array(implode(' AND ', array_merge($types_wheres, $subtypes_wheres))); + + } else { + // using type/subtype pairs + foreach ($pairs as $paired_type => $paired_subtypes) { + $paired_type = sanitise_string($paired_type); + if (is_array($paired_subtypes)) { + $paired_subtypes = array_map('sanitise_string', $paired_subtypes); + $paired_subtype_str = implode("','", $paired_subtypes); + if ($paired_subtype_str) { + $wheres[] = "({$table}.type = '$paired_type'" + . " AND {$table}.subtype IN ('$paired_subtype_str'))"; + } + } else { + $paired_subtype = sanitise_string($paired_subtypes); + $wheres[] = "({$table}.type = '$paired_type'" + . " AND {$table}.subtype = '$paired_subtype')"; } - } else { - $owner_guid = array((int)$owner_guid); } } - $owner_relationship = sanitise_string($owner_relationship); + if (is_array($wheres) && count($wheres)) { + $where = implode(' OR ', $wheres); + return "($where)"; + } - // Get a list of possible views - $activity_events= array(); - $activity_views = array_merge(elgg_view_tree('activity', 'default'), elgg_view_tree('river', 'default')); // Join activity with river + return ''; +} - $done = array(); +/** + * Get the where clause based on river action type strings + * + * @param array $types Array of action type strings + * + * @return string + * @since 1.8.0 + * @access private + */ +function elgg_river_get_action_where_sql($types) { + if (!$types) { + return ''; + } - foreach ($activity_views as $view) { - $fragments = explode('/', $view); - $tmp = explode('/',$view, 2); - $tmp = $tmp[1]; + if (!is_array($types)) { + $types = sanitise_string($types); + return "(rv.action_type = '$types')"; + } - if ((isset($fragments[0])) && (($fragments[0] == 'river') || ($fragments[0] == 'activity')) - && (!in_array($tmp, $done))) { + // sanitize types array + $types_sanitized = array(); + foreach ($types as $type) { + $types_sanitized[] = sanitise_string($type); + } - if (isset($fragments[1])) { - $f = array(); - for ($n = 1; $n < count($fragments); $n++) { - $val = sanitise_string($fragments[$n]); - switch($n) { - case 1: $key = 'type'; break; - case 2: $key = 'subtype'; break; - case 3: $key = 'event'; break; - } - $f[$key] = $val; - } + $type_str = implode("','", $types_sanitized); + return "(rv.action_type IN ('$type_str'))"; +} - // Filter result based on parameters - $add = true; - if ($type) { - if (!in_array($f['type'], $type)) { - $add = false; - } - } - if (($add) && ($subtype)) { - if (!in_array($f['subtype'], $subtype)) { - $add = false; - } - } - if (($add) && ($event)) { - if (!in_array($f['event'], $event)) { - $add = false; - } - } +/** + * Get the where clause based on river view strings + * + * @param array $views Array of view strings + * + * @return string + * @since 1.8.0 + * @access private + */ +function elgg_river_get_view_where_sql($views) { + if (!$views) { + return ''; + } - if ($add) { - $activity_events[] = $f; - } - } + if (!is_array($views)) { + $views = sanitise_string($views); + return "(rv.view = '$views')"; + } - $done[] = $tmp; - } + // sanitize views array + $views_sanitized = array(); + foreach ($views as $view) { + $views_sanitized[] = sanitise_string($view); } - $n = 0; - foreach ($activity_events as $details) { - // Get what we're talking about - if ($details['subtype'] == 'default') { - $details['subtype'] = ''; - } + $view_str = implode("','", $views_sanitized); + return "(rv.view IN ('$view_str'))"; +} - if (($details['type']) && ($details['event'])) { - if ($n > 0) { - $obj_query .= " or "; - } +/** + * Sets the access ID on river items for a particular object + * + * @param int $object_guid The GUID of the entity + * @param int $access_id The access ID + * + * @return bool Depending on success + */ +function update_river_access_by_object($object_guid, $access_id) { + // Sanitise + $object_guid = (int) $object_guid; + $access_id = (int) $access_id; - $access = ""; - if ($details['type']!='relationship') { - $access = " and " . get_access_sql_suffix('sl'); - } + // Load config + global $CONFIG; - $obj_query .= "( sl.object_type='{$details['type']}' - AND sl.object_subtype='{$details['subtype']}' - AND sl.event='{$details['event']}' $access )"; + // Remove + $query = "update {$CONFIG->dbprefix}river + set access_id = {$access_id} + where object_guid = {$object_guid}"; + return update_data($query); +} - $n++; - } - } +/** + * Page handler for activity + * + * @param array $page + * @return bool + * @access private + */ +function elgg_river_page_handler($page) { + global $CONFIG; - // User - if ((count($owner_guid)) && ($owner_guid[0] != 0)) { - $user = " and sl.performed_by_guid in (".implode(',', $owner_guid).")"; - - if ($owner_relationship) { - $friendsarray = ""; - if ($friends = elgg_get_entities_from_relationship(array( - 'relationship' => $owner_relationship, - 'relationship_guid' => $owner_guid[0], - 'inverse_relationship' => FALSE, - 'types' => 'user', - 'subtypes' => $subtype, - 'limit' => 9999)) - ) { - - $friendsarray = array(); - foreach($friends as $friend) { - $friendsarray[] = $friend->getGUID(); - } + elgg_set_page_owner_guid(elgg_get_logged_in_user_guid()); - $user = " and sl.performed_by_guid in (" . implode(',', $friendsarray).")"; - } - } + // make a URL segment available in page handler script + $page_type = elgg_extract(0, $page, 'all'); + $page_type = preg_replace('[\W]', '', $page_type); + if ($page_type == 'owner') { + $page_type = 'mine'; } + set_input('page_type', $page_type); - $query = "SELECT sl.* FROM {$CONFIG->dbprefix}system_log sl - WHERE 1 $user AND ($obj_query) - ORDER BY sl.time_created desc limit $offset, $limit"; - return get_data($query); + require_once("{$CONFIG->path}pages/river.php"); + return true; } + +/** + * Register river unit tests + * @access private + */ +function elgg_river_test($hook, $type, $value) { + global $CONFIG; + $value[] = $CONFIG->path . 'engine/tests/api/river.php'; + return $value; +} + +/** + * Initialize river library + * @access private + */ +function elgg_river_init() { + elgg_register_page_handler('activity', 'elgg_river_page_handler'); + $item = new ElggMenuItem('activity', elgg_echo('activity'), 'activity'); + elgg_register_menu_item('site', $item); + + elgg_register_widget_type('river_widget', elgg_echo('river:widget:title'), elgg_echo('river:widget:description')); + + elgg_register_action('river/delete', '', 'admin'); + + elgg_register_plugin_hook_handler('unit_test', 'system', 'elgg_river_test'); +} + +elgg_register_event_handler('init', 'system', 'elgg_river_init'); |
