aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--engine/lib/annotations.php305
-rw-r--r--engine/lib/deprecated-1.8.php108
-rw-r--r--engine/lib/metadata.php188
-rw-r--r--engine/lib/metastrings.php320
4 files changed, 473 insertions, 448 deletions
diff --git a/engine/lib/annotations.php b/engine/lib/annotations.php
index d61173ef6..a3f8f0bb9 100644
--- a/engine/lib/annotations.php
+++ b/engine/lib/annotations.php
@@ -183,7 +183,7 @@ function update_annotation($annotation_id, $name, $value, $value_type, $owner_gu
*
* annotation_case_sensitive => BOOL Overall Case sensitive
*
- * annotation_owner_guids => NULL|ARR guids for metadata owners
+ * annotation_owner_guids => NULL|ARR guids for annotation owners
*
* annotation_created_time_lower => INT Lower limit for created time.
*
@@ -191,302 +191,31 @@ function update_annotation($annotation_id, $name, $value, $value_type, $owner_gu
*
* annotation_calculation => STR Perform the MySQL function on the annotation values returned.
*
- * @return array
+ * @return mixed
* @since 1.8.0
*/
-function elgg_get_annotations($options = array()) {
- $defaults = array(
- // entities
- 'types' => ELGG_ENTITIES_ANY_VALUE,
- 'subtypes' => ELGG_ENTITIES_ANY_VALUE,
- 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE,
-
- 'guids' => ELGG_ENTITIES_ANY_VALUE,
- 'owner_guids' => ELGG_ENTITIES_ANY_VALUE,
- 'container_guids' => ELGG_ENTITIES_ANY_VALUE,
- 'site_guids' => get_config('site_guid'),
-
- 'modified_time_lower' => ELGG_ENTITIES_ANY_VALUE,
- 'modified_time_upper' => ELGG_ENTITIES_ANY_VALUE,
- 'created_time_lower' => ELGG_ENTITIES_ANY_VALUE,
- 'created_time_upper' => ELGG_ENTITIES_ANY_VALUE,
-
- // annotations
- // options are normalized to the plural in case we ever add support for them.
- 'annotation_names' => ELGG_ENTITIES_ANY_VALUE,
- 'annotation_values' => ELGG_ENTITIES_ANY_VALUE,
- //'annotation_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE,
- //'annotation_name_value_pairs_operator' => 'AND',
-
- 'annotation_case_sensitive' => TRUE,
- //'order_by_annotation' => array(),
- 'annotation_calculation' => ELGG_ENTITIES_NO_VALUE,
-
- 'annotation_created_time_lower' => ELGG_ENTITIES_ANY_VALUE,
- 'annotation_created_time_upper' => ELGG_ENTITIES_ANY_VALUE,
-
- 'annotation_owner_guids' => ELGG_ENTITIES_ANY_VALUE,
-
- // sql
- 'order_by' => 'a.time_created asc',
- 'limit' => 10,
- 'offset' => 0,
- 'count' => FALSE,
- 'selects' => array(),
- 'wheres' => array(),
- 'joins' => array(),
-
- 'callback' => 'row_to_elggannotation',
+function elgg_get_annotations(array $options = array()) {
+ // map the annotation_* options to metastring_* options
+ $map = array(
+ 'annotation_names' => 'metastring_names',
+ 'annotation_values' => 'metastring_values',
+ 'annotation_case_sensitive' => 'metastring_case_sensitive',
+ 'annotation_owner_guids' => 'metastring_owner_guids',
+ 'annotation_created_time_lower' => 'metastring_created_time_lower',
+ 'annotation_created_time_upper' => 'metastring_created_time_upper',
+ 'annotation_calculation' => 'metastring_calculation'
);
- $options = array_merge($defaults, $options);
-
- // can't use helper function with type_subtype_pair because
- // it's already an array...just need to merge it
- if (isset($options['type_subtype_pair'])) {
- if (isset($options['type_subtype_pairs'])) {
- $options['type_subtype_pairs'] = array_merge($options['type_subtype_pairs'],
- $options['type_subtype_pair']);
- } else {
- $options['type_subtype_pairs'] = $options['type_subtype_pair'];
- }
- }
-
- $singulars = array('type', 'subtype', 'guid', 'owner_guid', 'container_guid', 'site_guid',
- 'annotation_name', 'annotation_value'
- );
+ $singulars = array('annotation_name', 'annotation_value');
$options = elgg_normalise_plural_options_array($options, $singulars);
- if (!$options) {
- return false;
- }
-
- $db_prefix = elgg_get_config('dbprefix');
-
- // evaluate where clauses
- if (!is_array($options['wheres'])) {
- $options['wheres'] = array($options['wheres']);
- }
-
- $wheres = $options['wheres'];
-
- // entities
- $wheres[] = elgg_get_entity_type_subtype_where_sql('e', $options['types'],
- $options['subtypes'], $options['type_subtype_pairs']);
-
- $wheres[] = elgg_get_guid_based_where_sql('e.guid', $options['guids']);
- $wheres[] = elgg_get_guid_based_where_sql('e.owner_guid', $options['owner_guids']);
- $wheres[] = elgg_get_guid_based_where_sql('e.container_guid', $options['container_guids']);
- $wheres[] = elgg_get_guid_based_where_sql('e.site_guid', $options['site_guids']);
-
- $wheres[] = elgg_get_entity_time_where_sql('e', $options['created_time_upper'],
- $options['created_time_lower'], $options['modified_time_upper'], $options['modified_time_lower']);
-
- // annotations
- $annotation_clauses = elgg_get_annotation_sql('a', $options['annotation_names'],
- $options['annotation_values'], $options['annotation_case_sensitive']);
-
- $wheres = array_merge($wheres, $annotation_clauses['wheres']);
-
- $wheres[] = elgg_get_entity_time_where_sql('a', $options['annotation_created_time_upper'],
- $options['annotation_created_time_lower'], null, null);
-
- $wheres[] = elgg_get_guid_based_where_sql('a.owner_guid', $options['annotation_owner_guids']);
-
- // 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]);
- }
- }
-
- // evaluate join clauses
- if (!is_array($options['joins'])) {
- $options['joins'] = array($options['joins']);
- }
-
- $joins = $options['joins'];
-
- $joins = array_merge($joins, $annotation_clauses['joins']);
- $joins[] = "JOIN {$db_prefix}entities e ON a.entity_guid = e.guid";
- $joins[] = "JOIN {$db_prefix}metastrings n on a.name_id = n.id";
- $joins[] = "JOIN {$db_prefix}metastrings v on a.value_id = v.id";
-
-
- // remove identical join clauses
- $joins = array_unique($joins);
-
- foreach ($joins as $i => $join) {
- if ($join === FALSE) {
- return FALSE;
- } elseif (empty($join)) {
- unset($joins[$i]);
- }
- }
-
- // evalutate selects
- if ($options['selects']) {
- $selects = '';
- foreach ($options['selects'] as $select) {
- $selects .= ", $select";
- }
- } else {
- $selects = '';
- }
-
- // check for calculations
- if ($options['count']) {
- $options['annotation_calculation'] = 'count';
- }
-
- if ($options['annotation_calculation'] === ELGG_ENTITIES_NO_VALUE) {
- $query = "SELECT DISTINCT a.*, n.string as name, v.string as value FROM {$db_prefix}annotations a";
- } else {
- $query = "SELECT DISTINCT v.string as value, {$options['annotation_calculation']}(v.string) as calculation FROM {$db_prefix}annotations a";
- }
-
- // add joins
- foreach ($joins as $j) {
- $query .= " $j ";
- }
-
- // add wheres
- $query .= ' WHERE ';
-
- foreach ($wheres as $w) {
- $query .= " $w AND ";
- }
-
- // Add access controls
- $query .= get_access_sql_suffix('e');
- if ($options['annotation_calculation'] === ELGG_ENTITIES_NO_VALUE) {
- if ($options['group_by'] = sanitise_string($options['group_by'])) {
- $query .= " GROUP BY {$options['group_by']}";
- }
-
- if ($options['order_by'] = sanitise_string($options['order_by'])) {
- $query .= " ORDER BY {$options['order_by']}";
+ foreach ($map as $ann => $ms) {
+ if (isset($options[$ann])) {
+ $options[$ms] = $options[$ann];
}
-
- if ($options['limit']) {
- $limit = sanitise_int($options['limit']);
- $offset = sanitise_int($options['offset']);
- $query .= " LIMIT $offset, $limit";
- }
-
- $dt = get_data($query, $options['callback']);
- return $dt;
- } else {
- $result = get_data_row($query);
- return $result->calculation;
- }
-}
-
-/**
- * Returns an array of joins and wheres for use in annotations.
- *
- * @note The $pairs is reserved for name/value pairs if we want to implement those.
- *
- * @param string $table The annotation table name or alias
- * @param array $names An array of names
- * @param array $values An array of values
- * @param array $pairs Name / value pairs. Not currently used.
- * @param bool $case_sensitive Should name and values be case sensitive?
- *
- * @return array
- */
-function elgg_get_annotation_sql($table, $names = null, $values = null,
- $pairs = null, $case_sensitive = false) {
-
- if ((!$names && $names !== 0)
- && (!$values && $values !== 0)
- && (!$pairs && $pairs !== 0)) {
-
- return '';
- }
-
- $db_prefix = elgg_get_config('dbprefix');
-
- // join counter for incremental joins.
- $i = 1;
-
- // binary forces byte-to-byte comparision of strings, making
- // it case- and diacritical-mark- sensitive.
- // only supported on values.
- $binary = ($case_sensitive) ? ' BINARY ' : '';
-
- $access = get_access_sql_suffix($table);
-
- $return = array (
- 'joins' => array (),
- 'wheres' => array()
- );
-
- $wheres = array();
-
- // get names wheres and joins
- $names_where = '';
- if ($names !== NULL) {
- if (!is_array($names)) {
- $names = array($names);
- }
-
- $sanitised_names = array();
- foreach ($names as $name) {
- // normalise to 0.
- if (!$name) {
- $name = '0';
- }
- $sanitised_names[] = '\'' . sanitise_string($name) . '\'';
- }
-
- if ($names_str = implode(',', $sanitised_names)) {
- $return['joins'][] = "JOIN {$db_prefix}metastrings msn on $table.name_id = msn.id";
- $names_where = "(msn.string IN ($names_str))";
- }
- }
-
- // get values wheres and joins
- $values_where = '';
- if ($values !== NULL) {
- if (!is_array($values)) {
- $values = array($values);
- }
-
- $sanitised_values = array();
- foreach ($values as $value) {
- // normalize to 0
- if (!$value) {
- $value = 0;
- }
- $sanitised_values[] = '\'' . sanitise_string($value) . '\'';
- }
-
- if ($values_str = implode(',', $sanitised_values)) {
- $return['joins'][] = "JOIN {$db_prefix}metastrings msv on $table.value_id = msv.id";
- $values_where = "({$binary}msv.string IN ($values_str))";
- }
- }
-
- if ($names_where && $values_where) {
- $wheres[] = "($names_where AND $values_where AND $access)";
- } elseif ($names_where) {
- $wheres[] = "($names_where AND $access)";
- } elseif ($values_where) {
- $wheres[] = "($values_where AND $access)";
- }
-
- if ($where = implode(' AND ', $wheres)) {
- $return['wheres'][] = "($where)";
}
- return $return;
+ return elgg_get_metastring_based_objects($options, 'annotations');
}
diff --git a/engine/lib/deprecated-1.8.php b/engine/lib/deprecated-1.8.php
index 37f9d7fdd..4e8b02d28 100644
--- a/engine/lib/deprecated-1.8.php
+++ b/engine/lib/deprecated-1.8.php
@@ -3107,8 +3107,8 @@ function clear_all_plugin_settings($plugin_id = "") {
function get_annotations($entity_guid = 0, $entity_type = "", $entity_subtype = "", $name = "",
$value = "", $owner_guid = 0, $limit = 10, $offset = 0, $order_by = "asc", $timelower = 0,
$timeupper = 0, $entity_owner_guid = 0) {
- global $CONFIG;
+ elgg_deprecated_notice('get_annotations() is deprecated by elgg_get_annotations()', 1.8);
$options = array();
if ($entity_guid) {
@@ -3139,7 +3139,7 @@ $timeupper = 0, $entity_owner_guid = 0) {
$options['offset'] = $offset;
if ($order_by == 'desc') {
- $options['order_by'] = 'a.time_created desc';
+ $options['order_by'] = 'n_table.time_created desc';
}
if ($timelower) {
@@ -3181,7 +3181,7 @@ function list_annotations($entity_guid, $name = "", $limit = 25, $asc = true) {
$options = array(
'guid' => $entity_guid,
'limit' => $limit,
- 'order_by' => "a.time_created $asc"
+ 'order_by' => "n_table.time_created $asc"
);
return elgg_list_annotations($options);
@@ -3233,7 +3233,7 @@ $timeupper = 0, $calculation = '') {
}
if ($order_by == 'desc') {
- $options['order_by'] = 'a.time_created desc';
+ $options['order_by'] = 'n_table.time_created desc';
}
if ($timelower) {
@@ -3479,4 +3479,102 @@ function set_view_location($view, $location, $viewtype = '') {
function register_entity_url_handler($function_name, $entity_type = "all", $entity_subtype = "all") {
elgg_deprecated_notice("register_entity_url_handler() was deprecated by elgg_register_entity_url_handler()", 1.8);
return elgg_register_entity_url_handler($entity_type, $entity_subtype, $function_name);
-} \ No newline at end of file
+}
+
+
+/**
+ * Get the metadata where the entities they are referring to match a given criteria.
+ *
+ * @param mixed $meta_name Metadata name
+ * @param mixed $meta_value Metadata value
+ * @param string $entity_type The type of entity to look for, eg 'site' or 'object'
+ * @param string $entity_subtype The subtype of the entity.
+ * @param int $limit Limit
+ * @param int $offset Offset
+ * @param string $order_by Optional ordering.
+ * @param int $site_guid Site GUID. 0 for current, -1 for any
+ *
+ * @return mixed
+ * @deprecated 1.8 Use elgg_get_metadata()
+ */
+function find_metadata($meta_name = "", $meta_value = "", $entity_type = "", $entity_subtype = "",
+ $limit = 10, $offset = 0, $order_by = "", $site_guid = 0) {
+
+ elgg_deprecated_notice('get_metadata() is deprecated by elgg_get_metadata()', 1.8);
+
+ $options = array();
+
+ if ($meta_name) {
+ $options['annotation_name'] = $meta_name;
+ }
+
+ if ($meta_value) {
+ $options['annotation_value'] = $meta_value;
+ }
+
+ if ($entity_type) {
+ $options['type'] = $entity_type;
+ }
+
+ if ($entity_subtype) {
+ $options['subtype'] = $entity_subtype;
+ }
+
+ $options['limit'] = $limit;
+ $options['offset'] = $offset;
+
+ if ($order_by == 'desc') {
+ $options['order_by'] = 'n_table.time_created desc';
+ }
+
+ if ($site_guid) {
+ $options['site_guid'] = $site_guid;
+ }
+
+ return elgg_get_metadata($options);
+}
+
+
+/**
+ * Get metadata objects by name.
+ *
+ * @param int $entity_guid Entity GUID
+ * @param string $meta_name Metadata name
+ *
+ * @return mixed ElggMetadata object, an array of ElggMetadata or false.
+ * @deprecated 1.8 Use elgg_get_metadata()
+ */
+function get_metadata_byname($entity_guid, $meta_name) {
+ elgg_deprecated_notice('get_metadata_byname() is deprecated by elgg_get_metadata()', 1.8);
+
+ $options = array(
+ 'guid' => $entity_guid,
+ 'metadata_name' => $meta_name
+ );
+
+ $md = elgg_get_metadata($options);
+
+ if ($md && count($md) == 1) {
+ return $md[0];
+ }
+
+ return $md;
+}
+
+/**
+ * Return all the metadata for a given GUID.
+ *
+ * @param int $entity_guid Entity GUID
+ *
+ * @return mixed
+ * @deprecated 1.8 Use elgg_get_metadata()
+ */
+function get_metadata_for_entity($entity_guid) {
+ elgg_deprecated_notice('get_metadata_for_entity() is deprecated by elgg_get_metadata()', 1.8);
+
+ $options = array(
+ 'guid' => $entity_guid
+ );
+
+ return elgg_get_metadata($options);
+}
diff --git a/engine/lib/metadata.php b/engine/lib/metadata.php
index 5935df43b..a9be5a962 100644
--- a/engine/lib/metadata.php
+++ b/engine/lib/metadata.php
@@ -322,174 +322,52 @@ function delete_metadata($id) {
}
/**
- * Get metadata objects by name.
+ * Returns metadata. Accepts all elgg_get_entities() options for entity
+ * restraints.
*
- * @param int $entity_guid Entity GUID
- * @param string $meta_name Metadata name
+ * @see elgg_get_entities
*
- * @return mixed ElggMetadata object, an array of ElggMetadata or false.
- */
-function get_metadata_byname($entity_guid, $meta_name) {
- global $CONFIG;
-
- $meta_name = get_metastring_id($meta_name);
-
- if (empty($meta_name)) {
- return false;
- }
-
- $entity_guid = (int)$entity_guid;
- $access = get_access_sql_suffix("e");
- $md_access = get_access_sql_suffix("m");
-
- // If memcache is available then cache this (cache only by name for now
- // since this is the most common query)
- $meta = null;
- static $metabyname_memcache;
- if ((!$metabyname_memcache) && (is_memcache_available())) {
- $metabyname_memcache = new ElggMemcache('metabyname_memcache');
- }
- if ($metabyname_memcache) {
- $meta = $metabyname_memcache->load("{$entity_guid}:{$meta_name}");
- }
- if ($meta) {
- return $meta;
- }
-
- $query = "SELECT m.*, n.string as name, v.string as value"
- . " from {$CONFIG->dbprefix}metadata m"
- . " JOIN {$CONFIG->dbprefix}entities e ON e.guid = m.entity_guid"
- . " JOIN {$CONFIG->dbprefix}metastrings v on m.value_id = v.id"
- . " JOIN {$CONFIG->dbprefix}metastrings n on m.name_id = n.id"
- . " where m.entity_guid=$entity_guid and m.name_id='$meta_name'"
- . " and $access and $md_access ORDER BY m.id ASC" ;
-
- $result = get_data($query, "row_to_elggmetadata");
-
- if (!$result) {
- return false;
- }
-
- // Cache if memcache available
- if ($metabyname_memcache) {
- if (count($result) == 1) {
- $r = $result[0];
- } else {
- $r = $result;
- }
- // This is a bit of a hack - we shorten the expiry on object
- // metadata so that it'll be gone in an hour. This means that
- // deletions and more importantly updates will filter through eventually.
- $metabyname_memcache->setDefaultExpiry(3600);
- $metabyname_memcache->save("{$entity_guid}:{$meta_name}", $r);
- }
- if (count($result) == 1) {
- return $result[0];
- }
-
- return $result;
-}
-
-/**
- * Return all the metadata for a given GUID.
+ * @param array $options Array in format:
*
- * @param int $entity_guid Entity GUID
+ * metadata_names => NULL|ARR metadata names
*
- * @return mixed
- */
-function get_metadata_for_entity($entity_guid) {
- global $CONFIG;
-
- $entity_guid = (int)$entity_guid;
- $access = get_access_sql_suffix("e");
- $md_access = get_access_sql_suffix("m");
-
- $query = "SELECT m.*, n.string as name, v.string as value
- from {$CONFIG->dbprefix}metadata m
- JOIN {$CONFIG->dbprefix}entities e ON e.guid = m.entity_guid
- JOIN {$CONFIG->dbprefix}metastrings v on m.value_id = v.id
- JOIN {$CONFIG->dbprefix}metastrings n on m.name_id = n.id
- where m.entity_guid=$entity_guid and $access and $md_access";
-
- return get_data($query, "row_to_elggmetadata");
-}
-
-/**
- * Get the metadata where the entities they are referring to match a given criteria.
+ * metadata_values => NULL|ARR metadata values
+ *
+ * metadata_case_sensitive => BOOL Overall Case sensitive
*
- * @param mixed $meta_name Metadata name
- * @param mixed $meta_value Metadata value
- * @param string $entity_type The type of entity to look for, eg 'site' or 'object'
- * @param string $entity_subtype The subtype of the entity.
- * @param int $limit Limit
- * @param int $offset Offset
- * @param string $order_by Optional ordering.
- * @param int $site_guid Site GUID. 0 for current, -1 for any
+ * metadata_owner_guids => NULL|ARR guids for metadata owners
+ *
+ * metadata_created_time_lower => INT Lower limit for created time.
+ *
+ * metadata_created_time_upper => INT Upper limit for created time.
+ *
+ * metadata_calculation => STR Perform the MySQL function on the metadata values returned.
*
* @return mixed
+ * @since 1.8.0
*/
-function find_metadata($meta_name = "", $meta_value = "", $entity_type = "", $entity_subtype = "",
- $limit = 10, $offset = 0, $order_by = "", $site_guid = 0) {
- global $CONFIG;
-
- $meta_n = get_metastring_id($meta_name);
- $meta_v = get_metastring_id($meta_value);
-
- $entity_type = sanitise_string($entity_type);
- $entity_subtype = get_subtype_id($entity_type, $entity_subtype);
- $limit = (int)$limit;
- $offset = (int)$offset;
- if ($order_by == "") {
- $order_by = "e.time_created desc";
- }
-
- $order_by = sanitise_string($order_by);
- $site_guid = (int) $site_guid;
- if ($site_guid == 0) {
- $site_guid = $CONFIG->site_guid;
- }
-
- $where = array();
-
- if ($entity_type != "") {
- $where[] = "e.type='$entity_type'";
- }
+function elgg_get_metadata($options) {
+ // map the metadata_* options to metastring_* options
+ $map = array(
+ 'metadata_names' => 'metastring_names',
+ 'metadata_values' => 'metastring_values',
+ 'metadata_case_sensitive' => 'metastring_case_sensitive',
+ 'metadata_owner_guids' => 'metastring_owner_guids',
+ 'metadata_created_time_lower' => 'metastring_created_time_lower',
+ 'metadata_created_time_upper' => 'metastring_created_time_upper',
+ 'metadata_calculation' => 'metastring_calculation'
+ );
- if ($entity_subtype) {
- $where[] = "e.subtype=$entity_subtype";
- }
+ $singulars = array('metadata_name', 'metadata_value');
+ $options = elgg_normalise_plural_options_array($options, $singulars);
- if ($meta_name != "") {
- if (!$meta_v) {
- // The value is set, but we didn't get a value... so something went wrong.
- return false;
- }
- $where[] = "m.name_id='$meta_n'";
- }
- if ($meta_value != "") {
- // The value is set, but we didn't get a value... so something went wrong.
- if (!$meta_v) {
- return false;
+ foreach ($map as $ann => $ms) {
+ if (isset($options[$ann])) {
+ $options[$ms] = $options[$ann];
}
- $where[] = "m.value_id='$meta_v'";
- }
- if ($site_guid > 0) {
- $where[] = "e.site_guid = {$site_guid}";
- }
-
- $query = "SELECT m.*, n.string as name, v.string as value from {$CONFIG->dbprefix}entities e
- JOIN {$CONFIG->dbprefix}metadata m on e.guid = m.entity_guid
- JOIN {$CONFIG->dbprefix}metastrings v on m.value_id = v.id
- JOIN {$CONFIG->dbprefix}metastrings n on m.name_id = n.id where";
-
- foreach ($where as $w) {
- $query .= " $w and ";
}
- $query .= get_access_sql_suffix("e"); // Add access controls
- $query .= ' and ' . get_access_sql_suffix("m"); // Add access controls
- $query .= " order by $order_by limit $offset, $limit"; // Add order and limit
- return get_data($query, "row_to_elggmetadata");
+ return elgg_get_metastring_based_objects($options, 'metadata');
}
/**
diff --git a/engine/lib/metastrings.php b/engine/lib/metastrings.php
index 28f8475da..6009f0882 100644
--- a/engine/lib/metastrings.php
+++ b/engine/lib/metastrings.php
@@ -200,3 +200,323 @@ function delete_orphaned_metastrings() {
return delete_data($query);
}
+
+
+/**
+ * Returns an array of either ElggAnnotation or ElggMetadata objects.
+ * Accepts all elgg_get_entities() options for entity restraints.
+ *
+ * @see elgg_get_entities
+ *
+ * @param array $options Array in format:
+ *
+ * metastring_names => NULL|ARR metastring names
+ *
+ * metastring_values => NULL|ARR metastring values
+ *
+ * metastring_case_sensitive => BOOL Overall Case sensitive
+ *
+ * metastring_owner_guids => NULL|ARR guids for metadata owners
+ *
+ * metastring_created_time_lower => INT Lower limit for created time.
+ *
+ * metastring_created_time_upper => INT Upper limit for created time.
+ *
+ * metastring_calculation => STR Perform the MySQL function on the metastring values returned.
+ *
+ * @param string $type Either metadata or annotations
+ * @return mixed
+ * @access private
+ */
+function elgg_get_metastring_based_objects($options, $type = 'metadata') {
+
+ if ($type != 'metadata' && $type != 'annotations') {
+ return false;
+ }
+
+ $defaults = array(
+ // entities
+ 'types' => ELGG_ENTITIES_ANY_VALUE,
+ 'subtypes' => ELGG_ENTITIES_ANY_VALUE,
+ 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE,
+
+ 'guids' => ELGG_ENTITIES_ANY_VALUE,
+ 'owner_guids' => ELGG_ENTITIES_ANY_VALUE,
+ 'container_guids' => ELGG_ENTITIES_ANY_VALUE,
+ 'site_guids' => get_config('site_guid'),
+
+ 'modified_time_lower' => ELGG_ENTITIES_ANY_VALUE,
+ 'modified_time_upper' => ELGG_ENTITIES_ANY_VALUE,
+ 'created_time_lower' => ELGG_ENTITIES_ANY_VALUE,
+ 'created_time_upper' => ELGG_ENTITIES_ANY_VALUE,
+
+ // options are normalized to the plural in case we ever add support for them.
+ 'metastring_names' => ELGG_ENTITIES_ANY_VALUE,
+ 'metastring_values' => ELGG_ENTITIES_ANY_VALUE,
+ //'metastring_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE,
+ //'metastring_name_value_pairs_operator' => 'AND',
+
+ 'metastring_case_sensitive' => TRUE,
+ //'order_by_metastring' => array(),
+ 'metastring_calculation' => ELGG_ENTITIES_NO_VALUE,
+
+ 'metastring_created_time_lower' => ELGG_ENTITIES_ANY_VALUE,
+ 'metastring_created_time_upper' => ELGG_ENTITIES_ANY_VALUE,
+
+ 'metastring_owner_guids' => ELGG_ENTITIES_ANY_VALUE,
+
+ // sql
+ 'order_by' => 'n_table.time_created asc',
+ 'limit' => 10,
+ 'offset' => 0,
+ 'count' => FALSE,
+ 'selects' => array(),
+ 'wheres' => array(),
+ 'joins' => array(),
+
+ 'callback' => ($type == 'annotations') ? 'row_to_elggannotation' : 'row_to_elggmetadata'
+ );
+
+ $options = array_merge($defaults, $options);
+
+ // can't use helper function with type_subtype_pair because
+ // it's already an array...just need to merge it
+ if (isset($options['type_subtype_pair'])) {
+ if (isset($options['type_subtype_pairs'])) {
+ $options['type_subtype_pairs'] = array_merge($options['type_subtype_pairs'],
+ $options['type_subtype_pair']);
+ } else {
+ $options['type_subtype_pairs'] = $options['type_subtype_pair'];
+ }
+ }
+
+ $singulars = array('type', 'subtype', 'guid', 'owner_guid', 'container_guid', 'site_guid',
+ 'metastring_name', 'metastring_value'
+ );
+ $options = elgg_normalise_plural_options_array($options, $singulars);
+
+ if (!$options) {
+ return false;
+ }
+
+ $db_prefix = elgg_get_config('dbprefix');
+
+ // evaluate where clauses
+ if (!is_array($options['wheres'])) {
+ $options['wheres'] = array($options['wheres']);
+ }
+
+ $wheres = $options['wheres'];
+
+ // entities
+ $wheres[] = elgg_get_entity_type_subtype_where_sql('e', $options['types'],
+ $options['subtypes'], $options['type_subtype_pairs']);
+
+ $wheres[] = elgg_get_guid_based_where_sql('e.guid', $options['guids']);
+ $wheres[] = elgg_get_guid_based_where_sql('e.owner_guid', $options['owner_guids']);
+ $wheres[] = elgg_get_guid_based_where_sql('e.container_guid', $options['container_guids']);
+ $wheres[] = elgg_get_guid_based_where_sql('e.site_guid', $options['site_guids']);
+
+ $wheres[] = elgg_get_entity_time_where_sql('e', $options['created_time_upper'],
+ $options['created_time_lower'], $options['modified_time_upper'], $options['modified_time_lower']);
+
+
+ $wheres[] = elgg_get_entity_time_where_sql('n_table', $options['metastring_created_time_upper'],
+ $options['metastring_created_time_lower'], null, null);
+
+ $wheres[] = elgg_get_guid_based_where_sql('n_table.owner_guid', $options['metastring_owner_guids']);
+
+ // 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]);
+ }
+ }
+
+ // evaluate join clauses
+ if (!is_array($options['joins'])) {
+ $options['joins'] = array($options['joins']);
+ }
+
+ $joins = $options['joins'];
+
+ $joins[] = "JOIN {$db_prefix}entities e ON n_table.entity_guid = e.guid";
+ $joins[] = "JOIN {$db_prefix}metastrings n on n_table.name_id = n.id";
+ $joins[] = "JOIN {$db_prefix}metastrings v on n_table.value_id = v.id";
+
+
+ // remove identical join clauses
+ $joins = array_unique($joins);
+
+ foreach ($joins as $i => $join) {
+ if ($join === FALSE) {
+ return FALSE;
+ } elseif (empty($join)) {
+ unset($joins[$i]);
+ }
+ }
+
+ // metastrings
+ $metastring_clauses = elgg_get_metastring_sql('n_table', $options['metastring_names'],
+ $options['metastring_values'], $options['metastring_case_sensitive']);
+
+ if ($metastring_clauses) {
+ $wheres = array_merge($wheres, $metastring_clauses['wheres']);
+ $joins = array_merge($joins, $metastring_clauses['joins']);
+ }
+
+ // check for calculations
+ if ($options['count']) {
+ $options['metastring_calculation'] = 'count';
+ }
+
+ if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE) {
+ $query = "SELECT DISTINCT n_table.*, n.string as name, v.string as value FROM {$db_prefix}$type n_table";
+ } else {
+ $query = "SELECT DISTINCT v.string as value, {$options['metastring_calculation']}(v.string) as calculation FROM {$db_prefix}$type n_table";
+ }
+
+ // add joins
+ foreach ($joins as $j) {
+ $query .= " $j ";
+ }
+
+ // add wheres
+ $query .= ' WHERE ';
+
+ foreach ($wheres as $w) {
+ $query .= " $w AND ";
+ }
+
+ // Add access controls
+ $query .= get_access_sql_suffix('e');
+ if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE) {
+ if ($options['group_by'] = sanitise_string($options['group_by'])) {
+ $query .= " GROUP BY {$options['group_by']}";
+ }
+
+ if ($options['order_by'] = sanitise_string($options['order_by'])) {
+ $query .= " ORDER BY {$options['order_by']}, n_table.id";
+ }
+
+ if ($options['limit']) {
+ $limit = sanitise_int($options['limit']);
+ $offset = sanitise_int($options['offset']);
+ $query .= " LIMIT $offset, $limit";
+ }
+
+ $dt = get_data($query, $options['callback']);
+ return $dt;
+ } else {
+ $result = get_data_row($query);
+ return $result->calculation;
+ }
+}
+
+
+/**
+ * Returns an array of joins and wheres for use in metastrings.
+ *
+ * @note The $pairs is reserved for name/value pairs if we want to implement those.
+ *
+ * @param string $table The annotation or metadata table name or alias
+ * @param array $names An array of names
+ * @param array $values An array of values
+ * @param array $pairs Name / value pairs. Not currently used.
+ * @param bool $case_sensitive Should name and values be case sensitive?
+ *
+ * @return array
+ */
+function elgg_get_metastring_sql($table, $names = null, $values = null,
+ $pairs = null, $case_sensitive = false) {
+
+ if ((!$names && $names !== 0)
+ && (!$values && $values !== 0)
+ && (!$pairs && $pairs !== 0)) {
+
+ return '';
+ }
+
+ $db_prefix = elgg_get_config('dbprefix');
+
+ // join counter for incremental joins.
+ $i = 1;
+
+ // binary forces byte-to-byte comparision of strings, making
+ // it case- and diacritical-mark- sensitive.
+ // only supported on values.
+ $binary = ($case_sensitive) ? ' BINARY ' : '';
+
+ $access = get_access_sql_suffix($table);
+
+ $return = array (
+ 'joins' => array (),
+ 'wheres' => array()
+ );
+
+ $wheres = array();
+
+ // get names wheres and joins
+ $names_where = '';
+ if ($names !== NULL) {
+ if (!is_array($names)) {
+ $names = array($names);
+ }
+
+ $sanitised_names = array();
+ foreach ($names as $name) {
+ // normalise to 0.
+ if (!$name) {
+ $name = '0';
+ }
+ $sanitised_names[] = '\'' . sanitise_string($name) . '\'';
+ }
+
+ if ($names_str = implode(',', $sanitised_names)) {
+ $return['joins'][] = "JOIN {$db_prefix}metastrings msn on $table.name_id = msn.id";
+ $names_where = "(msn.string IN ($names_str))";
+ }
+ }
+
+ // get values wheres and joins
+ $values_where = '';
+ if ($values !== NULL) {
+ if (!is_array($values)) {
+ $values = array($values);
+ }
+
+ $sanitised_values = array();
+ foreach ($values as $value) {
+ // normalize to 0
+ if (!$value) {
+ $value = 0;
+ }
+ $sanitised_values[] = '\'' . sanitise_string($value) . '\'';
+ }
+
+ if ($values_str = implode(',', $sanitised_values)) {
+ $return['joins'][] = "JOIN {$db_prefix}metastrings msv on $table.value_id = msv.id";
+ $values_where = "({$binary}msv.string IN ($values_str))";
+ }
+ }
+
+ if ($names_where && $values_where) {
+ $wheres[] = "($names_where AND $values_where AND $access)";
+ } elseif ($names_where) {
+ $wheres[] = "($names_where AND $access)";
+ } elseif ($values_where) {
+ $wheres[] = "($values_where AND $access)";
+ }
+
+ if ($where = implode(' AND ', $wheres)) {
+ $return['wheres'][] = "($where)";
+ }
+
+ return $return;
+}