metastrings.php 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. <?php
  2. /**
  3. * Elgg metastrngs
  4. * Functions to manage object metastrings.
  5. *
  6. * @package Elgg.Core
  7. * @subpackage DataModel.MetaStrings
  8. */
  9. /**
  10. * Gets the metastring identifier for a value.
  11. *
  12. * Elgg normalizes the names and values of annotations and metadata. This function
  13. * provides the identifier used as the index in the metastrings table. Plugin
  14. * developers should only use this if denormalizing names/values for performance
  15. * reasons (to avoid multiple joins on the metastrings table).
  16. *
  17. * @param string $string The value
  18. * @param bool $case_sensitive Should the retrieval be case sensitive?
  19. * If not, there may be more than one result
  20. *
  21. * @return int|array metastring id or array of ids
  22. * @since 1.9.0
  23. */
  24. function elgg_get_metastring_id($string, $case_sensitive = true) {
  25. return _elgg_services()->metastringsTable->getId($string, $case_sensitive);
  26. }
  27. /**
  28. * Add a metastring.
  29. *
  30. * @warning You should not call this directly. Use elgg_get_metastring_id().
  31. *
  32. * @param string $string The value to be normalized
  33. * @return int The identifier for this string
  34. */
  35. function _elgg_add_metastring($string) {
  36. return _elgg_services()->metastringsTable->add($string);
  37. }
  38. /**
  39. * Returns an array of either \ElggAnnotation or \ElggMetadata objects.
  40. * Accepts all elgg_get_entities() options for entity restraints.
  41. *
  42. * @see elgg_get_entities
  43. *
  44. * @param array $options Array in format:
  45. *
  46. * metastring_names => null|ARR metastring names
  47. *
  48. * metastring_values => null|ARR metastring values
  49. *
  50. * metastring_ids => null|ARR metastring ids
  51. *
  52. * metastring_case_sensitive => BOOL Overall Case sensitive
  53. *
  54. * metastring_owner_guids => null|ARR Guids for metadata owners
  55. *
  56. * metastring_created_time_lower => INT Lower limit for created time.
  57. *
  58. * metastring_created_time_upper => INT Upper limit for created time.
  59. *
  60. * metastring_calculation => STR Perform the MySQL function on the metastring values
  61. * returned.
  62. * This differs from egef_annotation_calculation in that
  63. * it returns only the calculation of all annotation values.
  64. * You can sum, avg, count, etc. egef_annotation_calculation()
  65. * returns \ElggEntities ordered by a calculation on their
  66. * annotation values.
  67. *
  68. * metastring_type => STR metadata or annotation(s)
  69. *
  70. * @return \ElggExtender[]|int An array or count of metastring based objects
  71. * @access private
  72. */
  73. function _elgg_get_metastring_based_objects($options) {
  74. $options = _elgg_normalize_metastrings_options($options);
  75. switch ($options['metastring_type']) {
  76. case 'metadata':
  77. $type = 'metadata';
  78. $callback = 'row_to_elggmetadata';
  79. break;
  80. case 'annotations':
  81. case 'annotation':
  82. $type = 'annotations';
  83. $callback = 'row_to_elggannotation';
  84. break;
  85. default:
  86. return false;
  87. }
  88. $defaults = array(
  89. // entities
  90. 'types' => ELGG_ENTITIES_ANY_VALUE,
  91. 'subtypes' => ELGG_ENTITIES_ANY_VALUE,
  92. 'type_subtype_pairs' => ELGG_ENTITIES_ANY_VALUE,
  93. 'guids' => ELGG_ENTITIES_ANY_VALUE,
  94. 'owner_guids' => ELGG_ENTITIES_ANY_VALUE,
  95. 'container_guids' => ELGG_ENTITIES_ANY_VALUE,
  96. 'site_guids' => get_config('site_guid'),
  97. 'modified_time_lower' => ELGG_ENTITIES_ANY_VALUE,
  98. 'modified_time_upper' => ELGG_ENTITIES_ANY_VALUE,
  99. 'created_time_lower' => ELGG_ENTITIES_ANY_VALUE,
  100. 'created_time_upper' => ELGG_ENTITIES_ANY_VALUE,
  101. // options are normalized to the plural in case we ever add support for them.
  102. 'metastring_names' => ELGG_ENTITIES_ANY_VALUE,
  103. 'metastring_values' => ELGG_ENTITIES_ANY_VALUE,
  104. //'metastring_name_value_pairs' => ELGG_ENTITIES_ANY_VALUE,
  105. //'metastring_name_value_pairs_operator' => 'AND',
  106. 'metastring_case_sensitive' => true,
  107. //'order_by_metastring' => array(),
  108. 'metastring_calculation' => ELGG_ENTITIES_NO_VALUE,
  109. 'metastring_created_time_lower' => ELGG_ENTITIES_ANY_VALUE,
  110. 'metastring_created_time_upper' => ELGG_ENTITIES_ANY_VALUE,
  111. 'metastring_owner_guids' => ELGG_ENTITIES_ANY_VALUE,
  112. 'metastring_ids' => ELGG_ENTITIES_ANY_VALUE,
  113. // sql
  114. 'order_by' => 'n_table.time_created ASC, n_table.id ASC',
  115. 'limit' => elgg_get_config('default_limit'),
  116. 'offset' => 0,
  117. 'count' => false,
  118. 'selects' => array(),
  119. 'wheres' => array(),
  120. 'joins' => array(),
  121. 'distinct' => true,
  122. 'preload_owners' => false,
  123. 'callback' => $callback,
  124. );
  125. // @todo Ignore site_guid right now because of #2910
  126. $options['site_guid'] = ELGG_ENTITIES_ANY_VALUE;
  127. $options = array_merge($defaults, $options);
  128. // can't use helper function with type_subtype_pair because
  129. // it's already an array...just need to merge it
  130. if (isset($options['type_subtype_pair'])) {
  131. if (isset($options['type_subtype_pairs'])) {
  132. $options['type_subtype_pairs'] = array_merge($options['type_subtype_pairs'],
  133. $options['type_subtype_pair']);
  134. } else {
  135. $options['type_subtype_pairs'] = $options['type_subtype_pair'];
  136. }
  137. }
  138. $singulars = array(
  139. 'type', 'subtype', 'type_subtype_pair',
  140. 'guid', 'owner_guid', 'container_guid', 'site_guid',
  141. 'metastring_name', 'metastring_value',
  142. 'metastring_owner_guid', 'metastring_id',
  143. 'select', 'where', 'join'
  144. );
  145. $options = _elgg_normalize_plural_options_array($options, $singulars);
  146. if (!$options) {
  147. return false;
  148. }
  149. $db_prefix = elgg_get_config('dbprefix');
  150. // evaluate where clauses
  151. if (!is_array($options['wheres'])) {
  152. $options['wheres'] = array($options['wheres']);
  153. }
  154. $wheres = $options['wheres'];
  155. // entities
  156. $wheres[] = _elgg_get_entity_type_subtype_where_sql('e', $options['types'],
  157. $options['subtypes'], $options['type_subtype_pairs']);
  158. $wheres[] = _elgg_get_guid_based_where_sql('e.guid', $options['guids']);
  159. $wheres[] = _elgg_get_guid_based_where_sql('e.owner_guid', $options['owner_guids']);
  160. $wheres[] = _elgg_get_guid_based_where_sql('e.container_guid', $options['container_guids']);
  161. $wheres[] = _elgg_get_guid_based_where_sql('e.site_guid', $options['site_guids']);
  162. $wheres[] = _elgg_get_entity_time_where_sql('e', $options['created_time_upper'],
  163. $options['created_time_lower'], $options['modified_time_upper'], $options['modified_time_lower']);
  164. $wheres[] = _elgg_get_entity_time_where_sql('n_table', $options['metastring_created_time_upper'],
  165. $options['metastring_created_time_lower'], null, null);
  166. $wheres[] = _elgg_get_guid_based_where_sql('n_table.owner_guid',
  167. $options['metastring_owner_guids']);
  168. // see if any functions failed
  169. // remove empty strings on successful functions
  170. foreach ($wheres as $i => $where) {
  171. if ($where === false) {
  172. return false;
  173. } elseif (empty($where)) {
  174. unset($wheres[$i]);
  175. }
  176. }
  177. // remove identical where clauses
  178. $wheres = array_unique($wheres);
  179. // evaluate join clauses
  180. if (!is_array($options['joins'])) {
  181. $options['joins'] = array($options['joins']);
  182. }
  183. $joins = $options['joins'];
  184. $joins[] = "JOIN {$db_prefix}entities e ON n_table.entity_guid = e.guid";
  185. // evaluate selects
  186. if (!is_array($options['selects'])) {
  187. $options['selects'] = array($options['selects']);
  188. }
  189. $selects = $options['selects'];
  190. // For performance reasons we don't want the joins required for metadata / annotations
  191. // unless we're going through one of their callbacks.
  192. // this means we expect the functions passing different callbacks to pass their required joins.
  193. // If we're doing a calculation
  194. $custom_callback = ($options['callback'] == 'row_to_elggmetadata'
  195. || $options['callback'] == 'row_to_elggannotation');
  196. $is_calculation = $options['metastring_calculation'] ? true : false;
  197. if ($custom_callback || $is_calculation) {
  198. $joins[] = "JOIN {$db_prefix}metastrings n on n_table.name_id = n.id";
  199. $joins[] = "JOIN {$db_prefix}metastrings v on n_table.value_id = v.id";
  200. $selects[] = 'n.string as name';
  201. $selects[] = 'v.string as value';
  202. }
  203. foreach ($joins as $i => $join) {
  204. if ($join === false) {
  205. return false;
  206. } elseif (empty($join)) {
  207. unset($joins[$i]);
  208. }
  209. }
  210. // metastrings
  211. $metastring_clauses = _elgg_get_metastring_sql('n_table', $options['metastring_names'],
  212. $options['metastring_values'], null, $options['metastring_ids'],
  213. $options['metastring_case_sensitive']);
  214. if ($metastring_clauses) {
  215. $wheres = array_merge($wheres, $metastring_clauses['wheres']);
  216. $joins = array_merge($joins, $metastring_clauses['joins']);
  217. } else {
  218. $wheres[] = _elgg_get_access_where_sql(array(
  219. 'table_alias' => 'n_table',
  220. 'guid_column' => 'entity_guid',
  221. ));
  222. }
  223. $distinct = $options['distinct'] ? "DISTINCT " : "";
  224. if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) {
  225. $selects = array_unique($selects);
  226. // evalutate selects
  227. $select_str = '';
  228. if ($selects) {
  229. foreach ($selects as $select) {
  230. $select_str .= ", $select";
  231. }
  232. }
  233. $query = "SELECT $distinct n_table.*{$select_str} FROM {$db_prefix}$type n_table";
  234. } elseif ($options['count']) {
  235. // count is over the entities
  236. $query = "SELECT count($distinct e.guid) as calculation FROM {$db_prefix}$type n_table";
  237. } else {
  238. $query = "SELECT {$options['metastring_calculation']}(v.string) as calculation FROM {$db_prefix}$type n_table";
  239. }
  240. // remove identical join clauses
  241. $joins = array_unique($joins);
  242. // add joins
  243. foreach ($joins as $j) {
  244. $query .= " $j ";
  245. }
  246. // add wheres
  247. $query .= ' WHERE ';
  248. foreach ($wheres as $w) {
  249. $query .= " $w AND ";
  250. }
  251. // Add access controls
  252. $query .= _elgg_get_access_where_sql(array('table_alias' => 'e'));
  253. // reverse order by
  254. if (isset($options['reverse_order_by']) && $options['reverse_order_by']) {
  255. $options['order_by'] = _elgg_sql_reverse_order_by_clause($options['order_by']);
  256. }
  257. if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) {
  258. if (isset($options['group_by'])) {
  259. $options['group_by'] = sanitise_string($options['group_by']);
  260. $query .= " GROUP BY {$options['group_by']}";
  261. }
  262. if (isset($options['order_by']) && $options['order_by']) {
  263. $options['order_by'] = sanitise_string($options['order_by']);
  264. $query .= " ORDER BY {$options['order_by']}, n_table.id";
  265. }
  266. if ($options['limit']) {
  267. $limit = sanitise_int($options['limit']);
  268. $offset = sanitise_int($options['offset'], false);
  269. $query .= " LIMIT $offset, $limit";
  270. }
  271. $dt = get_data($query, $options['callback']);
  272. if ($options['preload_owners'] && is_array($dt) && count($dt) > 1) {
  273. _elgg_services()->entityPreloader->preload($dt, ['owner_guid']);
  274. }
  275. return $dt;
  276. } else {
  277. $result = get_data_row($query);
  278. return $result->calculation;
  279. }
  280. }
  281. /**
  282. * Returns an array of joins and wheres for use in metastrings.
  283. *
  284. * @note The $pairs is reserved for name/value pairs if we want to implement those.
  285. *
  286. * @param string $table The annotation or metadata table name or alias
  287. * @param array $names An array of names
  288. * @param array $values An array of values
  289. * @param array $pairs Name / value pairs. Not currently used.
  290. * @param array $ids Metastring IDs
  291. * @param bool $case_sensitive Should name and values be case sensitive?
  292. *
  293. * @return array
  294. * @access private
  295. */
  296. function _elgg_get_metastring_sql($table, $names = null, $values = null,
  297. $pairs = null, $ids = null, $case_sensitive = false) {
  298. if ((!$names && $names !== 0)
  299. && (!$values && $values !== 0)
  300. && !$ids
  301. && (!$pairs && $pairs !== 0)) {
  302. return array();
  303. }
  304. $db_prefix = elgg_get_config('dbprefix');
  305. // binary forces byte-to-byte comparision of strings, making
  306. // it case- and diacritical-mark- sensitive.
  307. // only supported on values.
  308. $binary = ($case_sensitive) ? ' BINARY ' : '';
  309. $return = array (
  310. 'joins' => array (),
  311. 'wheres' => array()
  312. );
  313. $wheres = array();
  314. // get names wheres and joins
  315. $names_where = '';
  316. if ($names !== null) {
  317. if (!is_array($names)) {
  318. $names = array($names);
  319. }
  320. $sanitised_names = array();
  321. foreach ($names as $name) {
  322. // normalise to 0.
  323. if (!$name) {
  324. $name = '0';
  325. }
  326. $sanitised_names[] = '\'' . sanitise_string($name) . '\'';
  327. }
  328. if ($names_str = implode(',', $sanitised_names)) {
  329. $return['joins'][] = "JOIN {$db_prefix}metastrings msn on $table.name_id = msn.id";
  330. $names_where = "(msn.string IN ($names_str))";
  331. }
  332. }
  333. // get values wheres and joins
  334. $values_where = '';
  335. if ($values !== null) {
  336. if (!is_array($values)) {
  337. $values = array($values);
  338. }
  339. $sanitised_values = array();
  340. foreach ($values as $value) {
  341. // normalize to 0
  342. if (!$value) {
  343. $value = 0;
  344. }
  345. $sanitised_values[] = '\'' . sanitise_string($value) . '\'';
  346. }
  347. if ($values_str = implode(',', $sanitised_values)) {
  348. $return['joins'][] = "JOIN {$db_prefix}metastrings msv on $table.value_id = msv.id";
  349. $values_where = "({$binary}msv.string IN ($values_str))";
  350. }
  351. }
  352. if ($ids !== null) {
  353. if (!is_array($ids)) {
  354. $ids = array($ids);
  355. }
  356. $ids_str = implode(',', $ids);
  357. if ($ids_str) {
  358. $wheres[] = "n_table.id IN ($ids_str)";
  359. }
  360. }
  361. if ($names_where && $values_where) {
  362. $wheres[] = "($names_where AND $values_where)";
  363. } elseif ($names_where) {
  364. $wheres[] = $names_where;
  365. } elseif ($values_where) {
  366. $wheres[] = $values_where;
  367. }
  368. $wheres[] = _elgg_get_access_where_sql(array(
  369. 'table_alias' => $table,
  370. 'guid_column' => 'entity_guid',
  371. ));
  372. if ($where = implode(' AND ', $wheres)) {
  373. $return['wheres'][] = "($where)";
  374. }
  375. return $return;
  376. }
  377. /**
  378. * Normalizes metadata / annotation option names to their corresponding metastrings name.
  379. *
  380. * @param array $options An options array
  381. * @return array
  382. * @access private
  383. */
  384. function _elgg_normalize_metastrings_options(array $options = array()) {
  385. // support either metastrings_type or metastring_type
  386. // because I've made this mistake many times and hunting it down is a pain...
  387. $type = elgg_extract('metastring_type', $options, null);
  388. $type = elgg_extract('metastrings_type', $options, $type);
  389. $options['metastring_type'] = $type;
  390. // support annotation_ and annotations_ because they're way too easy to confuse
  391. $prefixes = array('metadata_', 'annotation_', 'annotations_');
  392. // map the metadata_* options to metastring_* options
  393. $map = array(
  394. 'names' => 'metastring_names',
  395. 'values' => 'metastring_values',
  396. 'case_sensitive' => 'metastring_case_sensitive',
  397. 'owner_guids' => 'metastring_owner_guids',
  398. 'created_time_lower' => 'metastring_created_time_lower',
  399. 'created_time_upper' => 'metastring_created_time_upper',
  400. 'calculation' => 'metastring_calculation',
  401. 'ids' => 'metastring_ids',
  402. );
  403. foreach ($prefixes as $prefix) {
  404. $singulars = array("{$prefix}name", "{$prefix}value", "{$prefix}owner_guid", "{$prefix}id");
  405. $options = _elgg_normalize_plural_options_array($options, $singulars);
  406. foreach ($map as $specific => $normalized) {
  407. $key = $prefix . $specific;
  408. if (isset($options[$key])) {
  409. $options[$normalized] = $options[$key];
  410. }
  411. }
  412. }
  413. return $options;
  414. }
  415. /**
  416. * Enables or disables a metastrings-based object by its id.
  417. *
  418. * @warning To enable disabled metastrings you must first use
  419. * {@link access_show_hidden_entities()}.
  420. *
  421. * @param int $id The object's ID
  422. * @param string $enabled Value to set to: yes or no
  423. * @param string $type Metastring type: metadata or annotation
  424. *
  425. * @return bool
  426. * @throws InvalidParameterException
  427. * @access private
  428. */
  429. function _elgg_set_metastring_based_object_enabled_by_id($id, $enabled, $type) {
  430. $id = (int)$id;
  431. $db_prefix = elgg_get_config('dbprefix');
  432. $object = _elgg_get_metastring_based_object_from_id($id, $type);
  433. switch ($type) {
  434. case 'annotation':
  435. case 'annotations':
  436. $type = 'annotation';
  437. $table = "{$db_prefix}annotations";
  438. break;
  439. case 'metadata':
  440. $table = "{$db_prefix}metadata";
  441. break;
  442. }
  443. if ($enabled === 'yes' || $enabled === 1 || $enabled === true) {
  444. $enabled = 'yes';
  445. $event = 'enable';
  446. } elseif ($enabled === 'no' || $enabled === 0 || $enabled === false) {
  447. $enabled = 'no';
  448. $event = 'disable';
  449. } else {
  450. return false;
  451. }
  452. $return = false;
  453. if ($object) {
  454. // don't set it if it's already set.
  455. if ($object->enabled == $enabled) {
  456. $return = false;
  457. } elseif ($object->canEdit() && (elgg_trigger_event($event, $type, $object))) {
  458. $return = update_data("UPDATE $table SET enabled = '$enabled' where id = $id");
  459. }
  460. }
  461. return $return;
  462. }
  463. /**
  464. * Runs metastrings-based objects found using $options through $callback
  465. *
  466. * @warning Unlike _elgg_get_metastring_based_objects() this will not accept an
  467. * empty options array!
  468. *
  469. * @warning This returns null on no ops.
  470. *
  471. * @param array $options An options array. {@link _elgg_get_metastring_based_objects()}
  472. * @param string $callback The callback to pass each result through
  473. * @param bool $inc_offset Increment the offset? Pass false for callbacks that delete / disable
  474. *
  475. * @return bool|null true on success, false on failure, null if no objects are found.
  476. * @access private
  477. */
  478. function _elgg_batch_metastring_based_objects(array $options, $callback, $inc_offset = true) {
  479. if (!$options || !is_array($options)) {
  480. return false;
  481. }
  482. $batch = new \ElggBatch('_elgg_get_metastring_based_objects', $options, $callback, 50, $inc_offset);
  483. return $batch->callbackResult;
  484. }
  485. /**
  486. * Returns a singular metastring-based object by its ID.
  487. *
  488. * @param int $id The metastring-based object's ID
  489. * @param string $type The type: annotation or metadata
  490. * @return \ElggExtender
  491. * @access private
  492. */
  493. function _elgg_get_metastring_based_object_from_id($id, $type) {
  494. $id = (int)$id;
  495. if (!$id) {
  496. return false;
  497. }
  498. $options = array(
  499. 'metastring_type' => $type,
  500. 'metastring_id' => $id,
  501. );
  502. $obj = _elgg_get_metastring_based_objects($options);
  503. if ($obj && count($obj) == 1) {
  504. return $obj[0];
  505. }
  506. return false;
  507. }
  508. /**
  509. * Deletes a metastring-based object by its id
  510. *
  511. * @param int $id The object's ID
  512. * @param string $type The object's metastring type: annotation or metadata
  513. * @return bool
  514. * @access private
  515. */
  516. function _elgg_delete_metastring_based_object_by_id($id, $type) {
  517. $id = (int)$id;
  518. $db_prefix = elgg_get_config('dbprefix');
  519. switch ($type) {
  520. case 'annotations':
  521. case 'annotation':
  522. $table = $db_prefix . 'annotations';
  523. $type = 'annotation';
  524. break;
  525. case 'metadata':
  526. $table = $db_prefix . 'metadata';
  527. $type = 'metadata';
  528. break;
  529. default:
  530. return false;
  531. }
  532. $obj = _elgg_get_metastring_based_object_from_id($id, $type);
  533. if ($obj) {
  534. // Tidy up if memcache is enabled.
  535. // @todo only metadata is supported
  536. if ($type == 'metadata') {
  537. static $metabyname_memcache;
  538. if ((!$metabyname_memcache) && (is_memcache_available())) {
  539. $metabyname_memcache = new \ElggMemcache('metabyname_memcache');
  540. }
  541. if ($metabyname_memcache) {
  542. // @todo why name_id? is that even populated?
  543. $metabyname_memcache->delete("{$obj->entity_guid}:{$obj->name_id}");
  544. }
  545. }
  546. if ($obj->canEdit()) {
  547. // bc code for when we triggered 'delete', 'annotations' #4770
  548. $result = true;
  549. if ($type == "annotation") {
  550. $result = elgg_trigger_event('delete', 'annotations', $obj);
  551. if ($result === false) {
  552. elgg_deprecated_notice("Use the event 'delete', 'annotation'", 1.9);
  553. }
  554. }
  555. if (elgg_trigger_event('delete', $type, $obj) && $result) {
  556. return (bool)delete_data("DELETE FROM $table WHERE id = $id");
  557. }
  558. }
  559. }
  560. return false;
  561. }
  562. /**
  563. * Returns options to pass to elgg_get_entities() for metastrings operations.
  564. *
  565. * @param string $type Metastring type: annotation or metadata
  566. * @param array $options Options
  567. *
  568. * @return array
  569. * @access private
  570. */
  571. function _elgg_entities_get_metastrings_options($type, $options) {
  572. $valid_types = array('metadata', 'annotation');
  573. if (!in_array($type, $valid_types)) {
  574. return false;
  575. }
  576. // the options for annotations are singular (annotation_name) but the table
  577. // is plural (elgg_annotations) so rewrite for the table name.
  578. $n_table = ($type == 'annotation') ? 'annotations' : $type;
  579. $singulars = array("{$type}_name", "{$type}_value",
  580. "{$type}_name_value_pair", "{$type}_owner_guid");
  581. $options = _elgg_normalize_plural_options_array($options, $singulars);
  582. $clauses = _elgg_get_entity_metadata_where_sql('e', $n_table, $options["{$type}_names"],
  583. $options["{$type}_values"], $options["{$type}_name_value_pairs"],
  584. $options["{$type}_name_value_pairs_operator"], $options["{$type}_case_sensitive"],
  585. $options["order_by_{$type}"], $options["{$type}_owner_guids"]);
  586. if ($clauses) {
  587. // merge wheres to pass to elgg_get_entities()
  588. if (isset($options['wheres']) && !is_array($options['wheres'])) {
  589. $options['wheres'] = array($options['wheres']);
  590. } elseif (!isset($options['wheres'])) {
  591. $options['wheres'] = array();
  592. }
  593. $options['wheres'] = array_merge($options['wheres'], $clauses['wheres']);
  594. // merge joins to pass to elgg_get_entities()
  595. if (isset($options['joins']) && !is_array($options['joins'])) {
  596. $options['joins'] = array($options['joins']);
  597. } elseif (!isset($options['joins'])) {
  598. $options['joins'] = array();
  599. }
  600. $options['joins'] = array_merge($options['joins'], $clauses['joins']);
  601. if ($clauses['orders']) {
  602. $order_by_metadata = implode(", ", $clauses['orders']);
  603. if (isset($options['order_by']) && $options['order_by']) {
  604. $options['order_by'] = "$order_by_metadata, {$options['order_by']}";
  605. } else {
  606. $options['order_by'] = "$order_by_metadata, e.time_created DESC";
  607. }
  608. }
  609. }
  610. return $options;
  611. }
  612. /**
  613. * Metastring unit tests
  614. *
  615. * @param string $hook unit_test
  616. * @param string $type system
  617. * @param array $value Array of other tests
  618. *
  619. * @return array
  620. * @access private
  621. */
  622. function _elgg_metastrings_test($hook, $type, $value) {
  623. global $CONFIG;
  624. $value[] = $CONFIG->path . 'engine/tests/ElggCoreMetastringsTest.php';
  625. return $value;
  626. }
  627. return function(\Elgg\EventsService $events, \Elgg\HooksRegistrationService $hooks) {
  628. $hooks->registerHandler('unit_test', 'system', '_elgg_metastrings_test');
  629. };