aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engine/classes/ElggRiverItem.php99
-rw-r--r--engine/lib/annotations.php7
-rw-r--r--engine/lib/river.php772
-rw-r--r--engine/lib/views.php31
-rw-r--r--languages/en.php14
-rw-r--r--mod/riverdashboard/index.php2
-rw-r--r--mod/riverdashboard/start.php285
-rw-r--r--mod/riverdashboard/views/default/river/item/list.php2
-rw-r--r--pages/river.php59
-rw-r--r--views/default/core/river/body.php32
-rw-r--r--views/default/core/river/controls.php22
-rw-r--r--views/default/core/river/filter.php48
-rw-r--r--views/default/core/river/footer.php46
-rw-r--r--views/default/core/river/image.php12
-rw-r--r--views/default/css/screen.php93
-rw-r--r--views/default/forms/comments/inline.php15
16 files changed, 1169 insertions, 370 deletions
diff --git a/engine/classes/ElggRiverItem.php b/engine/classes/ElggRiverItem.php
new file mode 100644
index 000000000..6ff9b01d1
--- /dev/null
+++ b/engine/classes/ElggRiverItem.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * River item class.
+ *
+ * @package Elgg.Core
+ * @subpackage Core
+ */
+class ElggRiverItem
+{
+ public $id;
+ public $subject_guid;
+ public $object_guid;
+ public $annotation_id;
+ public $type;
+ public $subtype;
+ public $action_type;
+ public $access_id;
+ public $view;
+ public $posted;
+
+ /**
+ * Construct a river item object given a database row.
+ *
+ * @param stdClass $object Object obtained from database
+ */
+ function __construct($object) {
+ if (!($object instanceof stdClass)) {
+ // throw exception
+ }
+
+ foreach ($object as $key => $value) {
+ $this->$key = $value;
+ }
+ }
+
+ /**
+ * Get the subject of this river item
+ *
+ * @return ElggEntity
+ */
+ public function getSubjectEntity() {
+ return get_entity($this->subject_guid);
+ }
+
+ /**
+ * Get the object of this river item
+ *
+ * @return ElggEntity
+ */
+ public function getObjectEntity() {
+ return get_entity($this->object_guid);
+ }
+
+ /**
+ * Get the Annotation for this river item
+ *
+ * @return ElggAnnotation
+ */
+ public function getAnnotation() {
+ return get_annotation($this->annotation_id);
+ }
+
+ /**
+ * Get the view used to display this river item
+ *
+ * @return string
+ */
+ public function getView() {
+ return $this->view;
+ }
+
+ /**
+ * Get the time this activity was posted
+ *
+ * @return int
+ */
+ public function getPostedTime() {
+ return (int)$this->posted;
+ }
+
+ /**
+ * Get the type of the object
+ *
+ * @return string 'river'
+ */
+ public function getType() {
+ return 'river';
+ }
+
+ /**
+ * Get the subtype of the object
+ *
+ * @return string 'item'
+ */
+ public function getSubtype() {
+ return 'item';
+ }
+
+}
diff --git a/engine/lib/annotations.php b/engine/lib/annotations.php
index f858fa8d1..a0665f744 100644
--- a/engine/lib/annotations.php
+++ b/engine/lib/annotations.php
@@ -595,7 +595,12 @@ function list_annotations($entity_guid, $name = "", $limit = 25, $asc = true) {
$offset = (int) get_input("annoff", 0);
$annotations = get_annotations($entity_guid, "", "", $name, "", "", $limit, $offset, $asc);
- return elgg_view_annotation_list($annotations, $count, $offset, $limit);
+ $params = array(
+ 'count' => $count,
+ 'offset' => $offset,
+ 'limit' => $count,
+ );
+ return elgg_view_annotation_list($annotations, $params);
}
/**
diff --git a/engine/lib/river.php b/engine/lib/river.php
index ad069d5cf..46f11da46 100644
--- a/engine/lib/river.php
+++ b/engine/lib/river.php
@@ -1,7 +1,7 @@
<?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.Core
* @subpackage SocialModel.River
@@ -142,148 +142,373 @@ function remove_from_river_by_id($id) {
}
/**
- * Sets the access ID on river items for a particular object
+ * Get river items
*
- * @param int $object_guid The GUID of the entity
- * @param int $access_id The access ID
+ * @param array $options
+ * 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
*
- * @return bool 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
+ *
+ * relationship => STR Relationship identifier
+ * relationship_guid => INT|ARR Entity guid(s)
+ * inverse_relationship => BOOL Subject or object of the relationship (false)
+ *
+ * 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 update_river_access_by_object($object_guid, $access_id) {
- // Sanitise
- $object_guid = (int) $object_guid;
- $access_id = (int) $access_id;
-
- // Load config
+function elgg_get_river(array $options = array()) {
global $CONFIG;
- // Remove
- $query = "update {$CONFIG->dbprefix}river
- set access_id = {$access_id}
- where object_guid = {$object_guid}";
- return update_data($query);
-}
+ $defaults = array(
+ 'subject_guids' => ELGG_ENTITIES_ANY_VALUE,
+ 'object_guids' => ELGG_ENTITIES_ANY_VALUE,
+ 'annotation_ids' => ELGG_ENTITIES_ANY_VALUE,
+ 'action_types' => ELGG_ENTITIES_ANY_VALUE,
-/**
- * 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) {
+ 'relationship' => NULL,
+ 'relationship_guid' => NULL,
+ 'inverse_relationship' => FALSE,
- // Get config
- global $CONFIG;
+ 'types' => ELGG_ENTITIES_ANY_VALUE,
+ 'subtypes' => ELGG_ENTITIES_ANY_VALUE,
+ 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE,
- // Sanitise variables
- if (!is_array($subject_guid)) {
- $subject_guid = (int) $subject_guid;
- } else {
- foreach ($subject_guid as $key => $temp) {
- $subject_guid[$key] = (int) $temp;
+ 'posted_time_lower' => ELGG_ENTITIES_ANY_VALUE,
+ 'posted_time_upper' => ELGG_ENTITIES_ANY_VALUE,
+
+ 'limit' => 20,
+ 'offset' => 0,
+ 'count' => FALSE,
+
+ 'order_by' => 'rv.posted desc',
+ 'group_by' => ELGG_ENTITIES_ANY_VALUE,
+
+ 'wheres' => array(),
+ 'joins' => array(),
+ );
+
+ $options = array_merge($defaults, $options);
+
+ $singulars = array('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.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;
+
+ // remove identical where clauses
+ $wheres = array_unique($wheres);
+
+ // 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);
+
+ 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'", '',
+ $query .= elgg_river_get_access_sql();
+
+ if (!$options['count']) {
+ $options['group_by'] = sanitise_string($options['group_by']);
+ if ($options['group_by']) {
+ $query .= " GROUP BY {$options['group_by']}";
+ }
+
+ $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']);
+ $query .= " LIMIT $offset, $limit";
+ }
+
+ $river_items = get_data($query, 'elgg_row_to_elgg_river_item');
+
+ return $river_items;
+ } else {
+ $total = get_data_row($query);
+ return (int)$total->total;
+ }
+}
+
+/**
+ * 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()) {
+
+ $defaults = array(
+ 'offset' => (int) max(get_input('offset', 0), 0),
+ 'limit' => (int) max(get_input('limit', 20), 0),
+ 'pagination' => TRUE,
+ 'list_class' => 'elgg-river',
+ );
+
+ $options = array_merge($defaults, $options);
+
+ $options['count'] = TRUE;
+ $count = elgg_get_river($options);
+
+ $options['count'] = FALSE;
+ $items = elgg_get_river($options);
+
+ $options['count'] = $count;
+ $options['items'] = $items;
+ return elgg_view('layout/objects/list', $options);
+}
+
+/**
+ * Convert a database row to a new ElggRiverItem
+ *
+ * @param stdClass $row Database row from the river table
+ *
+ * @return ElggRiverItem
+ * @since 1.8.0
+ * @access private
+ */
+function elgg_row_to_elgg_river_item($row) {
+ if (!($row instanceof stdClass)) {
+ return NULL;
+ }
+
+ return new ElggRiverItem($row);
+}
+
+/**
+ * Get the river's access where clause
+ *
+ * @return string
+ * @since 1.8.0
+ * @access private
+ */
+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', 'subject_guid', get_access_sql_suffix()));
+}
- 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) . ") ";
+/**
+ * Returns SQL where clause for type and subtype on river table
+ *
+ * @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.
+ * FYI: It allows types and subtypes to not be paired.
+ *
+ * @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 elgg_get_river_type_subtype_where_sql($table, $types, $subtypes, $pairs) {
+ // short circuit if nothing is requested
+ if (!$types && !$subtypes && !$pairs) {
+ return '';
+ }
+
+ $wheres = array();
+
+ // 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);
+ $wheres[] = "({$table}.type = '$type')";
}
}
+
+ if ($subtypes) {
+ if (!is_array($subtypes)) {
+ $subtypes = array($subtypes);
+ }
+ foreach ($subtypes as $subtype) {
+ $subtype = sanitise_string($subtype);
+ $wheres[] = "({$table}.subtype = '$subtype')";
+ }
+ }
+
+ if (is_array($wheres) && count($wheres)) {
+ $wheres = array(implode(' AND ', $wheres));
+ }
} 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;
+ // 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'))";
}
- $where[] = " subject_guid in (" . implode(',', $guids) . ") ";
} else {
- return array();
+ $paired_subtype = sanitise_string($paired_subtypes);
+ $wheres[] = "({$table}.type = '$paired_type'"
+ . " AND {$table}.subtype = '$paired_subtype')";
}
}
}
- if (!empty($object_guid)) {
- if (!is_array($object_guid)) {
- $where[] = " object_guid = {$object_guid} ";
- } else {
- $where[] = " object_guid in (" . implode(',', $object_guid) . ") ";
- }
+
+ if (is_array($wheres) && count($wheres)) {
+ $where = implode(' OR ', $wheres);
+ return "($where)";
}
- if (!empty($type)) {
- $where[] = " type = '{$type}' ";
+
+ return '';
+}
+
+/**
+ * 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 '';
}
- if (!empty($subtype)) {
- $where[] = " subtype = '{$subtype}' ";
+
+ if (!is_array($types)) {
+ $types = sanitise_string($types);
+ return "'(rv.action_type = '$types')";
}
- if (!empty($action_type)) {
- $where[] = " action_type = '{$action_type}' ";
+
+ // sanitize types array
+ $types_sanitized = array();
+ foreach ($types as $type) {
+ $types_sanitized[] = sanitise_string($type);
}
- if (!empty($posted_min)) {
- $where[] = " posted > {$posted_min} ";
+
+ $type_str = implode("','", $types_sanitized);
+ return "(rv.action_type IN ('$type_str'))";
+}
+
+/**
+ * Returns a human-readable representation of a river item
+ *
+ * @param ElggRiverItem $item A river item object
+ *
+ * @return string|false Depending on success
+ */
+function elgg_view_river_item($item) {
+ if (!$item || !$item->getView() || !elgg_view_exists($item->getView())) {
+ return '';
}
- if (!empty($posted_max)) {
- $where[] = " posted < {$posted_max} ";
+
+ $subject = $item->getSubjectEntity();
+ $object = $item->getObjectEntity();
+ if (!$subject || !$object) {
+ // subject is disabled or subject/object deleted
+ return '';
}
- $whereclause = implode(' and ', $where);
+ $vars = array(
+ 'pict' => elgg_view('core/river/image', array('item' => $item)),
+ 'body' => elgg_view('core/river/body', array('item' => $item)),
+ 'pict_alt' => elgg_view('core/river/controls', array('item' => $item)),
+ 'class' => 'elgg-river-item',
+ );
+ return elgg_view('layout/objects/media', $vars);
+}
- // 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}";
+/**
+ * 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;
- // Get data
- return get_data($sql);
+ // Load config
+ global $CONFIG;
+
+ // Remove
+ $query = "update {$CONFIG->dbprefix}river
+ set access_id = {$access_id}
+ where object_guid = {$object_guid}";
+ return update_data($query);
}
/**
@@ -293,8 +518,9 @@ $posted_max = 0) {
* @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
+ * 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
@@ -304,140 +530,53 @@ $posted_max = 0) {
* @param int $posted_max The maximum time period to look at. Default: none
*
* @return array|false Depending on success
+ * @deprecated 1.8
*/
-function elgg_get_river_items($subject_guid = 0, $object_guid = 0, $subject_relationship = '',
-$type = '', $subtype = '', $action_type = '', $limit = 10, $offset = 0, $posted_min = 0,
+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) {
+ elgg_deprecated_notice("get_river_items deprecated by elgg_get_river", 1.8);
- // Get config
- global $CONFIG;
+ $options = 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;
- }
- }
- if (!is_array($object_guid)) {
- $object_guid = (int) $object_guid;
- } else {
- foreach ($object_guid as $key => $temp) {
- $object_guid[$key] = (int) $temp;
- }
- }
- if (!empty($type)) {
- $type = sanitise_string($type);
- }
- if (!empty($subtype)) {
- $subtype = sanitise_string($subtype);
+ if ($subject_guid) {
+ $options['subject_guid'] = $subject_guid;
}
- if (!empty($action_type)) {
- $action_type = sanitise_string($action_type);
- }
- $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_new('er', 'e')));
- 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) . ") ";
- }
- }
- } else {
- if (!is_array($subject_guid)) {
- $entities = elgg_get_entities_from_relationship(array(
- 'relationship' => $subject_relationship,
- 'relationship_guid' => $subject_guid,
- 'limit' => 9999,
- ));
- if (is_array($entities) && !empty($entities)) {
- $guids = array();
- foreach ($entities as $entity) {
- $guids[] = (int) $entity->guid;
- }
- // $guids[] = $subject_guid;
- $where[] = " subject_guid in (" . implode(',', $guids) . ") ";
- } else {
- return array();
- }
- }
- }
- if (!empty($object_guid)) {
- if (!is_array($object_guid)) {
- $where[] = " object_guid = {$object_guid} ";
- } else {
- $where[] = " object_guid in (" . implode(',', $object_guid) . ") ";
- }
+ if ($object_guid) {
+ $options['object_guid'] = $object_guid;
}
- if (!empty($type)) {
- $where[] = " er.type = '{$type}' ";
- }
- if (!empty($subtype)) {
- $where[] = " er.subtype = '{$subtype}' ";
- }
- if (!empty($action_type)) {
- $where[] = " action_type = '{$action_type}' ";
+
+ if ($subject_relationship) {
+ $options['relationship'] = $subject_relationship;
+ unset($options['subject_guid']);
+ $options['relationship_guid'] = $subject_guid;
}
- if (!empty($posted_min)) {
- $where[] = " posted > {$posted_min} ";
+
+ if ($type) {
+ $options['type'] = $type;
}
- if (!empty($posted_max)) {
- $where[] = " posted < {$posted_max} ";
+
+ if ($subtype) {
+ $options['subtype'] = $subtype;
}
- $whereclause = implode(' and ', $where);
+ if ($action_type) {
+ $options['action_type'] = $action_type;
+ }
- // Construct main SQL
- $sql = "select er.*" .
- " from {$CONFIG->dbprefix}river er, {$CONFIG->dbprefix}entities e " .
- " where {$whereclause} AND er.object_guid = e.guid GROUP BY object_guid " .
- " ORDER BY e.last_action desc LIMIT {$offset}, {$limit}";
+ $options['limit'] = $limit;
+ $options['offset'] = $offset;
- // Get data
- return get_data($sql);
-}
+ if ($posted_min) {
+ $options['posted_time_lower'] = $posted_min;
+ }
-/**
- * Returns a human-readable representation of a river item
- *
- * @see get_river_items
- *
- * @param stdClass $item A river item object as returned from get_river_items
- *
- * @return string|false Depending on success
- */
-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
- ));
+ if ($posted_max) {
+ $options['posted_time_upper'] = $posted_max;
}
- return false;
+
+ return elgg_get_river($options);
}
/**
@@ -456,147 +595,42 @@ function elgg_view_river_item($item) {
* @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
* @param bool $pagination Show pagination?
- * @param $bool $chronological Show in chronological order?
*
* @return string Human-readable river.
+ * @deprecated 1.8
*/
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, $chronological = false) {
+$posted_max = 0, $pagination = true) {
+ elgg_deprecated_notice("elgg_view_river_items deprecated for elgg_list_river", 1.8);
+
+ $river_items = get_river_items($subject_guid, $object_guid, $subject_relationship,
+ $type, $subtype, $action_type, $limit + 1, $posted_min, $posted_max);
// Get input from outside world and sanitise it
$offset = (int) get_input('offset', 0);
- // Get the correct function
- if ($chronological == true) {
- $riveritems = get_river_items($subject_guid, $object_guid, $subject_relationship, $type,
- $subtype, $action_type, ($limit + 1), $offset, $posted_min, $posted_max);
- } else {
- $riveritems = elgg_get_river_items($subject_guid, $object_guid, $subject_relationship, $type,
- $subtype, $action_type, ($limit + 1), $offset, $posted_min, $posted_max);
- }
-
- // Get river items, if they exist
- if ($riveritems) {
-
- return elgg_view('river/item/list', array(
- 'limit' => $limit,
- 'offset' => $offset,
- 'items' => $riveritems,
- 'pagination' => $pagination
- ));
-
- }
-
- return '';
-}
-
-/**
- * This function has been added here until we decide if it is going to roll into core or not
- * Add access restriction sql code to a given query.
- * Note that if this code is executed in privileged mode it will return blank.
- *
- * @TODO: DELETE once Query classes are fully integrated
- *
- * @param string $table_prefix_one Optional table. prefix for the access code.
- * @param string $table_prefix_two Another optiona table prefix?
- * @param int $owner Owner GUID
- *
- * @return string
- */
-function get_access_sql_suffix_new($table_prefix_one = '', $table_prefix_two = '', $owner = null) {
- global $ENTITY_SHOW_HIDDEN_OVERRIDE, $CONFIG;
-
- $sql = "";
- $friends_bit = "";
- $enemies_bit = "";
-
- if ($table_prefix_one) {
- $table_prefix_one = sanitise_string($table_prefix_one) . ".";
- }
-
- if ($table_prefix_two) {
- $table_prefix_two = sanitise_string($table_prefix_two) . ".";
- }
-
- if (!isset($owner)) {
- $owner = get_loggedin_userid();
- }
-
- if (!$owner) {
- $owner = -1;
- }
-
- $ignore_access = elgg_check_access_overrides($owner);
- $access = get_access_list($owner);
-
- if ($ignore_access) {
- $sql = " (1 = 1) ";
- } else if ($owner != -1) {
- $friends_bit = "{$table_prefix_one}access_id = " . ACCESS_FRIENDS . "
- AND {$table_prefix_one}owner_guid IN (
- SELECT guid_one FROM {$CONFIG->dbprefix}entity_relationships
- WHERE relationship='friend' AND guid_two=$owner
- )";
-
- $friends_bit = '(' . $friends_bit . ') OR ';
-
- if ((isset($CONFIG->user_block_and_filter_enabled)) && ($CONFIG->user_block_and_filter_enabled)) {
- // check to see if the user is in the entity owner's block list
- // or if the entity owner is in the user's filter list
- // if so, disallow access
- $enemies_bit = get_annotation_sql('elgg_block_list', "{$table_prefix_one}owner_guid",
- $owner, false);
-
- $enemies_bit = '('
- . $enemies_bit
- . ' AND ' . get_annotation_sql('elgg_filter_list', $owner, "{$table_prefix_one}owner_guid",
- false)
- . ')';
- }
- }
-
- if (empty($sql)) {
- $sql = " $friends_bit ({$table_prefix_one}access_id IN {$access}
- OR ({$table_prefix_one}owner_guid = {$owner})
- OR (
- {$table_prefix_one}access_id = " . ACCESS_PRIVATE . "
- AND {$table_prefix_one}owner_guid = $owner
- )
- )";
- }
-
- if ($enemies_bit) {
- $sql = "$enemies_bit AND ($sql)";
- }
-
- if (!$ENTITY_SHOW_HIDDEN_OVERRIDE) {
- $sql .= " and {$table_prefix_two}enabled='yes'";
- }
-
- return '(' . $sql . ')';
+ // view them
+ $params = array(
+ 'items' => $river_items,
+ 'count' => count($river_items),
+ 'offset' => $offset,
+ 'limit' => $limit,
+ 'pagination' => $pagination,
+ 'list-class' => 'elgg-river-list',
+ );
+
+ return elgg_view('layout/objects/list', $params);
}
/**
* Construct and execute the query required for the activity stream.
*
* @deprecated 1.8
- *
- * @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.
*/
function get_activity_stream_data($limit = 10, $offset = 0, $type = "", $subtype = "",
$owner_guid = "", $owner_relationship = "") {
+ elgg_deprecated_notice("get_activity_stream_data was deprecated", 1.8);
global $CONFIG;
@@ -744,3 +778,35 @@ $owner_guid = "", $owner_relationship = "") {
ORDER BY sl.time_created desc limit $offset, $limit";
return get_data($query);
}
+
+/**
+ * Page handler for activiy
+ *
+ * @param array $page
+ */
+function elgg_river_page_handler($page) {
+ global $CONFIG;
+
+ elgg_set_page_owner_guid(get_loggedin_userid());
+
+ $page_type = elgg_get_array_value(0, $page, 'all');
+ if ($page_type == 'owner') {
+ $page_type = 'mine';
+ }
+
+ // content filter code here
+ $entity_type = '';
+ $entity_subtype = '';
+
+ require_once("{$CONFIG->path}pages/river.php");
+}
+
+/**
+ * Initialize river library
+ */
+function elgg_river_init() {
+ register_page_handler('activity', 'elgg_river_page_handler');
+ add_menu(elgg_echo('activity'), "pg/activity/");
+}
+
+elgg_register_event_handler('init', 'system', 'elgg_river_init');
diff --git a/engine/lib/views.php b/engine/lib/views.php
index 7c542b289..fa65bcae8 100644
--- a/engine/lib/views.php
+++ b/engine/lib/views.php
@@ -835,18 +835,17 @@ $list_type_toggle = true, $pagination = true) {
* @return string The list of annotations
* @access private
*/
-function elgg_view_annotation_list($annotations, $count, $offset, $limit) {
- $params = array(
+function elgg_view_annotation_list($annotations, array $vars = array()) {
+ $defaults = array(
'items' => $annotations,
- 'count' => (int) $count,
- 'offset' => (int) $offset,
- 'limit' => (int) $limit,
- 'list-class' => 'elgg-annotation-list',
+ 'list_class' => 'elgg-annotation-list',
'full_view' => true,
'offset_key' => 'annoff',
);
- return elgg_view('layout/objects/list', $params);
+ $vars = array_merge($defaults, $vars);
+
+ return elgg_view('layout/objects/list', $vars);
}
/**
@@ -1062,11 +1061,19 @@ function elgg_view_form($action, $form_vars = array(), $body_vars = array()) {
* @since 1.8.0
* @access private
*/
-function elgg_view_list_item($item, $full_view, $vars) {
- if (elgg_instanceof($item)) {
- return elgg_view_entity($item, $full_view);
- } else {
- return elgg_view_annotation($item, $full_view);
+function elgg_view_list_item($item, $full_view, array $vars = array()) {
+ switch ($item->getType()) {
+ case 'user':
+ case 'object':
+ case 'group':
+ case 'site':
+ return elgg_view_entity($item, $full_view);
+ case 'annotation':
+ return elgg_view_annotation($item, $full_view);
+ case 'river':
+ return elgg_view_river_item($item);
+ default:
+ break;
}
}
diff --git a/languages/en.php b/languages/en.php
index e4e17d9b9..bbda438a1 100644
--- a/languages/en.php
+++ b/languages/en.php
@@ -628,6 +628,15 @@ $english = array(
'usersettings:statistics:label:membersince' => "Member since",
'usersettings:statistics:label:lastlogin' => "Last logged in",
+/**
+ * Activity river
+ */
+ 'river:all' => 'All Site Activity',
+ 'river:mine' => 'My Activity',
+ 'river:friends' => 'Friends Activty',
+ 'river:select' => 'Show %s',
+ 'river:comments:more' => '+%u more',
+ 'river:generic_comment' => 'commented on %s %s',
/**
@@ -663,8 +672,11 @@ $english = array(
'send' => 'Send',
'post' => 'Post',
'submit' => 'Submit',
- 'site' => 'Site',
+ 'comment' => 'Comment',
+ 'site' => 'Site',
+ 'activity' => 'Activity',
+
'up' => 'Up',
'down' => 'Down',
'top' => 'Top',
diff --git a/mod/riverdashboard/index.php b/mod/riverdashboard/index.php
index 8d650e1f5..be516b150 100644
--- a/mod/riverdashboard/index.php
+++ b/mod/riverdashboard/index.php
@@ -41,7 +41,7 @@ switch($orient) {
$title .= elgg_view_title($title_wording);
$extend = elgg_view("activity/extend");
-$river = elgg_view_river_items($subject_guid, 0, $relationship_type, $type, $subtype, '', 20, 0, 0, TRUE, FALSE);
+$river = elgg_view_river_items_new($subject_guid, 0, $relationship_type, $type, $subtype, '', 20, 0, 0, TRUE);
// Replacing callback calls in the nav with something meaningless
$river = str_replace('callback=true', 'replaced=88,334', $river);
diff --git a/mod/riverdashboard/start.php b/mod/riverdashboard/start.php
index 8469448a0..f7421e1cd 100644
--- a/mod/riverdashboard/start.php
+++ b/mod/riverdashboard/start.php
@@ -5,6 +5,8 @@
* @package RiverDashboard
*/
+elgg_register_event_handler('init', 'system', 'riverdashboard_init');
+
function riverdashboard_init() {
global $CONFIG;
// Register and optionally replace the dashboard
@@ -83,4 +85,285 @@ function riverdashboard_ecml_views_hook($hook, $entity_type, $return_value, $par
return $return_value;
}
-elgg_register_event_handler('init', 'system', 'riverdashboard_init');
+/**
+ * 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 elgg_get_river_items($subject_guid = 0, $object_guid = 0, $subject_relationship = '',
+$type = '', $subtype = '', $action_type = '', $limit = 10, $offset = 0, $posted_min = 0,
+$posted_max = 0) {
+
+ // Get config
+ global $CONFIG;
+
+ // Sanitise variables
+ if (!is_array($subject_guid)) {
+ $subject_guid = (int) $subject_guid;
+ } else {
+ foreach ($subject_guid as $key => $temp) {
+ $subject_guid[$key] = (int) $temp;
+ }
+ }
+ if (!is_array($object_guid)) {
+ $object_guid = (int) $object_guid;
+ } else {
+ foreach ($object_guid as $key => $temp) {
+ $object_guid[$key] = (int) $temp;
+ }
+ }
+ if (!empty($type)) {
+ $type = sanitise_string($type);
+ }
+ if (!empty($subtype)) {
+ $subtype = sanitise_string($subtype);
+ }
+ if (!empty($action_type)) {
+ $action_type = sanitise_string($action_type);
+ }
+ $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_new('er', 'e')));
+
+ 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) . ") ";
+ }
+ }
+ } else {
+ if (!is_array($subject_guid)) {
+ $entities = elgg_get_entities_from_relationship(array(
+ 'relationship' => $subject_relationship,
+ 'relationship_guid' => $subject_guid,
+ 'limit' => 9999,
+ ));
+ if (is_array($entities) && !empty($entities)) {
+ $guids = array();
+ foreach ($entities as $entity) {
+ $guids[] = (int) $entity->guid;
+ }
+ // $guids[] = $subject_guid;
+ $where[] = " subject_guid in (" . implode(',', $guids) . ") ";
+ } else {
+ return array();
+ }
+ }
+ }
+ if (!empty($object_guid)) {
+ if (!is_array($object_guid)) {
+ $where[] = " object_guid = {$object_guid} ";
+ } else {
+ $where[] = " object_guid in (" . implode(',', $object_guid) . ") ";
+ }
+ }
+ if (!empty($type)) {
+ $where[] = " er.type = '{$type}' ";
+ }
+ if (!empty($subtype)) {
+ $where[] = " er.subtype = '{$subtype}' ";
+ }
+ if (!empty($action_type)) {
+ $where[] = " action_type = '{$action_type}' ";
+ }
+ if (!empty($posted_min)) {
+ $where[] = " posted > {$posted_min} ";
+ }
+ if (!empty($posted_max)) {
+ $where[] = " posted < {$posted_max} ";
+ }
+
+ $whereclause = implode(' and ', $where);
+
+ // Construct main SQL
+ $sql = "select er.*" .
+ " from {$CONFIG->dbprefix}river er, {$CONFIG->dbprefix}entities e " .
+ " where {$whereclause} AND er.object_guid = e.guid GROUP BY object_guid " .
+ " ORDER BY e.last_action desc LIMIT {$offset}, {$limit}";
+
+ // Get data
+ return get_data($sql);
+}
+
+/**
+ * This function has been added here until we decide if it is going to roll into core or not
+ * Add access restriction sql code to a given query.
+ * Note that if this code is executed in privileged mode it will return blank.
+ *
+ * @TODO: DELETE once Query classes are fully integrated
+ *
+ * @param string $table_prefix_one Optional table. prefix for the access code.
+ * @param string $table_prefix_two Another optiona table prefix?
+ * @param int $owner Owner GUID
+ *
+ * @return string
+ */
+function get_access_sql_suffix_new($table_prefix_one = '', $table_prefix_two = '', $owner = null) {
+ global $ENTITY_SHOW_HIDDEN_OVERRIDE, $CONFIG;
+
+ $sql = "";
+ $friends_bit = "";
+ $enemies_bit = "";
+
+ if ($table_prefix_one) {
+ $table_prefix_one = sanitise_string($table_prefix_one) . ".";
+ }
+
+ if ($table_prefix_two) {
+ $table_prefix_two = sanitise_string($table_prefix_two) . ".";
+ }
+
+ if (!isset($owner)) {
+ $owner = get_loggedin_userid();
+ }
+
+ if (!$owner) {
+ $owner = -1;
+ }
+
+ $ignore_access = elgg_check_access_overrides($owner);
+ $access = get_access_list($owner);
+
+ if ($ignore_access) {
+ $sql = " (1 = 1) ";
+ } else if ($owner != -1) {
+ $friends_bit = "{$table_prefix_one}access_id = " . ACCESS_FRIENDS . "
+ AND {$table_prefix_one}owner_guid IN (
+ SELECT guid_one FROM {$CONFIG->dbprefix}entity_relationships
+ WHERE relationship='friend' AND guid_two=$owner
+ )";
+
+ $friends_bit = '(' . $friends_bit . ') OR ';
+
+ if ((isset($CONFIG->user_block_and_filter_enabled)) && ($CONFIG->user_block_and_filter_enabled)) {
+ // check to see if the user is in the entity owner's block list
+ // or if the entity owner is in the user's filter list
+ // if so, disallow access
+ $enemies_bit = get_annotation_sql('elgg_block_list', "{$table_prefix_one}owner_guid",
+ $owner, false);
+
+ $enemies_bit = '('
+ . $enemies_bit
+ . ' AND ' . get_annotation_sql('elgg_filter_list', $owner, "{$table_prefix_one}owner_guid",
+ false)
+ . ')';
+ }
+ }
+
+ if (empty($sql)) {
+ $sql = " $friends_bit ({$table_prefix_one}access_id IN {$access}
+ OR ({$table_prefix_one}owner_guid = {$owner})
+ OR (
+ {$table_prefix_one}access_id = " . ACCESS_PRIVATE . "
+ AND {$table_prefix_one}owner_guid = $owner
+ )
+ )";
+ }
+
+ if ($enemies_bit) {
+ $sql = "$enemies_bit AND ($sql)";
+ }
+
+ if (!$ENTITY_SHOW_HIDDEN_OVERRIDE) {
+ $sql .= " and {$table_prefix_two}enabled='yes'";
+ }
+
+ return '(' . $sql . ')';
+}
+
+/**
+ * Returns a human-readable version of the river.
+ *
+ * @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
+ * @param bool $pagination Show pagination?
+ * @param $bool $chronological Show in chronological order?
+ *
+ * @return string Human-readable river.
+ */
+function elgg_view_river_items_new($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);
+
+ $riveritems = elgg_get_river_items($subject_guid, $object_guid, $subject_relationship, $type,
+ $subtype, $action_type, ($limit + 1), $offset, $posted_min, $posted_max);
+
+ // Get river items, if they exist
+ if ($riveritems) {
+
+ return elgg_view('river/item/list', array(
+ 'limit' => $limit,
+ 'offset' => $offset,
+ 'items' => $riveritems,
+ 'pagination' => $pagination
+ ));
+
+ }
+
+ return '';
+}
+
+/**
+ * Returns a human-readable representation of a river item
+ *
+ * @param ElggRiverItem $item A river item object
+ *
+ * @return string|false Depending on success
+ */
+function elgg_view_river_item_new($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
+ ));
+ }
+ return false;
+}
diff --git a/mod/riverdashboard/views/default/river/item/list.php b/mod/riverdashboard/views/default/river/item/list.php
index c708a9db6..d09f5de1c 100644
--- a/mod/riverdashboard/views/default/river/item/list.php
+++ b/mod/riverdashboard/views/default/river/item/list.php
@@ -5,7 +5,7 @@ if (isset($vars['items']) && is_array($vars['items'])) {
$i = 0;
if (!empty($vars['items'])) {
foreach($vars['items'] as $item) {
- echo elgg_view_river_item($item);
+ echo elgg_view_river_item_new($item);
$i++;
if ($i >= $vars['limit']) {
break;
diff --git a/pages/river.php b/pages/river.php
new file mode 100644
index 000000000..120ad4b57
--- /dev/null
+++ b/pages/river.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Main activity stream list page
+ */
+
+// $page_type comes from the page handler function
+
+$options = array();
+
+$type = get_input('type', 'all');
+$subtype = get_input('subtype', '');
+if ($subtype) {
+ $selector = "type=$type&subtype=$subtype";
+} else {
+ $selector = "type=$type";
+}
+
+if ($type != 'all') {
+ $options['type'] = $type;
+ if ($subtype) {
+ $options['subtype'] = $subtype;
+ }
+}
+
+switch ($page_type) {
+ case 'mine':
+ $title = elgg_echo('river:mine');
+ $page_filter = 'mine';
+ $options['subject_guid'] = get_loggedin_userid();
+ break;
+ case 'friends':
+ $title = elgg_echo('river:friends');
+ $page_filter = 'friends';
+ $options['relationship_guid'] = get_loggedin_userid();
+ $options['relationship'] = 'friend';
+ break;
+ default:
+ $title = elgg_echo('river:all');
+ $page_filter = 'all';
+ break;
+}
+
+$content = elgg_view('core/river/filter', array('selector' => $selector));
+
+$content .= elgg_list_river($options);
+
+$sidebar = elgg_view('core/river/sidebar');
+
+$params = array(
+ 'content' => $content,
+ 'sidebar' => $sidebar,
+ 'buttons' => '',
+ 'filter_context' => $page_filter,
+ 'class' => 'elgg-river-layout',
+);
+
+$body = elgg_view_layout('content', $params);
+
+echo elgg_view_page($title, $body);
diff --git a/views/default/core/river/body.php b/views/default/core/river/body.php
new file mode 100644
index 000000000..4129c3b15
--- /dev/null
+++ b/views/default/core/river/body.php
@@ -0,0 +1,32 @@
+<?php
+/**
+ * Body of river item
+ *
+ * @uses $vars[item]
+ */
+
+$item = $vars[item];
+$subject = $item->getSubjectEntity();
+
+// river item header
+$params = array(
+ 'href' => $subject->getURL(),
+ 'text' => $subject->name,
+);
+$subject_link = elgg_view('output/url', $params);
+$timestamp = elgg_get_friendly_time($item->getPostedTime());
+$header = "$subject_link <span class=\"elgg-river-timestamp\">$timestamp</span>";
+
+// body
+$body = elgg_view($item->getView(), array('item' => $item));
+
+// footer
+$footer = elgg_view('core/river/footer', $vars);
+
+$params = array(
+ 'header' => $header,
+ 'body' => $body,
+ 'footer' => $footer,
+ 'show_inner' => false,
+);
+echo elgg_view('layout/objects/module', $params);
diff --git a/views/default/core/river/controls.php b/views/default/core/river/controls.php
new file mode 100644
index 000000000..7d6feaadd
--- /dev/null
+++ b/views/default/core/river/controls.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ * Controls on an river item
+ *
+ *
+ * @uses $vars['item']
+ */
+
+$object = $vars['item']->getObjectEntity();
+
+if (isloggedin()) {
+ if ($vars['item']->annotation_id == 0) {
+ $params = array(
+ 'href' => '#',
+ 'text' => elgg_echo('generic_comments:text'),
+ 'class' => 'elgg-toggle',
+ 'internalid' => "elgg-toggler-{$object->getGUID()}",
+ );
+ echo elgg_view('output/url', $params);
+ //echo elgg_view('likes/forms/link', array('entity' => $object));
+ }
+} \ No newline at end of file
diff --git a/views/default/core/river/filter.php b/views/default/core/river/filter.php
new file mode 100644
index 000000000..08e211d44
--- /dev/null
+++ b/views/default/core/river/filter.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Content filter for river
+ *
+ * @uses $vars[]
+ */
+
+// create selection array
+$options = array();
+$options['type=all'] = elgg_echo('river:select', array(elgg_echo('all')));
+$registered_entities = elgg_get_config('registered_entities');
+if (!empty($registered_entities)) {
+ foreach ($registered_entities as $type => $subtypes) {
+ if (!is_array($subtypes)) {
+ $label = elgg_echo('river:select', array(elgg_echo("item:$type")));
+ $options["type=$type"] = $label;
+ } else {
+ foreach ($subtypes as $subtype) {
+ $label = elgg_echo('river:select', array(elgg_echo("item:$type:$subtype")));
+ $options["type=$type&subtype=$subtype"] = $label;
+ }
+ }
+ }
+}
+
+$params = array(
+ 'internalid' => 'elgg-river-selector',
+ 'options_values' => $options,
+);
+$selector = $vars['selector'];
+if ($selector) {
+ $params['value'] = $selector;
+}
+echo elgg_view('input/pulldown', $params);
+?>
+
+<script type="text/javascript">
+$(document).ready(function() {
+ $('#elgg-river-selector').change(function() {
+ var url = window.location.href;
+ if (window.location.search.length) {
+ url = url.substring(0, url.indexOf('?'));
+ }
+ url += '?' + $(this).val();
+ elgg.forward(url);
+ });
+});
+</script>
diff --git a/views/default/core/river/footer.php b/views/default/core/river/footer.php
new file mode 100644
index 000000000..de27f265f
--- /dev/null
+++ b/views/default/core/river/footer.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * River item footer
+ */
+
+$item = $vars['item'];
+$object = $item->getObjectEntity();
+
+// annotations do not have comments
+if ($item->annotation_id != 0 || !$object) {
+ return true;
+}
+
+$comment_count = count_annotations($object->getGUID(), '', '', 'generic_comment');
+
+$comments = get_annotations($object->getGUID(), "", "", 'generic_comment', "", "", 3, 0, "desc");
+if ($comments) {
+ $comments = array_reverse($comments);
+
+?>
+ <span class="elgg-river-comments-tab"><?php echo elgg_echo('comments'); ?></span>
+
+<?php
+
+ echo elgg_view_annotation_list($comments, array('list_class' => 'elgg-river-comments'));
+
+ if ($comment_count > count($comments)) {
+ $num_more_comments = $comment_count - count($comments);
+ $url = $object->getURL();
+ $params = array(
+ 'href' => $url,
+ 'text' => elgg_echo('river:comments:more', array($num_more_comments)),
+ );
+ $link = elgg_view('output/url', $params);
+ echo "<div class=\"elgg-river-more\">$link</div>";
+ }
+}
+
+// inline comment form
+$body = elgg_view('forms/comments/inline', array('entity' => $object));
+$params = array(
+ 'body' => $body,
+ 'action' => 'action/comments/add',
+ 'internalid' => "elgg-togglee-{$object->getGUID()}",
+);
+echo elgg_view('input/form', $params);
diff --git a/views/default/core/river/image.php b/views/default/core/river/image.php
new file mode 100644
index 000000000..afb6b4019
--- /dev/null
+++ b/views/default/core/river/image.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Elgg river image
+ *
+ * Displayed next to the body of each river item
+ *
+ * @uses $vars['item']
+ */
+
+$subject = $vars['item']->getSubjectEntity();
+
+echo elgg_view("profile/icon", array('entity' => $subject, 'size' => 'small'));
diff --git a/views/default/css/screen.php b/views/default/css/screen.php
index cfbae0e1e..114213a0b 100644
--- a/views/default/css/screen.php
+++ b/views/default/css/screen.php
@@ -226,6 +226,99 @@ a.widget-edit-button:hover, a.widget-delete-button:hover {
}
/* ***************************************
+ RIVER
+*************************************** */
+.elgg-river {
+ border-top: 1px solid #CCCCCC;
+}
+.elgg-river li {
+ border-bottom: 1px solid #CCCCCC;
+}
+.elgg-river-item {
+ padding: 7px 0;
+}
+.elgg-river-item .elgg-pict {
+ margin-right: 20px;
+}
+.elgg-river-timestamp {
+ color: #666666;
+ font-size: 85%;
+ font-style: italic;
+ line-height: 1.2em;
+}
+.elgg-river-excerpt {
+ border-left: 1px solid #CCCCCC;
+ font-size: 85%;
+ line-height: 1.5em;
+ margin: 8px 0 5px 0;
+ padding-left: 5px;
+}
+.elgg-river-layout .input-pulldown {
+ float: right;
+ margin: 10px 0;
+}
+
+.elgg-river-comments-tab {
+ display: block;
+ background-color: #EEEEEE;
+ color: #4690D6;
+ margin-top: 5px;
+ width: auto;
+ float: right;
+ font-size: 85%;
+ padding: 1px 7px;
+ -moz-border-radius-topleft: 5px;
+ -moz-border-radius-topright: 5px;
+ -webkit-border-top-left-radius: 5px;
+ -webkit-border-top-right-radius: 5px;
+}
+.elgg-river-comments {
+ margin: 0;
+ border-top: none;
+}
+.elgg-river-comments li:first-child {
+ -moz-border-radius-topleft: 5px;
+ -webkit-border-top-left-radius: 5px;
+}
+.elgg-river-comments li:last-child {
+ -moz-border-radius-bottomleft: 5px;
+ -moz-border-radius-bottomright: 5px;
+ -webkit-border-bottom-right-radius: 5px;
+ -webkit-border-bottom-left-radius: 5px;
+}
+.elgg-river-comments li {
+ background-color: #EEEEEE;
+ border-bottom: none;
+ padding: 4px;
+ margin-bottom: 2px;
+}
+.elgg-river-comments .elgg-media {
+ padding: 0;
+}
+.elgg-river-more {
+ background-color: #EEEEEE;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ padding: 2px 4px;
+ font-size: 85%;
+ margin-bottom: 2px;
+}
+.elgg-river-item form {
+ background-color: #EEEEEE;
+ padding: 4px 4px;
+ -moz-border-radius: 5px;
+ -webkit-border-radius: 5px;
+ display: none;
+ height: 30px;
+}
+.elgg-river-item input[type=text] {
+ width: 80%;
+}
+.elgg-river-item input[type=submit] {
+ margin: 0 0 0 10px;
+}
+
+/* ***************************************
LOGIN / REGISTER
*************************************** */
/* login in sidebar */
diff --git a/views/default/forms/comments/inline.php b/views/default/forms/comments/inline.php
new file mode 100644
index 000000000..ee5883435
--- /dev/null
+++ b/views/default/forms/comments/inline.php
@@ -0,0 +1,15 @@
+<?php
+/**
+ * Inline comment form body
+ *
+ * @uses $vars['entity']
+ */
+
+if (isset($vars['entity']) && isloggedin()) {
+ echo elgg_view('input/text', array('internalname' => 'generic_comment'));
+ echo elgg_view('input/hidden', array(
+ 'internalname' => 'entity_guid',
+ 'value' => $vars['entity']->getGUID()
+ ));
+ echo elgg_view('input/submit', array('value' => elgg_echo('comment')));
+}