Elgg  Version 1.11
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 = $options['joins'];
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  foreach ($joins as $i => $join) {
245  if ($join === false) {
246  return false;
247  } elseif (empty($join)) {
248  unset($joins[$i]);
249  }
250  }
251 
252  // metastrings
253  $metastring_clauses = _elgg_get_metastring_sql('n_table', $options['metastring_names'],
254  $options['metastring_values'], null, $options['metastring_ids'],
255  $options['metastring_case_sensitive']);
256 
257  if ($metastring_clauses) {
258  $wheres = array_merge($wheres, $metastring_clauses['wheres']);
259  $joins = array_merge($joins, $metastring_clauses['joins']);
260  } else {
261  $wheres[] = _elgg_get_access_where_sql(array('table_alias' => 'n_table'));
262  }
263 
264  $distinct = $options['distinct'] ? "DISTINCT " : "";
265 
266  if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) {
267  $selects = array_unique($selects);
268  // evalutate selects
269  $select_str = '';
270  if ($selects) {
271  foreach ($selects as $select) {
272  $select_str .= ", $select";
273  }
274  }
275 
276  $query = "SELECT $distinct n_table.*{$select_str} FROM {$db_prefix}$type n_table";
277  } elseif ($options['count']) {
278  // count is over the entities
279  $query = "SELECT count($distinct e.guid) as calculation FROM {$db_prefix}$type n_table";
280  } else {
281  $query = "SELECT {$options['metastring_calculation']}(v.string) as calculation FROM {$db_prefix}$type n_table";
282  }
283 
284  // remove identical join clauses
285  $joins = array_unique($joins);
286 
287  // add joins
288  foreach ($joins as $j) {
289  $query .= " $j ";
290  }
291 
292  // add wheres
293  $query .= ' WHERE ';
294 
295  foreach ($wheres as $w) {
296  $query .= " $w AND ";
297  }
298 
299  // Add access controls
300  $query .= _elgg_get_access_where_sql(array('table_alias' => 'e'));
301 
302  // reverse order by
303  if (isset($options['reverse_order_by']) && $options['reverse_order_by']) {
304  $options['order_by'] = _elgg_sql_reverse_order_by_clause($options['order_by']);
305  }
306 
307  if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) {
308  if (isset($options['group_by'])) {
309  $options['group_by'] = sanitise_string($options['group_by']);
310  $query .= " GROUP BY {$options['group_by']}";
311  }
312 
313  if (isset($options['order_by']) && $options['order_by']) {
314  $options['order_by'] = sanitise_string($options['order_by']);
315  $query .= " ORDER BY {$options['order_by']}, n_table.id";
316  }
317 
318  if ($options['limit']) {
319  $limit = sanitise_int($options['limit']);
320  $offset = sanitise_int($options['offset'], false);
321  $query .= " LIMIT $offset, $limit";
322  }
323 
324  $dt = get_data($query, $options['callback']);
325 
326  if ($options['preload_owners'] && is_array($dt) && count($dt) > 1) {
327  _elgg_services()->entityPreloader->preload($dt, ['owner_guid']);
328  }
329 
330  return $dt;
331  } else {
332  $result = get_data_row($query);
333  return $result->calculation;
334  }
335 }
336 
352 function _elgg_get_metastring_sql($table, $names = null, $values = null,
353  $pairs = null, $ids = null, $case_sensitive = false) {
354 
355  if ((!$names && $names !== 0)
356  && (!$values && $values !== 0)
357  && !$ids
358  && (!$pairs && $pairs !== 0)) {
359 
360  return array();
361  }
362 
363  $db_prefix = elgg_get_config('dbprefix');
364 
365  // binary forces byte-to-byte comparision of strings, making
366  // it case- and diacritical-mark- sensitive.
367  // only supported on values.
368  $binary = ($case_sensitive) ? ' BINARY ' : '';
369 
370  $return = array (
371  'joins' => array (),
372  'wheres' => array()
373  );
374 
375  $wheres = array();
376 
377  // get names wheres and joins
378  $names_where = '';
379  if ($names !== null) {
380  if (!is_array($names)) {
381  $names = array($names);
382  }
383 
384  $sanitised_names = array();
385  foreach ($names as $name) {
386  // normalise to 0.
387  if (!$name) {
388  $name = '0';
389  }
390  $sanitised_names[] = '\'' . sanitise_string($name) . '\'';
391  }
392 
393  if ($names_str = implode(',', $sanitised_names)) {
394  $return['joins'][] = "JOIN {$db_prefix}metastrings msn on $table.name_id = msn.id";
395  $names_where = "(msn.string IN ($names_str))";
396  }
397  }
398 
399  // get values wheres and joins
400  $values_where = '';
401  if ($values !== null) {
402  if (!is_array($values)) {
403  $values = array($values);
404  }
405 
406  $sanitised_values = array();
407  foreach ($values as $value) {
408  // normalize to 0
409  if (!$value) {
410  $value = 0;
411  }
412  $sanitised_values[] = '\'' . sanitise_string($value) . '\'';
413  }
414 
415  if ($values_str = implode(',', $sanitised_values)) {
416  $return['joins'][] = "JOIN {$db_prefix}metastrings msv on $table.value_id = msv.id";
417  $values_where = "({$binary}msv.string IN ($values_str))";
418  }
419  }
420 
421  if ($ids !== null) {
422  if (!is_array($ids)) {
423  $ids = array($ids);
424  }
425 
426  $ids_str = implode(',', $ids);
427 
428  if ($ids_str) {
429  $wheres[] = "n_table.id IN ($ids_str)";
430  }
431  }
432 
433  if ($names_where && $values_where) {
434  $wheres[] = "($names_where AND $values_where)";
435  } elseif ($names_where) {
436  $wheres[] = $names_where;
437  } elseif ($values_where) {
438  $wheres[] = $values_where;
439  }
440 
441  $wheres[] = _elgg_get_access_where_sql(array('table_alias' => $table));
442 
443  if ($where = implode(' AND ', $wheres)) {
444  $return['wheres'][] = "($where)";
445  }
446 
447  return $return;
448 }
449 
458 
459  // support either metastrings_type or metastring_type
460  // because I've made this mistake many times and hunting it down is a pain...
461  $type = elgg_extract('metastring_type', $options, null);
462  $type = elgg_extract('metastrings_type', $options, $type);
463 
464  $options['metastring_type'] = $type;
465 
466  // support annotation_ and annotations_ because they're way too easy to confuse
467  $prefixes = array('metadata_', 'annotation_', 'annotations_');
468 
469  // map the metadata_* options to metastring_* options
470  $map = array(
471  'names' => 'metastring_names',
472  'values' => 'metastring_values',
473  'case_sensitive' => 'metastring_case_sensitive',
474  'owner_guids' => 'metastring_owner_guids',
475  'created_time_lower' => 'metastring_created_time_lower',
476  'created_time_upper' => 'metastring_created_time_upper',
477  'calculation' => 'metastring_calculation',
478  'ids' => 'metastring_ids',
479  );
480 
481  foreach ($prefixes as $prefix) {
482  $singulars = array("{$prefix}name", "{$prefix}value", "{$prefix}owner_guid", "{$prefix}id");
484 
485  foreach ($map as $specific => $normalized) {
486  $key = $prefix . $specific;
487  if (isset($options[$key])) {
488  $options[$normalized] = $options[$key];
489  }
490  }
491  }
492 
493  return $options;
494 }
495 
511  $id = (int)$id;
512  $db_prefix = elgg_get_config('dbprefix');
513 
515 
516  switch ($type) {
517  case 'annotation':
518  case 'annotations':
519  $type = 'annotation';
520  $table = "{$db_prefix}annotations";
521  break;
522 
523  case 'metadata':
524  $table = "{$db_prefix}metadata";
525  break;
526  }
527 
528  if ($enabled === 'yes' || $enabled === 1 || $enabled === true) {
529  $enabled = 'yes';
530  $event = 'enable';
531  } elseif ($enabled === 'no' || $enabled === 0 || $enabled === false) {
532  $enabled = 'no';
533  $event = 'disable';
534  } else {
535  return false;
536  }
537 
538  $return = false;
539 
540  if ($object) {
541  // don't set it if it's already set.
542  if ($object->enabled == $enabled) {
543  $return = false;
544  } elseif ($object->canEdit() && (elgg_trigger_event($event, $type, $object))) {
545  $return = update_data("UPDATE $table SET enabled = '$enabled' where id = $id");
546  }
547  }
548 
549  return $return;
550 }
551 
567 function _elgg_batch_metastring_based_objects(array $options, $callback, $inc_offset = true) {
568  if (!$options || !is_array($options)) {
569  return false;
570  }
571 
572  $batch = new \ElggBatch('_elgg_get_metastring_based_objects', $options, $callback, 50, $inc_offset);
573  return $batch->callbackResult;
574 }
575 
585  $id = (int)$id;
586  if (!$id) {
587  return false;
588  }
589 
590  $options = array(
591  'metastring_type' => $type,
592  'metastring_id' => $id,
593  );
594 
596 
597  if ($obj && count($obj) == 1) {
598  return $obj[0];
599  }
600 
601  return false;
602 }
603 
613  $id = (int)$id;
614  $db_prefix = elgg_get_config('dbprefix');
615 
616  switch ($type) {
617  case 'annotations':
618  case 'annotation':
619  $table = $db_prefix . 'annotations';
620  $type = 'annotation';
621  break;
622 
623  case 'metadata':
624  $table = $db_prefix . 'metadata';
625  $type = 'metadata';
626  break;
627 
628  default:
629  return false;
630  }
631 
633 
634  if ($obj) {
635  // Tidy up if memcache is enabled.
636  // @todo only metadata is supported
637  if ($type == 'metadata') {
638  static $metabyname_memcache;
639  if ((!$metabyname_memcache) && (is_memcache_available())) {
640  $metabyname_memcache = new \ElggMemcache('metabyname_memcache');
641  }
642 
643  if ($metabyname_memcache) {
644  // @todo why name_id? is that even populated?
645  $metabyname_memcache->delete("{$obj->entity_guid}:{$obj->name_id}");
646  }
647  }
648 
649  if ($obj->canEdit()) {
650  // bc code for when we triggered 'delete', 'annotations' #4770
651  $result = true;
652  if ($type == "annotation") {
653  $result = elgg_trigger_event('delete', 'annotations', $obj);
654  if ($result === false) {
655  elgg_deprecated_notice("Use the event 'delete', 'annotation'", 1.9);
656  }
657  }
658 
659  if (elgg_trigger_event('delete', $type, $obj) && $result) {
660  return (bool)delete_data("DELETE FROM $table WHERE id = $id");
661  }
662  }
663  }
664 
665  return false;
666 }
667 
678  $valid_types = array('metadata', 'annotation');
679  if (!in_array($type, $valid_types)) {
680  return false;
681  }
682 
683  // the options for annotations are singular (annotation_name) but the table
684  // is plural (elgg_annotations) so rewrite for the table name.
685  $n_table = ($type == 'annotation') ? 'annotations' : $type;
686 
687  $singulars = array("{$type}_name", "{$type}_value",
688  "{$type}_name_value_pair", "{$type}_owner_guid");
690 
691  $clauses = _elgg_get_entity_metadata_where_sql('e', $n_table, $options["{$type}_names"],
692  $options["{$type}_values"], $options["{$type}_name_value_pairs"],
693  $options["{$type}_name_value_pairs_operator"], $options["{$type}_case_sensitive"],
694  $options["order_by_{$type}"], $options["{$type}_owner_guids"]);
695 
696  if ($clauses) {
697  // merge wheres to pass to elgg_get_entities()
698  if (isset($options['wheres']) && !is_array($options['wheres'])) {
699  $options['wheres'] = array($options['wheres']);
700  } elseif (!isset($options['wheres'])) {
701  $options['wheres'] = array();
702  }
703 
704  $options['wheres'] = array_merge($options['wheres'], $clauses['wheres']);
705 
706  // merge joins to pass to elgg_get_entities()
707  if (isset($options['joins']) && !is_array($options['joins'])) {
708  $options['joins'] = array($options['joins']);
709  } elseif (!isset($options['joins'])) {
710  $options['joins'] = array();
711  }
712 
713  $options['joins'] = array_merge($options['joins'], $clauses['joins']);
714 
715  if ($clauses['orders']) {
716  $order_by_metadata = implode(", ", $clauses['orders']);
717  if (isset($options['order_by']) && $options['order_by']) {
718  $options['order_by'] = "$order_by_metadata, {$options['order_by']}";
719  } else {
720  $options['order_by'] = "$order_by_metadata, e.time_created DESC";
721  }
722  }
723  }
724 
725  return $options;
726 }
727 
739  global $CONFIG;
740  $value[] = $CONFIG->path . 'engine/tests/ElggCoreMetastringsTest.php';
741  return $value;
742 }
743 
744 return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
745  $hooks->registerHandler('unit_test', 'system', '_elgg_metastrings_test');
746 };
_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:1976
_elgg_get_guid_based_where_sql($column, $guids)
Returns SQL where clause for owner and containers.
Definition: entities.php:535
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:553
$value
Definition: longtext.php:26
$return
Definition: opendd.php:15
if(!$count) $offset
Definition: pagination.php:25
elgg_extract($key, array $array, $default=null, $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:1246
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:1619
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:31
$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:1967
elgg_deprecated_notice($msg, $dep_version, $backtrace_level=1)
Log a notice about deprecated use of a function, view, etc.
Definition: elgglib.php:1006
elgg global
Pointer to the global context.
Definition: elgglib.js:12
$type
Definition: add.php:8
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:520
_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:1376
_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:570
_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