Elgg  Version 2.2
 All Classes Namespaces Files Functions Variables Pages
EntityTable.php
Go to the documentation of this file.
1 <?php
2 namespace Elgg\Database;
3 
6 
16 class EntityTable {
22  private $CONFIG;
23 
27  public function __construct() {
28  global $CONFIG;
29  $this->CONFIG = $CONFIG;
30  }
31 
46  function getRow($guid) {
47 
48 
49  if (!$guid) {
50  return false;
51  }
52 
53  $guid = (int) $guid;
54  $access = _elgg_get_access_where_sql(array('table_alias' => ''));
55 
56  return _elgg_services()->db->getDataRow("SELECT * from {$this->CONFIG->dbprefix}entities where guid=$guid and $access");
57  }
58 
74  function rowToElggStar($row) {
75  if (!($row instanceof \stdClass)) {
76  return $row;
77  }
78 
79  if ((!isset($row->guid)) || (!isset($row->subtype))) {
80  return $row;
81  }
82 
83  $new_entity = false;
84 
85  // Create a memcache cache if we can
86  static $newentity_cache;
87  if ((!$newentity_cache) && (is_memcache_available())) {
88  $newentity_cache = new \ElggMemcache('new_entity_cache');
89  }
90  if ($newentity_cache) {
91  $new_entity = $newentity_cache->load($row->guid);
92  }
93  if ($new_entity) {
94  return $new_entity;
95  }
96 
97  // load class for entity if one is registered
98  $classname = get_subtype_class_from_id($row->subtype);
99  if ($classname != "") {
100  if (class_exists($classname)) {
101  $new_entity = new $classname($row);
102 
103  if (!($new_entity instanceof \ElggEntity)) {
104  $msg = $classname . " is not a " . '\ElggEntity' . ".";
105  throw new \ClassException($msg);
106  }
107  } else {
108  error_log("Class '" . $classname . "' was not found, missing plugin?");
109  }
110  }
111 
112  if (!$new_entity) {
113  //@todo Make this into a function
114  switch ($row->type) {
115  case 'object' :
116  $new_entity = new \ElggObject($row);
117  break;
118  case 'user' :
119  $new_entity = new \ElggUser($row);
120  break;
121  case 'group' :
122  $new_entity = new \ElggGroup($row);
123  break;
124  case 'site' :
125  $new_entity = new \ElggSite($row);
126  break;
127  default:
128  $msg = "Entity type " . $row->type . " is not supported.";
129  throw new \InstallationException($msg);
130  }
131  }
132 
133  // Cache entity if we have a cache available
134  if (($newentity_cache) && ($new_entity)) {
135  $newentity_cache->save($new_entity->guid, $new_entity);
136  }
137 
138  return $new_entity;
139  }
140 
150  function get($guid, $type = '') {
151  // We could also use: if (!(int) $guid) { return false },
152  // but that evaluates to a false positive for $guid = true.
153  // This is a bit slower, but more thorough.
154  if (!is_numeric($guid) || $guid === 0 || $guid === '0') {
155  return false;
156  }
157 
158  // Check local cache first
159  $new_entity = _elgg_services()->entityCache->get($guid);
160  if ($new_entity) {
161  if ($type) {
162  return elgg_instanceof($new_entity, $type) ? $new_entity : false;
163  }
164  return $new_entity;
165  }
166 
167  $options = [
168  'guid' => $guid,
169  'limit' => 1,
170  'site_guids' => ELGG_ENTITIES_ANY_VALUE, // for BC with get_entity, allow matching any site
171  ];
172  if ($type) {
173  $options['type'] = $type;
174  }
175  $entities = $this->getEntities($options);
176  return $entities ? $entities[0] : false;
177  }
178 
191  function exists($guid) {
192 
193 
195 
196  $query = "SELECT count(*) as total FROM {$this->CONFIG->dbprefix}entities WHERE guid = $guid";
197  $result = _elgg_services()->db->getDataRow($query);
198  if ($result->total == 0) {
199  return false;
200  } else {
201  return true;
202  }
203  }
204 
213  function enable($guid, $recursive = true) {
214 
215  // Override access only visible entities
216  $old_access_status = access_get_show_hidden_status();
218 
219  $result = false;
221  if ($entity) {
222  $result = $entity->enable($recursive);
223  }
224 
225  access_show_hidden_entities($old_access_status);
226  return $result;
227  }
228 
311  function getEntities(array $options = array()) {
312 
313 
314  $defaults = array(
315  'types' => ELGG_ENTITIES_ANY_VALUE,
316  'subtypes' => ELGG_ENTITIES_ANY_VALUE,
317  'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE,
318 
319  'guids' => ELGG_ENTITIES_ANY_VALUE,
320  'owner_guids' => ELGG_ENTITIES_ANY_VALUE,
321  'container_guids' => ELGG_ENTITIES_ANY_VALUE,
322  'site_guids' => $this->CONFIG->site_guid,
323 
324  'modified_time_lower' => ELGG_ENTITIES_ANY_VALUE,
325  'modified_time_upper' => ELGG_ENTITIES_ANY_VALUE,
326  'created_time_lower' => ELGG_ENTITIES_ANY_VALUE,
327  'created_time_upper' => ELGG_ENTITIES_ANY_VALUE,
328 
329  'reverse_order_by' => false,
330  'order_by' => 'e.time_created desc',
331  'group_by' => ELGG_ENTITIES_ANY_VALUE,
332  'limit' => _elgg_services()->config->get('default_limit'),
333  'offset' => 0,
334  'count' => false,
335  'selects' => array(),
336  'wheres' => array(),
337  'joins' => array(),
338 
339  'preload_owners' => false,
340  'preload_containers' => false,
341  'callback' => 'entity_row_to_elggstar',
342  'distinct' => true,
343 
344  // private API
345  '__ElggBatch' => null,
346  );
347 
348  $options = array_merge($defaults, $options);
349 
350  // can't use helper function with type_subtype_pair because
351  // it's already an array...just need to merge it
352  if (isset($options['type_subtype_pair'])) {
353  if (isset($options['type_subtype_pairs'])) {
354  $options['type_subtype_pairs'] = array_merge($options['type_subtype_pairs'],
355  $options['type_subtype_pair']);
356  } else {
357  $options['type_subtype_pairs'] = $options['type_subtype_pair'];
358  }
359  }
360 
361  $singulars = array('type', 'subtype', 'guid', 'owner_guid', 'container_guid', 'site_guid');
362  $options = _elgg_normalize_plural_options_array($options, $singulars);
363 
364  $options = $this->autoJoinTables($options);
365 
366  // evaluate where clauses
367  if (!is_array($options['wheres'])) {
368  $options['wheres'] = array($options['wheres']);
369  }
370 
371  $wheres = $options['wheres'];
372 
373  $wheres[] = $this->getEntityTypeSubtypeWhereSql('e', $options['types'],
374  $options['subtypes'], $options['type_subtype_pairs']);
375 
376  $wheres[] = $this->getGuidBasedWhereSql('e.guid', $options['guids']);
377  $wheres[] = $this->getGuidBasedWhereSql('e.owner_guid', $options['owner_guids']);
378  $wheres[] = $this->getGuidBasedWhereSql('e.container_guid', $options['container_guids']);
379  $wheres[] = $this->getGuidBasedWhereSql('e.site_guid', $options['site_guids']);
380 
381  $wheres[] = $this->getEntityTimeWhereSql('e', $options['created_time_upper'],
382  $options['created_time_lower'], $options['modified_time_upper'], $options['modified_time_lower']);
383 
384  // see if any functions failed
385  // remove empty strings on successful functions
386  foreach ($wheres as $i => $where) {
387  if ($where === false) {
388  return false;
389  } elseif (empty($where)) {
390  unset($wheres[$i]);
391  }
392  }
393 
394  // remove identical where clauses
395  $wheres = array_unique($wheres);
396 
397  // evaluate join clauses
398  if (!is_array($options['joins'])) {
399  $options['joins'] = array($options['joins']);
400  }
401 
402  // remove identical join clauses
403  $joins = array_unique($options['joins']);
404 
405  foreach ($joins as $i => $join) {
406  if ($join === false) {
407  return false;
408  } elseif (empty($join)) {
409  unset($joins[$i]);
410  }
411  }
412 
413  // evalutate selects
414  if ($options['selects']) {
415  $selects = '';
416  foreach ($options['selects'] as $select) {
417  $selects .= ", $select";
418  }
419  } else {
420  $selects = '';
421  }
422 
423  if (!$options['count']) {
424  $distinct = $options['distinct'] ? "DISTINCT" : "";
425  $query = "SELECT $distinct e.*{$selects} FROM {$this->CONFIG->dbprefix}entities e ";
426  } else {
427  // note: when DISTINCT unneeded, it's slightly faster to compute COUNT(*) than GUIDs
428  $count_expr = $options['distinct'] ? "DISTINCT e.guid" : "*";
429  $query = "SELECT COUNT($count_expr) as total FROM {$this->CONFIG->dbprefix}entities e ";
430  }
431 
432  // add joins
433  foreach ($joins as $j) {
434  $query .= " $j ";
435  }
436 
437  // add wheres
438  $query .= ' WHERE ';
439 
440  foreach ($wheres as $w) {
441  $query .= " $w AND ";
442  }
443 
444  // Add access controls
445  $query .= _elgg_get_access_where_sql();
446 
447  // reverse order by
448  if ($options['reverse_order_by']) {
449  $options['order_by'] = _elgg_sql_reverse_order_by_clause($options['order_by']);
450  }
451 
452  if ($options['count']) {
453  $total = _elgg_services()->db->getDataRow($query);
454  return (int)$total->total;
455  }
456 
457  if ($options['group_by']) {
458  $query .= " GROUP BY {$options['group_by']}";
459  }
460 
461  if ($options['order_by']) {
462  $query .= " ORDER BY {$options['order_by']}";
463  }
464 
465  if ($options['limit']) {
466  $limit = sanitise_int($options['limit'], false);
467  $offset = sanitise_int($options['offset'], false);
468  $query .= " LIMIT $offset, $limit";
469  }
470 
471  if ($options['callback'] === 'entity_row_to_elggstar') {
472  $results = $this->fetchFromSql($query, $options['__ElggBatch']);
473  } else {
474  $results = _elgg_services()->db->getData($query, $options['callback']);
475  }
476 
477  if (!$results) {
478  // no results, no preloading
479  return $results;
480  }
481 
482  // populate entity and metadata caches, and prepare $entities for preloader
483  $guids = array();
484  foreach ($results as $item) {
485  // A custom callback could result in items that aren't \ElggEntity's, so check for them
486  if ($item instanceof \ElggEntity) {
487  _elgg_services()->entityCache->set($item);
488  // plugins usually have only settings
489  if (!$item instanceof \ElggPlugin) {
490  $guids[] = $item->guid;
491  }
492  }
493  }
494  // @todo Without this, recursive delete fails. See #4568
495  reset($results);
496 
497  if ($guids) {
498  // there were entities in the result set, preload metadata for them
499  _elgg_services()->metadataCache->populateFromEntities($guids);
500  }
501 
502  if (count($results) > 1) {
503  $props_to_preload = [];
504  if ($options['preload_owners']) {
505  $props_to_preload[] = 'owner_guid';
506  }
507  if ($options['preload_containers']) {
508  $props_to_preload[] = 'container_guid';
509  }
510  if ($props_to_preload) {
511  // note, ElggEntityPreloaderIntegrationTest assumes it can swap out
512  // the preloader after boot. If you inject this component at construction
513  // time that unit test will break. :/
514  _elgg_services()->entityPreloader->preload($results, $props_to_preload);
515  }
516  }
517 
518  return $results;
519  }
520 
528  protected function autoJoinTables(array $options) {
529  // we must be careful that the query doesn't specify any options that may join
530  // tables or change the selected columns
531  if (!is_array($options['types'])
532  || count($options['types']) !== 1
533  || !empty($options['selects'])
534  || !empty($options['wheres'])
535  || !empty($options['joins'])
536  || $options['callback'] !== 'entity_row_to_elggstar'
537  || $options['count']) {
538  // Too dangerous to auto-join
539  return $options;
540  }
541 
542  $join_types = [
543  // Each class must have a static getExternalAttributes() : array
544  'object' => 'ElggObject',
545  'user' => 'ElggUser',
546  'group' => 'ElggGroup',
547  'site' => 'ElggSite',
548  ];
549 
550  // We use reset() because $options['types'] may not have a numeric key
551  $type = reset($options['types']);
552  if (empty($join_types[$type])) {
553  return $options;
554  }
555 
556  // Get the columns we'll need to select. We can't use st.* because the order_by
557  // clause may reference "guid", which MySQL will complain about being ambiguous
558  if (!is_callable([$join_types[$type], 'getExternalAttributes'])) {
559  // for some reason can't get external attributes.
560  return $options;
561  }
562 
563  $attributes = $join_types[$type]::getExternalAttributes();
564  foreach (array_keys($attributes) as $col) {
565  $options['selects'][] = "st.$col";
566  }
567 
568  // join the secondary table
569  $options['joins'][] = "JOIN {$this->CONFIG->dbprefix}{$type}s_entity st ON (e.guid = st.guid)";
570 
571  return $options;
572  }
573 
584  function fetchFromSql($sql, \ElggBatch $batch = null) {
585  static $plugin_subtype;
586  if (null === $plugin_subtype) {
587  $plugin_subtype = get_subtype_id('object', 'plugin');
588  }
589 
590  // Keys are types, values are columns that, if present, suggest that the secondary
591  // table is already JOINed. Note it's OK if guess incorrectly because entity load()
592  // will fetch any missing attributes.
593  $types_to_optimize = array(
594  'object' => 'title',
595  'user' => 'password',
596  'group' => 'name',
597  'site' => 'url',
598  );
599 
600  $rows = _elgg_services()->db->getData($sql);
601 
602  // guids to look up in each type
603  $lookup_types = array();
604  // maps GUIDs to the $rows key
605  $guid_to_key = array();
606 
607  if (isset($rows[0]->type, $rows[0]->subtype)
608  && $rows[0]->type === 'object'
609  && $rows[0]->subtype == $plugin_subtype) {
610  // Likely the entire resultset is plugins, which have already been optimized
611  // to JOIN the secondary table. In this case we allow retrieving from cache,
612  // but abandon the extra queries.
613  $types_to_optimize = array();
614  }
615 
616  // First pass: use cache where possible, gather GUIDs that we're optimizing
617  foreach ($rows as $i => $row) {
618  if (empty($row->guid) || empty($row->type)) {
619  throw new \LogicException('Entity row missing guid or type');
620  }
621  $entity = _elgg_services()->entityCache->get($row->guid);
622  if ($entity) {
623  $entity->refresh($row);
624  $rows[$i] = $entity;
625  continue;
626  }
627  if (isset($types_to_optimize[$row->type])) {
628  // check if row already looks JOINed.
629  if (isset($row->{$types_to_optimize[$row->type]})) {
630  // Row probably already contains JOINed secondary table. Don't make another query just
631  // to pull data that's already there
632  continue;
633  }
634  $lookup_types[$row->type][] = $row->guid;
635  $guid_to_key[$row->guid] = $i;
636  }
637  }
638  // Do secondary queries and merge rows
639  if ($lookup_types) {
640  $dbprefix = _elgg_services()->config->get('dbprefix');
641 
642  foreach ($lookup_types as $type => $guids) {
643  $set = "(" . implode(',', $guids) . ")";
644  $sql = "SELECT * FROM {$dbprefix}{$type}s_entity WHERE guid IN $set";
645  $secondary_rows = _elgg_services()->db->getData($sql);
646  if ($secondary_rows) {
647  foreach ($secondary_rows as $secondary_row) {
648  $key = $guid_to_key[$secondary_row->guid];
649  // cast to arrays to merge then cast back
650  $rows[$key] = (object)array_merge((array)$rows[$key], (array)$secondary_row);
651  }
652  }
653  }
654  }
655  // Second pass to finish conversion
656  foreach ($rows as $i => $row) {
657  if ($row instanceof \ElggEntity) {
658  continue;
659  } else {
660  try {
662  } catch (IncompleteEntityException $e) {
663  // don't let incomplete entities throw fatal errors
664  unset($rows[$i]);
665 
666  // report incompletes to the batch process that spawned this query
667  if ($batch) {
668  $batch->reportIncompleteEntity($row);
669  }
670  }
671  }
672  }
673  return $rows;
674  }
675 
687  function getEntityTypeSubtypeWhereSql($table, $types, $subtypes, $pairs) {
688  // subtype depends upon type.
689  if ($subtypes && !$types) {
690  _elgg_services()->logger->warn("Cannot set subtypes without type.");
691  return false;
692  }
693 
694  // short circuit if nothing is requested
695  if (!$types && !$subtypes && !$pairs) {
696  return '';
697  }
698 
699  // these are the only valid types for entities in elgg
700  $valid_types = _elgg_services()->config->get('entity_types');
701 
702  // pairs override
703  $wheres = array();
704  if (!is_array($pairs)) {
705  if (!is_array($types)) {
706  $types = array($types);
707  }
708 
709  if ($subtypes && !is_array($subtypes)) {
710  $subtypes = array($subtypes);
711  }
712 
713  // decrementer for valid types. Return false if no valid types
714  $valid_types_count = count($types);
715  $valid_subtypes_count = 0;
716  // remove invalid types to get an accurate count of
717  // valid types for the invalid subtype detection to use
718  // below.
719  // also grab the count of ALL subtypes on valid types to decrement later on
720  // and check against.
721  //
722  // yes this is duplicating a foreach on $types.
723  foreach ($types as $type) {
724  if (!in_array($type, $valid_types)) {
725  $valid_types_count--;
726  unset($types[array_search($type, $types)]);
727  } else {
728  // do the checking (and decrementing) in the subtype section.
729  $valid_subtypes_count += count($subtypes);
730  }
731  }
732 
733  // return false if nothing is valid.
734  if (!$valid_types_count) {
735  return false;
736  }
737 
738  // subtypes are based upon types, so we need to look at each
739  // type individually to get the right subtype id.
740  foreach ($types as $type) {
741  $subtype_ids = array();
742  if ($subtypes) {
743  foreach ($subtypes as $subtype) {
744  // check that the subtype is valid
745  if (!$subtype && ELGG_ENTITIES_NO_VALUE === $subtype) {
746  // subtype value is 0
747  $subtype_ids[] = ELGG_ENTITIES_NO_VALUE;
748  } elseif (!$subtype) {
749  // subtype is ignored.
750  // this handles ELGG_ENTITIES_ANY_VALUE, '', and anything falsy that isn't 0
751  continue;
752  } else {
753  $subtype_id = get_subtype_id($type, $subtype);
754 
755  if ($subtype_id) {
756  $subtype_ids[] = $subtype_id;
757  } else {
758  $valid_subtypes_count--;
759  _elgg_services()->logger->notice("Type-subtype '$type:$subtype' does not exist!");
760  continue;
761  }
762  }
763  }
764 
765  // return false if we're all invalid subtypes in the only valid type
766  if ($valid_subtypes_count <= 0) {
767  return false;
768  }
769  }
770 
771  if (is_array($subtype_ids) && count($subtype_ids)) {
772  $subtype_ids_str = implode(',', $subtype_ids);
773  $wheres[] = "({$table}.type = '$type' AND {$table}.subtype IN ($subtype_ids_str))";
774  } else {
775  $wheres[] = "({$table}.type = '$type')";
776  }
777  }
778  } else {
779  // using type/subtype pairs
780  $valid_pairs_count = count($pairs);
781  $valid_pairs_subtypes_count = 0;
782 
783  // same deal as above--we need to know how many valid types
784  // and subtypes we have before hitting the subtype section.
785  // also normalize the subtypes into arrays here.
786  foreach ($pairs as $paired_type => $paired_subtypes) {
787  if (!in_array($paired_type, $valid_types)) {
788  $valid_pairs_count--;
789  unset($pairs[array_search($paired_type, $pairs)]);
790  } else {
791  if ($paired_subtypes && !is_array($paired_subtypes)) {
792  $pairs[$paired_type] = array($paired_subtypes);
793  }
794  $valid_pairs_subtypes_count += count($paired_subtypes);
795  }
796  }
797 
798  if ($valid_pairs_count <= 0) {
799  return false;
800  }
801  foreach ($pairs as $paired_type => $paired_subtypes) {
802  // this will always be an array because of line 2027, right?
803  // no...some overly clever person can say pair => array('object' => null)
804  if (is_array($paired_subtypes)) {
805  $paired_subtype_ids = array();
806  foreach ($paired_subtypes as $paired_subtype) {
807  if (ELGG_ENTITIES_NO_VALUE === $paired_subtype
808  || ($paired_subtype_id = get_subtype_id($paired_type, $paired_subtype))) {
809 
810  $paired_subtype_ids[] = (ELGG_ENTITIES_NO_VALUE === $paired_subtype) ?
811  ELGG_ENTITIES_NO_VALUE : $paired_subtype_id;
812  } else {
813  $valid_pairs_subtypes_count--;
814  _elgg_services()->logger->notice("Type-subtype '$paired_type:$paired_subtype' does not exist!");
815  // return false if we're all invalid subtypes in the only valid type
816  continue;
817  }
818  }
819 
820  // return false if there are no valid subtypes.
821  if ($valid_pairs_subtypes_count <= 0) {
822  return false;
823  }
824 
825 
826  if ($paired_subtype_ids_str = implode(',', $paired_subtype_ids)) {
827  $wheres[] = "({$table}.type = '$paired_type'"
828  . " AND {$table}.subtype IN ($paired_subtype_ids_str))";
829  }
830  } else {
831  $wheres[] = "({$table}.type = '$paired_type')";
832  }
833  }
834  }
835 
836  // pairs override the above. return false if they don't exist.
837  if (is_array($wheres) && count($wheres)) {
838  $where = implode(' OR ', $wheres);
839  return "($where)";
840  }
841 
842  return '';
843  }
844 
856  // short circuit if nothing requested
857  // 0 is a valid guid
858  if (!$guids && $guids !== 0) {
859  return '';
860  }
861 
862  // normalize and sanitise owners
863  if (!is_array($guids)) {
864  $guids = array($guids);
865  }
866 
867  $guids_sanitized = array();
868  foreach ($guids as $guid) {
869  if ($guid !== ELGG_ENTITIES_NO_VALUE) {
870  $guid = sanitise_int($guid);
871 
872  if (!$guid) {
873  return false;
874  }
875  }
876  $guids_sanitized[] = $guid;
877  }
878 
879  $where = '';
880  $guid_str = implode(',', $guids_sanitized);
881 
882  // implode(',', 0) returns 0.
883  if ($guid_str !== false && $guid_str !== '') {
884  $where = "($column IN ($guid_str))";
885  }
886 
887  return $where;
888  }
889 
903  function getEntityTimeWhereSql($table, $time_created_upper = null,
904  $time_created_lower = null, $time_updated_upper = null, $time_updated_lower = null) {
905 
906  $wheres = array();
907 
908  // exploit PHP's loose typing (quack) to check that they are INTs and not str cast to 0
909  if ($time_created_upper && $time_created_upper == sanitise_int($time_created_upper)) {
910  $wheres[] = "{$table}.time_created <= $time_created_upper";
911  }
912 
913  if ($time_created_lower && $time_created_lower == sanitise_int($time_created_lower)) {
914  $wheres[] = "{$table}.time_created >= $time_created_lower";
915  }
916 
917  if ($time_updated_upper && $time_updated_upper == sanitise_int($time_updated_upper)) {
918  $wheres[] = "{$table}.time_updated <= $time_updated_upper";
919  }
920 
921  if ($time_updated_lower && $time_updated_lower == sanitise_int($time_updated_lower)) {
922  $wheres[] = "{$table}.time_updated >= $time_updated_lower";
923  }
924 
925  if (is_array($wheres) && count($wheres) > 0) {
926  $where_str = implode(' AND ', $wheres);
927  return "($where_str)";
928  }
929 
930  return '';
931  }
932 
964  function getEntitiesFromAttributes(array $options = array()) {
965  $defaults = array(
966  'attribute_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE,
967  'attribute_name_value_pairs_operator' => 'AND',
968  );
969 
970  $options = array_merge($defaults, $options);
971 
972  $singulars = array('type', 'attribute_name_value_pair');
974 
976 
977  if ($clauses) {
978  // merge wheres to pass to elgg_get_entities()
979  if (isset($options['wheres']) && !is_array($options['wheres'])) {
980  $options['wheres'] = array($options['wheres']);
981  } elseif (!isset($options['wheres'])) {
982  $options['wheres'] = array();
983  }
984 
985  $options['wheres'] = array_merge($options['wheres'], $clauses['wheres']);
986 
987  // merge joins to pass to elgg_get_entities()
988  if (isset($options['joins']) && !is_array($options['joins'])) {
989  $options['joins'] = array($options['joins']);
990  } elseif (!isset($options['joins'])) {
991  $options['joins'] = array();
992  }
993 
994  $options['joins'] = array_merge($options['joins'], $clauses['joins']);
995  }
996 
998  }
999 
1007  function getEntityAttributeWhereSql(array $options = array()) {
1008 
1009  if (!isset($options['types'])) {
1010  throw new \InvalidArgumentException("The entity type must be defined for elgg_get_entities_from_attributes()");
1011  }
1012 
1013  if (is_array($options['types']) && count($options['types']) !== 1) {
1014  throw new \InvalidArgumentException("Only one type can be passed to elgg_get_entities_from_attributes()");
1015  }
1016 
1017  // type can be passed as string or array
1018  $type = $options['types'];
1019  if (is_array($type)) {
1020  $type = $type[0];
1021  }
1022 
1023  // @todo the types should be defined somewhere (as constant on \ElggEntity?)
1024  if (!in_array($type, array('group', 'object', 'site', 'user'))) {
1025  throw new \InvalidArgumentException("Invalid type '$type' passed to elgg_get_entities_from_attributes()");
1026  }
1027 
1028 
1029  $type_table = "{$this->CONFIG->dbprefix}{$type}s_entity";
1030 
1031  $return = array(
1032  'joins' => array(),
1033  'wheres' => array(),
1034  );
1035 
1036  // short circuit if nothing requested
1037  if ($options['attribute_name_value_pairs'] == ELGG_ENTITIES_ANY_VALUE) {
1038  return $return;
1039  }
1040 
1041  if (!is_array($options['attribute_name_value_pairs'])) {
1042  throw new \InvalidArgumentException("attribute_name_value_pairs must be an array for elgg_get_entities_from_attributes()");
1043  }
1044 
1045  $wheres = array();
1046 
1047  // check if this is an array of pairs or just a single pair.
1048  $pairs = $options['attribute_name_value_pairs'];
1049  if (isset($pairs['name']) || isset($pairs['value'])) {
1050  $pairs = array($pairs);
1051  }
1052 
1053  $pair_wheres = array();
1054  foreach ($pairs as $index => $pair) {
1055  // must have at least a name and value
1056  if (!isset($pair['name']) || !isset($pair['value'])) {
1057  continue;
1058  }
1059 
1060  if (isset($pair['operand'])) {
1061  $operand = sanitize_string($pair['operand']);
1062  } else {
1063  $operand = '=';
1064  }
1065 
1066  if (is_numeric($pair['value'])) {
1067  $value = sanitize_string($pair['value']);
1068  } else if (is_array($pair['value'])) {
1069  $values_array = array();
1070  foreach ($pair['value'] as $pair_value) {
1071  if (is_numeric($pair_value)) {
1072  $values_array[] = sanitize_string($pair_value);
1073  } else {
1074  $values_array[] = "'" . sanitize_string($pair_value) . "'";
1075  }
1076  }
1077 
1078  $operand = 'IN';
1079  if ($values_array) {
1080  $value = '(' . implode(', ', $values_array) . ')';
1081  }
1082 
1083  } else {
1084  $value = "'" . sanitize_string($pair['value']) . "'";
1085  }
1086 
1087  $name = sanitize_string($pair['name']);
1088 
1089  // case sensitivity can be specified per pair
1090  $pair_binary = '';
1091  if (isset($pair['case_sensitive'])) {
1092  $pair_binary = ($pair['case_sensitive']) ? 'BINARY ' : '';
1093  }
1094 
1095  $pair_wheres[] = "({$pair_binary}type_table.$name $operand $value)";
1096  }
1097 
1098  if ($where = implode(" {$options['attribute_name_value_pairs_operator']} ", $pair_wheres)) {
1099  $return['wheres'][] = "($where)";
1100 
1101  $return['joins'][] = "JOIN $type_table type_table ON e.guid = type_table.guid";
1102  }
1103 
1104  return $return;
1105  }
1106 
1124  function getDates($type = '', $subtype = '', $container_guid = 0, $site_guid = 0,
1125  $order_by = 'time_created') {
1126 
1127 
1128 
1129  $site_guid = (int) $site_guid;
1130  if ($site_guid == 0) {
1131  $site_guid = $this->CONFIG->site_guid;
1132  }
1133  $where = array();
1134 
1135  if ($type != "") {
1137  $where[] = "type='$type'";
1138  }
1139 
1140  if (is_array($subtype)) {
1141  $tempwhere = "";
1142  if (sizeof($subtype)) {
1143  foreach ($subtype as $typekey => $subtypearray) {
1144  foreach ($subtypearray as $subtypeval) {
1145  $typekey = sanitise_string($typekey);
1146  if (!empty($subtypeval)) {
1147  if (!$subtypeval = (int) get_subtype_id($typekey, $subtypeval)) {
1148  return false;
1149  }
1150  } else {
1151  $subtypeval = 0;
1152  }
1153  if (!empty($tempwhere)) {
1154  $tempwhere .= " or ";
1155  }
1156  $tempwhere .= "(type = '{$typekey}' and subtype = {$subtypeval})";
1157  }
1158  }
1159  }
1160  if (!empty($tempwhere)) {
1161  $where[] = "({$tempwhere})";
1162  }
1163  } else {
1164  if ($subtype) {
1165  if (!$subtype_id = get_subtype_id($type, $subtype)) {
1166  return false;
1167  } else {
1168  $where[] = "subtype=$subtype_id";
1169  }
1170  }
1171  }
1172 
1173  if ($container_guid !== 0) {
1174  if (is_array($container_guid)) {
1175  foreach ($container_guid as $key => $val) {
1176  $container_guid[$key] = (int) $val;
1177  }
1178  $where[] = "container_guid in (" . implode(",", $container_guid) . ")";
1179  } else {
1181  $where[] = "container_guid = {$container_guid}";
1182  }
1183  }
1184 
1185  if ($site_guid > 0) {
1186  $where[] = "site_guid = {$site_guid}";
1187  }
1188 
1189  $where[] = _elgg_get_access_where_sql(array('table_alias' => ''));
1190 
1191  $sql = "SELECT DISTINCT EXTRACT(YEAR_MONTH FROM FROM_UNIXTIME(time_created)) AS yearmonth
1192  FROM {$this->CONFIG->dbprefix}entities where ";
1193 
1194  foreach ($where as $w) {
1195  $sql .= " $w and ";
1196  }
1197 
1198  $sql .= "1=1 ORDER BY $order_by";
1199  if ($result = _elgg_services()->db->getData($sql)) {
1200  $endresult = array();
1201  foreach ($result as $res) {
1202  $endresult[] = $res->yearmonth;
1203  }
1204  return $endresult;
1205  }
1206  return false;
1207  }
1208 
1221  function updateLastAction($guid, $posted = null) {
1222 
1223  $guid = (int)$guid;
1224  $posted = (int)$posted;
1225 
1226  if (!$posted) {
1227  $posted = time();
1228  }
1229 
1230  if ($guid) {
1231  //now add to the river updated table
1232  $query = "UPDATE {$this->CONFIG->dbprefix}entities SET last_action = {$posted} WHERE guid = {$guid}";
1233  $result = _elgg_services()->db->updateData($query);
1234  if ($result) {
1235  return true;
1236  } else {
1237  return false;
1238  }
1239  } else {
1240  return false;
1241  }
1242  }
1243 
1253  public function getUserForPermissionsCheck($guid = 0) {
1254  if (!$guid) {
1255  return _elgg_services()->session->getLoggedInUser();
1256  }
1257 
1258  // need to ignore access and show hidden entities for potential hidden/disabled users
1259  $ia = _elgg_services()->session->setIgnoreAccess(true);
1260  $show_hidden = access_show_hidden_entities(true);
1261 
1262  $user = $this->get($guid, 'user');
1263 
1264  _elgg_services()->session->setIgnoreAccess($ia);
1265  access_show_hidden_entities($show_hidden);
1266 
1267  if (!$user) {
1268  // requested to check access for a specific user_guid, but there is no user entity, so the caller
1269  // should cancel the check and return false
1270  $message = _elgg_services()->translator->translate('UserFetchFailureException', array($guid));
1271  _elgg_services()->logger->warn($message);
1272 
1273  throw new UserFetchFailureException();
1274  }
1275 
1276  return $user;
1277  }
1278 }
enable($guid, $recursive=true)
Enable an entity.
$table
Definition: cron.php:34
if($guid==elgg_get_logged_in_user_guid()) $name
Definition: delete.php:21
$e
Definition: metadata.php:12
get_subtype_id($type, $subtype)
Return the id for a given subtype.
Definition: entities.php:27
$defaults
const ELGG_ENTITIES_NO_VALUE
Definition: elgglib.php:2010
if($selector) $select
Definition: filter.php:36
$value
Definition: longtext.php:26
$column
Definition: add.php:13
$subtype
Definition: delete.php:28
$return
Definition: opendd.php:15
if(!$count) $offset
Definition: pagination.php:26
$guid
Removes an admin notice.
autoJoinTables(array $options)
Decorate getEntities() options in order to auto-join secondary tables where it's safe to do so...
_elgg_sql_reverse_order_by_clause($order_by)
Reverses the ordering in an ORDER BY clause.
Definition: elgglib.php:1723
sanitize_string($string)
Sanitizes a string for use in a query.
Definition: database.php:153
getRow($guid)
Returns a database row from the entities table.
Definition: EntityTable.php:46
$options
Elgg admin footer.
Definition: footer.php:6
getDates($type= '', $subtype= '', $container_guid=0, $site_guid=0, $order_by= 'time_created')
Returns a list of months in which entities were updated or created.
get_subtype_class_from_id($subtype_id)
Returns the class name for a subtype id.
Definition: entities.php:71
entity_row_to_elggstar($row)
Create an Elgg* object from a given entity row.
Definition: entities.php:193
getEntityTimeWhereSql($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.
updateLastAction($guid, $posted=null)
Update the last_action column in the entities table for $guid.
rowToElggStar($row)
Create an Elgg* object from a given entity row.
Definition: EntityTable.php:74
elgg_instanceof($entity, $type=null, $subtype=null, $class=null)
Checks if $entity is an and optionally for type and subtype.
Definition: entities.php:716
$limit
Definition: userpicker.php:38
$key
Definition: summary.php:34
getGuidBasedWhereSql($column, $guids)
Returns SQL where clause for owner and containers.
$item
Definition: item.php:12
Exception indicating a user could not be looked up for a permissions check.
sanitise_string($string)
Alias of sanitize_string.
Definition: database.php:166
$dbprefix
Definition: index.php:13
$user
Definition: ban.php:13
const ELGG_ENTITIES_ANY_VALUE
Definition: elgglib.php:2001
getEntityAttributeWhereSql(array $options=array())
Get the join and where clauses for working with entity attributes.
_elgg_services(\Elgg\Di\ServiceProvider $services=null)
Get the global service provider.
Definition: autoloader.php:17
access_get_show_hidden_status()
Return current status of showing disabled entities.
Definition: access.php:170
exists($guid)
Does an entity exist?
getEntities(array $options=array())
Returns an array of entities with optional filtering.
fetchFromSql($sql,\ElggBatch $batch=null)
Return entities from an SQL query generated by elgg_get_entities.
$posted
Definition: comment.php:83
$guids
access_show_hidden_entities($show_hidden)
Show or hide disabled entities.
Definition: access.php:158
$subtypes
$entity
Definition: delete.php:7
$row
sanitise_int($int, $signed=true)
Alias of sanitize_int.
Definition: database.php:194
_elgg_get_entity_attribute_where_sql(array $options=array())
Get the join and where clauses for working with entity attributes.
Definition: entities.php:465
is_memcache_available()
Return true if memcache is available and configured.
Definition: memcache.php:16
$rows
getEntitiesFromAttributes(array $options=array())
Gets entities based upon attributes in secondary tables.
$container_guid
elgg_get_entities_from_relationship($options)
Return entities matching a given query joining against a relationship.
_elgg_normalize_plural_options_array($options, $singulars)
Normalise the singular keys in an options array to plural keys.
Definition: elgglib.php:1440
sanitize_int($int, $signed=true)
Sanitizes an integer for database use.
Definition: database.php:180
getEntityTypeSubtypeWhereSql($table, $types, $subtypes, $pairs)
Returns SQL where clause for type and subtype on main entity table.
_elgg_get_access_where_sql(array $options=array())
Returns the SQL where clause for enforcing read access to data.
Definition: access.php:214
$attributes
Definition: ajax_loader.php:13
get_entity($guid)
Loads and returns an entity object from a guid.
Definition: entities.php:204
$access
Definition: save.php:15
if(!$display_name) $type
Definition: delete.php:27
__construct()
Constructor.
Definition: EntityTable.php:27
getUserForPermissionsCheck($guid=0)
Get a user by GUID even if the entity is hidden or disabled.