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