Elgg  Version 1.12
metastrings.php
Go to the documentation of this file.
1 <?php
25 function elgg_get_metastring_id($string, $case_sensitive = true) {
26  return _elgg_services()->metastringsTable->getId($string, $case_sensitive);
27 }
28 
38  return _elgg_services()->metastringsTable->add($string);
39 }
40 
78 
79  switch ($options['metastring_type']) {
80  case 'metadata':
81  $type = 'metadata';
82  $callback = 'row_to_elggmetadata';
83  break;
84 
85  case 'annotations':
86  case 'annotation':
87  $type = 'annotations';
88  $callback = 'row_to_elggannotation';
89  break;
90 
91  default:
92  return false;
93  }
94 
95  $defaults = array(
96  // entities
97  'types' => ELGG_ENTITIES_ANY_VALUE,
98  'subtypes' => ELGG_ENTITIES_ANY_VALUE,
99  'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE,
100 
101  'guids' => ELGG_ENTITIES_ANY_VALUE,
102  'owner_guids' => ELGG_ENTITIES_ANY_VALUE,
103  'container_guids' => ELGG_ENTITIES_ANY_VALUE,
104  'site_guids' => get_config('site_guid'),
105 
106  'modified_time_lower' => ELGG_ENTITIES_ANY_VALUE,
107  'modified_time_upper' => ELGG_ENTITIES_ANY_VALUE,
108  'created_time_lower' => ELGG_ENTITIES_ANY_VALUE,
109  'created_time_upper' => ELGG_ENTITIES_ANY_VALUE,
110 
111  // options are normalized to the plural in case we ever add support for them.
112  'metastring_names' => ELGG_ENTITIES_ANY_VALUE,
113  'metastring_values' => ELGG_ENTITIES_ANY_VALUE,
114  //'metastring_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE,
115  //'metastring_name_value_pairs_operator' => 'AND',
116 
117  'metastring_case_sensitive' => true,
118  //'order_by_metastring' => array(),
119  'metastring_calculation' => ELGG_ENTITIES_NO_VALUE,
120 
121  'metastring_created_time_lower' => ELGG_ENTITIES_ANY_VALUE,
122  'metastring_created_time_upper' => ELGG_ENTITIES_ANY_VALUE,
123 
124  'metastring_owner_guids' => ELGG_ENTITIES_ANY_VALUE,
125 
126  'metastring_ids' => ELGG_ENTITIES_ANY_VALUE,
127 
128  // sql
129  'order_by' => 'n_table.time_created ASC, n_table.id ASC',
130  'limit' => elgg_get_config('default_limit'),
131  'offset' => 0,
132  'count' => false,
133  'selects' => array(),
134  'wheres' => array(),
135  'joins' => array(),
136 
137  'distinct' => true,
138  'preload_owners' => false,
139  'callback' => $callback,
140  );
141 
142  // @todo Ignore site_guid right now because of #2910
143  $options['site_guid'] = ELGG_ENTITIES_ANY_VALUE;
144 
145  $options = array_merge($defaults, $options);
146 
147  // can't use helper function with type_subtype_pair because
148  // it's already an array...just need to merge it
149  if (isset($options['type_subtype_pair'])) {
150  if (isset($options['type_subtype_pairs'])) {
151  $options['type_subtype_pairs'] = array_merge($options['type_subtype_pairs'],
152  $options['type_subtype_pair']);
153  } else {
154  $options['type_subtype_pairs'] = $options['type_subtype_pair'];
155  }
156  }
157 
158  $singulars = array(
159  'type', 'subtype', 'type_subtype_pair',
160  'guid', 'owner_guid', 'container_guid', 'site_guid',
161  'metastring_name', 'metastring_value',
162  'metastring_owner_guid', 'metastring_id',
163  'select', 'where', 'join'
164  );
165 
167 
168  if (!$options) {
169  return false;
170  }
171 
172  $db_prefix = elgg_get_config('dbprefix');
173 
174  // evaluate where clauses
175  if (!is_array($options['wheres'])) {
176  $options['wheres'] = array($options['wheres']);
177  }
178 
179  $wheres = $options['wheres'];
180 
181  // entities
182  $wheres[] = _elgg_get_entity_type_subtype_where_sql('e', $options['types'],
183  $options['subtypes'], $options['type_subtype_pairs']);
184 
185  $wheres[] = _elgg_get_guid_based_where_sql('e.guid', $options['guids']);
186  $wheres[] = _elgg_get_guid_based_where_sql('e.owner_guid', $options['owner_guids']);
187  $wheres[] = _elgg_get_guid_based_where_sql('e.container_guid', $options['container_guids']);
188  $wheres[] = _elgg_get_guid_based_where_sql('e.site_guid', $options['site_guids']);
189 
190  $wheres[] = _elgg_get_entity_time_where_sql('e', $options['created_time_upper'],
191  $options['created_time_lower'], $options['modified_time_upper'], $options['modified_time_lower']);
192 
193 
194  $wheres[] = _elgg_get_entity_time_where_sql('n_table', $options['metastring_created_time_upper'],
195  $options['metastring_created_time_lower'], null, null);
196 
197  $wheres[] = _elgg_get_guid_based_where_sql('n_table.owner_guid',
198  $options['metastring_owner_guids']);
199 
200  // see if any functions failed
201  // remove empty strings on successful functions
202  foreach ($wheres as $i => $where) {
203  if ($where === false) {
204  return false;
205  } elseif (empty($where)) {
206  unset($wheres[$i]);
207  }
208  }
209 
210  // remove identical where clauses
211  $wheres = array_unique($wheres);
212 
213  // evaluate join clauses
214  if (!is_array($options['joins'])) {
215  $options['joins'] = array($options['joins']);
216  }
217 
218  $joins = array();
219  $joins[] = "JOIN {$db_prefix}entities e ON n_table.entity_guid = e.guid";
220 
221  // evaluate selects
222  if (!is_array($options['selects'])) {
223  $options['selects'] = array($options['selects']);
224  }
225 
226  $selects = $options['selects'];
227 
228  // For performance reasons we don't want the joins required for metadata / annotations
229  // unless we're going through one of their callbacks.
230  // this means we expect the functions passing different callbacks to pass their required joins.
231  // If we're doing a calculation
232  $custom_callback = ($options['callback'] == 'row_to_elggmetadata'
233  || $options['callback'] == 'row_to_elggannotation');
234  $is_calculation = $options['metastring_calculation'] ? true : false;
235 
236  if ($custom_callback || $is_calculation) {
237  $joins[] = "JOIN {$db_prefix}metastrings n on n_table.name_id = n.id";
238  $joins[] = "JOIN {$db_prefix}metastrings v on n_table.value_id = v.id";
239 
240  $selects[] = 'n.string as name';
241  $selects[] = 'v.string as value';
242  }
243 
244  // add optional joins
245  $joins = array_merge($joins, $options['joins']);
246 
247  // metastrings
248  $metastring_clauses = _elgg_get_metastring_sql('n_table', $options['metastring_names'],
249  $options['metastring_values'], null, $options['metastring_ids'],
250  $options['metastring_case_sensitive']);
251 
252  if ($metastring_clauses) {
253  $wheres = array_merge($wheres, $metastring_clauses['wheres']);
254  $joins = array_merge($joins, $metastring_clauses['joins']);
255  } else {
256  $wheres[] = _elgg_get_access_where_sql(array(
257  'table_alias' => 'n_table',
258  'guid_column' => 'entity_guid',
259  ));
260  }
261 
262  $distinct = $options['distinct'] ? "DISTINCT " : "";
263 
264  if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) {
265  $selects = array_unique($selects);
266  // evalutate selects
267  $select_str = '';
268  if ($selects) {
269  foreach ($selects as $select) {
270  $select_str .= ", $select";
271  }
272  }
273 
274  $query = "SELECT $distinct n_table.*{$select_str} FROM {$db_prefix}$type n_table";
275  } elseif ($options['count']) {
276  // count is over the entities
277  $query = "SELECT count($distinct e.guid) as calculation FROM {$db_prefix}$type n_table";
278  } else {
279  $query = "SELECT {$options['metastring_calculation']}(v.string) as calculation FROM {$db_prefix}$type n_table";
280  }
281 
282  foreach ($joins as $i => $join) {
283  if ($join === false) {
284  return false;
285  } elseif (empty($join)) {
286  unset($joins[$i]);
287  }
288  }
289 
290  // remove identical join clauses
291  $joins = array_unique($joins);
292 
293  // add joins
294  foreach ($joins as $j) {
295  $query .= " $j ";
296  }
297 
298  // add wheres
299  $query .= ' WHERE ';
300 
301  foreach ($wheres as $w) {
302  $query .= " $w AND ";
303  }
304 
305  // Add access controls
306  $query .= _elgg_get_access_where_sql(array('table_alias' => 'e'));
307 
308  // reverse order by
309  if (isset($options['reverse_order_by']) && $options['reverse_order_by']) {
310  $options['order_by'] = _elgg_sql_reverse_order_by_clause($options['order_by']);
311  }
312 
313  if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) {
314  if (isset($options['group_by'])) {
315  $options['group_by'] = sanitise_string($options['group_by']);
316  $query .= " GROUP BY {$options['group_by']}";
317  }
318 
319  if (isset($options['order_by']) && $options['order_by']) {
320  $options['order_by'] = sanitise_string($options['order_by']);
321  $query .= " ORDER BY {$options['order_by']}, n_table.id";
322  }
323 
324  if ($options['limit']) {
325  $limit = sanitise_int($options['limit']);
326  $offset = sanitise_int($options['offset'], false);
327  $query .= " LIMIT $offset, $limit";
328  }
329 
330  $dt = get_data($query, $options['callback']);
331 
332  if ($options['preload_owners'] && is_array($dt) && count($dt) > 1) {
333  _elgg_services()->entityPreloader->preload($dt, ['owner_guid']);
334  }
335 
336  return $dt;
337  } else {
338  $result = get_data_row($query);
339  return $result->calculation;
340  }
341 }
342 
358 function _elgg_get_metastring_sql($table, $names = null, $values = null,
359  $pairs = null, $ids = null, $case_sensitive = false) {
360 
361  if ((!$names && $names !== 0)
362  && (!$values && $values !== 0)
363  && !$ids
364  && (!$pairs && $pairs !== 0)) {
365 
366  return array();
367  }
368 
369  $db_prefix = elgg_get_config('dbprefix');
370 
371  // binary forces byte-to-byte comparision of strings, making
372  // it case- and diacritical-mark- sensitive.
373  // only supported on values.
374  $binary = ($case_sensitive) ? ' BINARY ' : '';
375 
376  $return = array (
377  'joins' => array (),
378  'wheres' => array()
379  );
380 
381  $wheres = array();
382 
383  // get names wheres and joins
384  $names_where = '';
385  if ($names !== null) {
386  if (!is_array($names)) {
387  $names = array($names);
388  }
389 
390  $sanitised_names = array();
391  foreach ($names as $name) {
392  // normalise to 0.
393  if (!$name) {
394  $name = '0';
395  }
396  $sanitised_names[] = '\'' . sanitise_string($name) . '\'';
397  }
398 
399  if ($names_str = implode(',', $sanitised_names)) {
400  $return['joins'][] = "JOIN {$db_prefix}metastrings msn on $table.name_id = msn.id";
401  $names_where = "(msn.string IN ($names_str))";
402  }
403  }
404 
405  // get values wheres and joins
406  $values_where = '';
407  if ($values !== null) {
408  if (!is_array($values)) {
409  $values = array($values);
410  }
411 
412  $sanitised_values = array();
413  foreach ($values as $value) {
414  // normalize to 0
415  if (!$value) {
416  $value = 0;
417  }
418  $sanitised_values[] = '\'' . sanitise_string($value) . '\'';
419  }
420 
421  if ($values_str = implode(',', $sanitised_values)) {
422  $return['joins'][] = "JOIN {$db_prefix}metastrings msv on $table.value_id = msv.id";
423  $values_where = "({$binary}msv.string IN ($values_str))";
424  }
425  }
426 
427  if ($ids !== null) {
428  if (!is_array($ids)) {
429  $ids = array($ids);
430  }
431 
432  $ids_str = implode(',', $ids);
433 
434  if ($ids_str) {
435  $wheres[] = "n_table.id IN ($ids_str)";
436  }
437  }
438 
439  if ($names_where && $values_where) {
440  $wheres[] = "($names_where AND $values_where)";
441  } elseif ($names_where) {
442  $wheres[] = $names_where;
443  } elseif ($values_where) {
444  $wheres[] = $values_where;
445  }
446 
447  $wheres[] = _elgg_get_access_where_sql(array(
448  'table_alias' => $table,
449  'guid_column' => 'entity_guid',
450  ));
451 
452  if ($where = implode(' AND ', $wheres)) {
453  $return['wheres'][] = "($where)";
454  }
455 
456  return $return;
457 }
458 
467 
468  // support either metastrings_type or metastring_type
469  // because I've made this mistake many times and hunting it down is a pain...
470  $type = elgg_extract('metastring_type', $options, null);
471  $type = elgg_extract('metastrings_type', $options, $type);
472 
473  $options['metastring_type'] = $type;
474 
475  // support annotation_ and annotations_ because they're way too easy to confuse
476  $prefixes = array('metadata_', 'annotation_', 'annotations_');
477 
478  // map the metadata_* options to metastring_* options
479  $map = array(
480  'names' => 'metastring_names',
481  'values' => 'metastring_values',
482  'case_sensitive' => 'metastring_case_sensitive',
483  'owner_guids' => 'metastring_owner_guids',
484  'created_time_lower' => 'metastring_created_time_lower',
485  'created_time_upper' => 'metastring_created_time_upper',
486  'calculation' => 'metastring_calculation',
487  'ids' => 'metastring_ids',
488  );
489 
490  foreach ($prefixes as $prefix) {
491  $singulars = array("{$prefix}name", "{$prefix}value", "{$prefix}owner_guid", "{$prefix}id");
493 
494  foreach ($map as $specific => $normalized) {
495  $key = $prefix . $specific;
496  if (isset($options[$key])) {
497  $options[$normalized] = $options[$key];
498  }
499  }
500  }
501 
502  return $options;
503 }
504 
520  $id = (int)$id;
521  $db_prefix = elgg_get_config('dbprefix');
522 
524 
525  switch ($type) {
526  case 'annotation':
527  case 'annotations':
528  $type = 'annotation';
529  $table = "{$db_prefix}annotations";
530  break;
531 
532  case 'metadata':
533  $table = "{$db_prefix}metadata";
534  break;
535  }
536 
537  if ($enabled === 'yes' || $enabled === 1 || $enabled === true) {
538  $enabled = 'yes';
539  $event = 'enable';
540  } elseif ($enabled === 'no' || $enabled === 0 || $enabled === false) {
541  $enabled = 'no';
542  $event = 'disable';
543  } else {
544  return false;
545  }
546 
547  $return = false;
548 
549  if ($object) {
550  // don't set it if it's already set.
551  if ($object->enabled == $enabled) {
552  $return = false;
553  } elseif ($object->canEdit() && (elgg_trigger_event($event, $type, $object))) {
554  $return = update_data("UPDATE $table SET enabled = '$enabled' where id = $id");
555  }
556  }
557 
558  return $return;
559 }
560 
576 function _elgg_batch_metastring_based_objects(array $options, $callback, $inc_offset = true) {
577  if (!$options || !is_array($options)) {
578  return false;
579  }
580 
581  $batch = new \ElggBatch('_elgg_get_metastring_based_objects', $options, $callback, 50, $inc_offset);
582  return $batch->callbackResult;
583 }
584 
594  $id = (int)$id;
595  if (!$id) {
596  return false;
597  }
598 
599  $options = array(
600  'metastring_type' => $type,
601  'metastring_id' => $id,
602  );
603 
605 
606  if ($obj && count($obj) == 1) {
607  return $obj[0];
608  }
609 
610  return false;
611 }
612 
622  $id = (int)$id;
623  $db_prefix = elgg_get_config('dbprefix');
624 
625  switch ($type) {
626  case 'annotations':
627  case 'annotation':
628  $table = $db_prefix . 'annotations';
629  $type = 'annotation';
630  break;
631 
632  case 'metadata':
633  $table = $db_prefix . 'metadata';
634  $type = 'metadata';
635  break;
636 
637  default:
638  return false;
639  }
640 
642 
643  if ($obj) {
644  // Tidy up if memcache is enabled.
645  // @todo only metadata is supported
646  if ($type == 'metadata') {
647  static $metabyname_memcache;
648  if ((!$metabyname_memcache) && (is_memcache_available())) {
649  $metabyname_memcache = new \ElggMemcache('metabyname_memcache');
650  }
651 
652  if ($metabyname_memcache) {
653  // @todo why name_id? is that even populated?
654  $metabyname_memcache->delete("{$obj->entity_guid}:{$obj->name_id}");
655  }
656  }
657 
658  if ($obj->canEdit()) {
659  // bc code for when we triggered 'delete', 'annotations' #4770
660  $result = true;
661  if ($type == "annotation") {
662  $result = elgg_trigger_event('delete', 'annotations', $obj);
663  if ($result === false) {
664  elgg_deprecated_notice("Use the event 'delete', 'annotation'", 1.9);
665  }
666  }
667 
668  if (elgg_trigger_event('delete', $type, $obj) && $result) {
669  return (bool)delete_data("DELETE FROM $table WHERE id = $id");
670  }
671  }
672  }
673 
674  return false;
675 }
676 
687  $valid_types = array('metadata', 'annotation');
688  if (!in_array($type, $valid_types)) {
689  return false;
690  }
691 
692  // the options for annotations are singular (annotation_name) but the table
693  // is plural (elgg_annotations) so rewrite for the table name.
694  $n_table = ($type == 'annotation') ? 'annotations' : $type;
695 
696  $singulars = array("{$type}_name", "{$type}_value",
697  "{$type}_name_value_pair", "{$type}_owner_guid");
699 
700  $clauses = _elgg_get_entity_metadata_where_sql('e', $n_table, $options["{$type}_names"],
701  $options["{$type}_values"], $options["{$type}_name_value_pairs"],
702  $options["{$type}_name_value_pairs_operator"], $options["{$type}_case_sensitive"],
703  $options["order_by_{$type}"], $options["{$type}_owner_guids"]);
704 
705  if ($clauses) {
706  // merge wheres to pass to elgg_get_entities()
707  if (isset($options['wheres']) && !is_array($options['wheres'])) {
708  $options['wheres'] = array($options['wheres']);
709  } elseif (!isset($options['wheres'])) {
710  $options['wheres'] = array();
711  }
712 
713  $options['wheres'] = array_merge($options['wheres'], $clauses['wheres']);
714 
715  // merge joins to pass to elgg_get_entities()
716  if (isset($options['joins']) && !is_array($options['joins'])) {
717  $options['joins'] = array($options['joins']);
718  } elseif (!isset($options['joins'])) {
719  $options['joins'] = array();
720  }
721 
722  $options['joins'] = array_merge($options['joins'], $clauses['joins']);
723 
724  if ($clauses['orders']) {
725  $order_by_metadata = implode(", ", $clauses['orders']);
726  if (isset($options['order_by']) && $options['order_by']) {
727  $options['order_by'] = "$order_by_metadata, {$options['order_by']}";
728  } else {
729  $options['order_by'] = "$order_by_metadata, e.time_created DESC";
730  }
731  }
732  }
733 
734  return $options;
735 }
736 
748  global $CONFIG;
749  $value[] = $CONFIG->path . 'engine/tests/ElggCoreMetastringsTest.php';
750  return $value;
751 }
752 
753 return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
754  $hooks->registerHandler('unit_test', 'system', '_elgg_metastrings_test');
755 };
_elgg_entities_get_metastrings_options($type, $options)
Returns options to pass to elgg_get_entities() for metastrings operations.
elgg_get_config($name, $site_guid=0)
Get an Elgg configuration value.
_elgg_batch_metastring_based_objects(array $options, $callback, $inc_offset=true)
Runs metastrings-based objects found using $options through $callback.
elgg_get_metastring_id($string, $case_sensitive=true)
Gets the metastring identifier for a value.
Definition: metastrings.php:25
get_data_row($query, $callback="")
Retrieve a single row from the database.
Definition: database.php:66
$table
Definition: cron.php:28
if($guid==elgg_get_logged_in_user_guid()) $name
Definition: delete.php:21
$object
Definition: upgrade.php:12
$defaults
const ELGG_ENTITIES_NO_VALUE
Definition: elgglib.php:2015
_elgg_get_guid_based_where_sql($column, $guids)
Returns SQL where clause for owner and containers.
Definition: entities.php:539
get_config($name, $site_guid=0)
Gets a configuration value.
_elgg_get_entity_time_where_sql($table, $time_created_upper=null, $time_created_lower=null, $time_updated_upper=null, $time_updated_lower=null)
Returns SQL where clause for entity time limits.
Definition: entities.php:557
$value
Definition: longtext.php:26
$return
Definition: opendd.php:15
if(!$count) $offset
Definition: pagination.php:26
delete_data($query)
Remove a row from the database.
Definition: database.php:106
_elgg_get_metastring_sql($table, $names=null, $values=null, $pairs=null, $ids=null, $case_sensitive=false)
Returns an array of joins and wheres for use in metastrings.
_elgg_sql_reverse_order_by_clause($order_by)
Reverses the ordering in an ORDER BY clause.
Definition: elgglib.php:1658
update_data($query)
Update a row in the database.
Definition: database.php:93
$string
$options
Definition: index.php:14
_elgg_get_entity_metadata_where_sql($e_table, $n_table, $names=null, $values=null, $pairs=null, $pair_operator= 'AND', $case_sensitive=true, $order_by_metadata=null, $owner_guids=null)
Returns metadata name and value SQL where for entities.
Definition: metadata.php:296
$limit
Definition: userpicker.php:38
$key
Definition: summary.php:34
_elgg_delete_metastring_based_object_by_id($id, $type)
Deletes a metastring-based object by its id.
_elgg_services()
Definition: autoloader.php:14
_elgg_get_metastring_based_object_from_id($id, $type)
Returns a singular metastring-based object by its ID.
global $CONFIG
$enabled
Sample cli installer script.
sanitise_string($string)
Wrapper function for alternate English spelling (.
Definition: database.php:150
_elgg_get_metastring_based_objects($options)
Returns an array of either or objects.
Definition: metastrings.php:76
const ELGG_ENTITIES_ANY_VALUE
Definition: elgglib.php:2006
elgg_deprecated_notice($msg, $dep_version, $backtrace_level=1)
Log a notice about deprecated use of a function, view, etc.
Definition: elgglib.php:1031
elgg global
Pointer to the global context.
Definition: elgglib.js:12
$type
Definition: add.php:8
elgg_extract($key, $array, $default=null, $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:1271
get_data($query, $callback="")
Retrieve rows from the database.
Definition: database.php:50
_elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pairs)
Returns SQL where clause for type and subtype on main entity table.
Definition: entities.php:524
_elgg_normalize_metastrings_options(array $options=array())
Normalizes metadata / annotation option names to their corresponding metastrings name.
sanitise_int($int, $signed=true)
Sanitizes an integer for database use.
Definition: database.php:173
_elgg_metastrings_test($hook, $type, $value)
Metastring unit tests.
is_memcache_available()
Return true if memcache is available and configured.
Definition: memcache.php:16
_elgg_normalize_plural_options_array($options, $singulars)
Normalise the singular keys in an options array to plural keys.
Definition: elgglib.php:1401
_elgg_get_access_where_sql(array $options=array())
Returns the SQL where clause for enforcing read access to data.
Definition: access.php:216
elgg_trigger_event($event, $object_type, $object=null)
Definition: elgglib.php:584
_elgg_set_metastring_based_object_enabled_by_id($id, $enabled, $type)
Enables or disables a metastrings-based object by its id.
if(!$collection_name) $id
Definition: add.php:17
if(!$num_display) $db_prefix
Definition: content.php:12
_elgg_add_metastring($string)
Add a metastring.
Definition: metastrings.php:37