diff options
-rw-r--r-- | engine/lib/annotations.php | 305 | ||||
-rw-r--r-- | engine/lib/deprecated-1.8.php | 108 | ||||
-rw-r--r-- | engine/lib/metadata.php | 188 | ||||
-rw-r--r-- | engine/lib/metastrings.php | 320 |
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; +} |