callbacks.inc 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099
  1. <?php
  2. /**
  3. * @file
  4. * Provides various callbacks for the whole core module integration.
  5. */
  6. /**
  7. * Callback for getting properties of an entity.
  8. */
  9. function entity_metadata_entity_get_properties($entity, array $options, $name, $entity_type) {
  10. if ($name == 'url') {
  11. $return = entity_uri($entity_type, $entity);
  12. return url($return['path'], $return['options'] + $options);
  13. }
  14. }
  15. /**
  16. * Callback for getting book node properties.
  17. * @see entity_metadata_book_entity_info_alter()
  18. */
  19. function entity_metadata_book_get_properties($node, array $options, $name, $entity_type) {
  20. switch ($name) {
  21. case 'book':
  22. if (isset($node->book['bid'])) {
  23. return $node->book['bid'];
  24. }
  25. return NULL;
  26. case 'book_ancestors':
  27. $ancestors = array();
  28. while (!empty($node->book['plid']) && $node->book['plid'] != -1) {
  29. $link = book_link_load($node->book['plid']);
  30. array_unshift($ancestors, $link['nid']);
  31. $node = node_load($link['nid']);
  32. }
  33. return $ancestors;
  34. }
  35. }
  36. /**
  37. * Callback for getting comment properties.
  38. * @see entity_metadata_comment_entity_info_alter()
  39. */
  40. function entity_metadata_comment_get_properties($comment, array $options, $name) {
  41. switch ($name) {
  42. case 'name':
  43. return $comment->name;
  44. case 'mail':
  45. if ($comment->uid != 0) {
  46. $account = user_load($comment->uid);
  47. return $account->mail;
  48. }
  49. return $comment->mail;
  50. case 'edit_url':
  51. return url('comment/edit/' . $comment->cid, $options);
  52. case 'parent':
  53. if (!empty($comment->pid)) {
  54. return $comment->pid;
  55. }
  56. // There is no parent comment.
  57. return NULL;
  58. }
  59. }
  60. /**
  61. * Callback for setting comment properties.
  62. * @see entity_metadata_comment_entity_info_alter()
  63. */
  64. function entity_metadata_comment_setter($comment, $name, $value) {
  65. switch ($name) {
  66. case 'node':
  67. $comment->nid = $value;
  68. // Also set the bundle name.
  69. $node = node_load($value);
  70. $comment->node_type = 'comment_node_' . $node->type;
  71. break;
  72. }
  73. }
  74. /**
  75. * Callback for getting comment related node properties.
  76. * @see entity_metadata_comment_entity_info_alter()
  77. */
  78. function entity_metadata_comment_get_node_properties($node, array $options, $name, $entity_type) {
  79. switch ($name) {
  80. case 'comment_count':
  81. return isset($node->comment_count) ? $node->comment_count : 0;
  82. case 'comment_count_new':
  83. return comment_num_new($node->nid);
  84. case 'comments':
  85. $select = db_select('comment', 'c')
  86. ->fields('c', array('cid'))
  87. ->condition('c.nid', $node->nid);
  88. return array_keys($select->execute()->fetchAllKeyed(0, 0));
  89. }
  90. }
  91. /**
  92. * Getter callback for getting global languages.
  93. */
  94. function entity_metadata_locale_get_languages($data, array $options, $name) {
  95. return isset($GLOBALS[$name]) ? $GLOBALS[$name]->language : NULL;
  96. }
  97. /**
  98. * Getter callback for getting the preferred user language.
  99. */
  100. function entity_metadata_locale_get_user_language($account, array $options, $name) {
  101. return user_preferred_language($account)->language;
  102. }
  103. /**
  104. * Return the options lists for the node and comment status property.
  105. */
  106. function entity_metadata_status_options_list() {
  107. return array(
  108. NODE_PUBLISHED => t('Published'),
  109. NODE_NOT_PUBLISHED => t('Unpublished'),
  110. );
  111. }
  112. /**
  113. * Callback for getting node properties.
  114. *
  115. * @see entity_metadata_node_entity_info_alter()
  116. */
  117. function entity_metadata_node_get_properties($node, array $options, $name, $entity_type) {
  118. switch ($name) {
  119. case 'is_new':
  120. return empty($node->nid) || !empty($node->is_new);
  121. case 'source':
  122. if (!empty($node->tnid) && $source = node_load($node->tnid)) {
  123. return $source;
  124. }
  125. return NULL;
  126. case 'edit_url':
  127. return url('node/' . $node->nid . '/edit', $options);
  128. case 'author':
  129. return !empty($node->uid) ? $node->uid : drupal_anonymous_user();
  130. }
  131. }
  132. /**
  133. * Callback for determing access for node revision related properties.
  134. */
  135. function entity_metadata_node_revision_access($op, $name, $entity = NULL, $account = NULL) {
  136. return $op == 'view' ? user_access('view revisions', $account) : user_access('administer nodes', $account);
  137. }
  138. /**
  139. * Callback for getting poll properties.
  140. * @see entity_metadata_poll_entity_info_alter()
  141. */
  142. function entity_metadata_poll_node_get_properties($node, array $options, $name) {
  143. $total_votes = $highest_votes = 0;
  144. foreach ($node->choice as $choice) {
  145. if ($choice['chvotes'] > $highest_votes) {
  146. $winner = $choice;
  147. $highest_votes = $choice['chvotes'];
  148. }
  149. $total_votes = $total_votes + $choice['chvotes'];
  150. }
  151. if ($name == 'poll_duration') {
  152. return $node->runtime;
  153. }
  154. elseif ($name == 'poll_votes') {
  155. return $total_votes;
  156. }
  157. elseif (!isset($winner)) {
  158. // There is no poll winner yet.
  159. return NULL;
  160. }
  161. switch ($name) {
  162. case 'poll_winner_votes':
  163. return $winner['chvotes'];
  164. case 'poll_winner':
  165. return $winner['chtext'];
  166. case 'poll_winner_percent':
  167. return ($winner['chvotes'] / $total_votes) * 100;
  168. }
  169. }
  170. /**
  171. * Callback for getting statistics properties.
  172. * @see entity_metadata_statistics_entity_info_alter()
  173. */
  174. function entity_metadata_statistics_node_get_properties($node, array $options, $name) {
  175. $statistics = (array) statistics_get($node->nid);
  176. $statistics += array('totalcount' => 0, 'daycount' => 0, 'timestamp' => NULL);
  177. switch ($name) {
  178. case 'views':
  179. return $statistics['totalcount'];
  180. case 'day_views':
  181. return $statistics['daycount'];
  182. case 'last_view':
  183. return $statistics['timestamp'];
  184. }
  185. }
  186. /**
  187. * Access callback for restricted node statistics properties.
  188. */
  189. function entity_metadata_statistics_properties_access($op, $property, $entity = NULL, $account = NULL) {
  190. if ($property == 'views' && user_access('view post access counter', $account)) {
  191. return TRUE;
  192. }
  193. return user_access('access statistics', $account);
  194. }
  195. /**
  196. * Callback for getting site-wide properties.
  197. * @see entity_metadata_system_entity_info_alter()
  198. */
  199. function entity_metadata_system_get_properties($data = FALSE, array $options, $name) {
  200. switch ($name) {
  201. case 'name':
  202. return variable_get('site_name', 'Drupal');
  203. case 'url':
  204. return url('<front>', $options);
  205. case 'login_url':
  206. return url('user', $options);
  207. case 'current_user':
  208. return $GLOBALS['user']->uid ? $GLOBALS['user']->uid : drupal_anonymous_user();
  209. case 'current_date':
  210. return REQUEST_TIME;
  211. case 'current_page':
  212. // Subsequent getters of the struct retrieve the actual values.
  213. return array();
  214. default:
  215. return variable_get('site_' . $name, '');
  216. }
  217. }
  218. /**
  219. * Callback for getting properties for the current page request.
  220. * @see entity_metadata_system_entity_info_alter()
  221. */
  222. function entity_metadata_system_get_page_properties($data = array(), array $options, $name) {
  223. switch ($name) {
  224. case 'url':
  225. return $GLOBALS['base_root'] . request_uri();
  226. }
  227. }
  228. /**
  229. * Callback for getting file properties.
  230. * @see entity_metadata_system_entity_info_alter()
  231. */
  232. function entity_metadata_system_get_file_properties($file, array $options, $name) {
  233. switch ($name) {
  234. case 'name':
  235. return $file->filename;
  236. case 'mime':
  237. return $file->filemime;
  238. case 'size':
  239. return $file->filesize;
  240. case 'url':
  241. return url(file_create_url($file->uri), $options);
  242. case 'owner':
  243. return $file->uid;
  244. }
  245. }
  246. /**
  247. * Callback for getting term properties.
  248. *
  249. * @see entity_metadata_taxonomy_entity_info_alter()
  250. */
  251. function entity_metadata_taxonomy_term_get_properties($term, array $options, $name) {
  252. switch ($name) {
  253. case 'node_count':
  254. return count(taxonomy_select_nodes($term->tid));
  255. case 'description':
  256. return check_markup($term->description, isset($term->format) ? $term->format : NULL, '', TRUE);
  257. case 'parent':
  258. if (isset($term->parent[0]) && !is_array(isset($term->parent[0]))) {
  259. return $term->parent;
  260. }
  261. return array_keys(taxonomy_get_parents($term->tid));
  262. case 'parents_all':
  263. // We have to return an array of ids.
  264. $tids = array();
  265. foreach (taxonomy_get_parents_all($term->tid) as $parent) {
  266. $tids[] = $parent->tid;
  267. }
  268. return $tids;
  269. }
  270. }
  271. /**
  272. * Callback for setting term properties.
  273. *
  274. * @see entity_metadata_taxonomy_entity_info_alter()
  275. */
  276. function entity_metadata_taxonomy_term_setter($term, $name, $value) {
  277. switch ($name) {
  278. case 'vocabulary':
  279. // Make sure to update the taxonomy bundle key, so load the vocabulary.
  280. // Support both, loading by name or ID.
  281. $vocabulary = is_numeric($value) ? taxonomy_vocabulary_load($value) : taxonomy_vocabulary_machine_name_load($value);
  282. $term->vocabulary_machine_name = $vocabulary->machine_name;
  283. return $term->vid = $vocabulary->vid;
  284. case 'parent':
  285. return $term->parent = $value;
  286. }
  287. }
  288. /**
  289. * Callback for getting vocabulary properties.
  290. * @see entity_metadata_taxonomy_entity_info_alter()
  291. */
  292. function entity_metadata_taxonomy_vocabulary_get_properties($vocabulary, array $options, $name) {
  293. switch ($name) {
  294. case 'term_count':
  295. $sql = "SELECT COUNT (1) FROM {taxonomy_term_data} td WHERE td.vid = :vid";
  296. return db_query($sql, array(':vid' => $vocabulary->vid))->fetchField();
  297. }
  298. }
  299. /**
  300. * Callback for getting user properties.
  301. * @see entity_metadata_user_entity_info_alter()
  302. */
  303. function entity_metadata_user_get_properties($account, array $options, $name, $entity_type) {
  304. switch ($name) {
  305. case 'last_access':
  306. // In case there was no access the value is 0, but we have to return NULL.
  307. return empty($account->access) ? NULL : $account->access;
  308. case 'last_login':
  309. return empty($account->login) ? NULL : $account->login;
  310. case 'name':
  311. return empty($account->uid) ? variable_get('anonymous', t('Anonymous')) : $account->name;
  312. case 'url':
  313. if (empty($account->uid)) {
  314. return NULL;
  315. }
  316. $return = entity_uri('user', $account);
  317. return $return ? url($return['path'], $return['options'] + $options) : '';
  318. case 'edit_url':
  319. return empty($account->uid) ? NULL : url("user/$account->uid/edit", $options);
  320. case 'roles':
  321. return isset($account->roles) ? array_keys($account->roles) : array();
  322. case 'theme':
  323. return empty($account->theme) ? variable_get('theme_default', 'bartik') : $account->theme;
  324. }
  325. }
  326. /**
  327. * Callback for setting user properties.
  328. * @see entity_metadata_user_entity_info_alter()
  329. */
  330. function entity_metadata_user_set_properties($account, $name, $value) {
  331. switch ($name) {
  332. case 'roles':
  333. $account->roles = array_intersect_key(user_roles(), array_flip($value));
  334. break;
  335. }
  336. }
  337. /**
  338. * Options list callback returning all user roles.
  339. */
  340. function entity_metadata_user_roles($property_name = 'roles', $info = array(), $op = 'edit') {
  341. $roles = user_roles();
  342. if ($op == 'edit') {
  343. unset($roles[DRUPAL_AUTHENTICATED_RID], $roles[DRUPAL_ANONYMOUS_RID]);
  344. }
  345. return $roles;
  346. }
  347. /**
  348. * Return the options lists for user status property.
  349. */
  350. function entity_metadata_user_status_options_list() {
  351. return array(
  352. 0 => t('Blocked'),
  353. 1 => t('Active'),
  354. );
  355. }
  356. /**
  357. * Callback defining an options list for language properties.
  358. */
  359. function entity_metadata_language_list() {
  360. $list = array();
  361. $list[LANGUAGE_NONE] = t('Language neutral');
  362. foreach (language_list() as $language) {
  363. $list[$language->language] = t($language->name);
  364. }
  365. return $list;
  366. }
  367. /**
  368. * Callback for getting field property values.
  369. */
  370. function entity_metadata_field_property_get($entity, array $options, $name, $entity_type, $info) {
  371. $field = field_info_field($name);
  372. $columns = array_keys($field['columns']);
  373. $langcode = isset($options['language']) ? $options['language']->language : LANGUAGE_NONE;
  374. $langcode = entity_metadata_field_get_language($entity_type, $entity, $field, $langcode, TRUE);
  375. $values = array();
  376. if (isset($entity->{$name}[$langcode])) {
  377. foreach ($entity->{$name}[$langcode] as $delta => $data) {
  378. $values[$delta] = $data[$columns[0]];
  379. if ($info['type'] == 'boolean' || $info['type'] == 'list<boolean>') {
  380. // Ensure that we have a clean boolean data type.
  381. $values[$delta] = (boolean) $values[$delta];
  382. }
  383. }
  384. }
  385. // For an empty single-valued field, we have to return NULL.
  386. return $field['cardinality'] == 1 ? ($values ? reset($values) : NULL) : $values;
  387. }
  388. /**
  389. * Callback for setting field property values.
  390. */
  391. function entity_metadata_field_property_set($entity, $name, $value, $langcode, $entity_type, $info) {
  392. $field = field_info_field($name);
  393. $columns = array_keys($field['columns']);
  394. $langcode = entity_metadata_field_get_language($entity_type, $entity, $field, $langcode);
  395. $values = $field['cardinality'] == 1 ? array($value) : (array) $value;
  396. $items = array();
  397. foreach ($values as $delta => $value) {
  398. if (isset($value)) {
  399. $items[$delta][$columns[0]] = $value;
  400. if ($info['type'] == 'boolean' || $info['type'] == 'list<boolean>') {
  401. // Convert boolean values back to an integer for writing.
  402. $items[$delta][$columns[0]] = (integer) $items[$delta][$columns[0]] = $value;
  403. }
  404. }
  405. }
  406. $entity->{$name}[$langcode] = $items;
  407. // Empty the static field language cache, so the field system picks up any
  408. // possible new languages.
  409. drupal_static_reset('field_language');
  410. }
  411. /**
  412. * Callback returning the options list of a field.
  413. */
  414. function entity_metadata_field_options_list($name, $info) {
  415. $field_property_info = $info;
  416. if (is_numeric($name) && isset($info['parent'])) {
  417. // The options list is to be returned for a single item of a multiple field.
  418. $field_property_info = $info['parent']->info();
  419. $name = $field_property_info['name'];
  420. }
  421. if (($field = field_info_field($name)) && isset($field_property_info['parent'])) {
  422. // Retrieve the wrapped entity holding the field.
  423. $wrapper = $field_property_info['parent'];
  424. try {
  425. $entity = $wrapper->value();
  426. }
  427. catch (EntityMetadataWrapperException $e) {
  428. // No data available.
  429. $entity = NULL;
  430. }
  431. // Support translating labels via i18n field.
  432. if (module_exists('i18n_field') && ($translate = i18n_field_type_info($field['type'], 'translate_options'))) {
  433. return $translate($field);
  434. }
  435. else {
  436. $instance = $wrapper->getBundle() ? field_info_instance($wrapper->type(), $name, $wrapper->getBundle()) : NULL;
  437. return (array) module_invoke($field['module'], 'options_list', $field, $instance, $wrapper->type(), $entity);
  438. }
  439. }
  440. }
  441. /**
  442. * Callback to verbatim get the data structure of a field. Useful for fields
  443. * that add metadata for their own data structure.
  444. */
  445. function entity_metadata_field_verbatim_get($entity, array $options, $name, $entity_type, &$context) {
  446. // Set contextual info useful for getters of any child properties.
  447. $context['instance'] = field_info_instance($context['parent']->type(), $name, $context['parent']->getBundle());
  448. $context['field'] = field_info_field($name);
  449. $langcode = isset($options['language']) ? $options['language']->language : LANGUAGE_NONE;
  450. $langcode = entity_metadata_field_get_language($entity_type, $entity, $context['field'], $langcode, TRUE);
  451. if ($context['field']['cardinality'] == 1) {
  452. return isset($entity->{$name}[$langcode][0]) ? $entity->{$name}[$langcode][0] : NULL;
  453. }
  454. return isset($entity->{$name}[$langcode]) ? $entity->{$name}[$langcode] : array();
  455. }
  456. /**
  457. * Writes the passed field items in the object. Useful as field level setter
  458. * to set the whole data structure at once.
  459. */
  460. function entity_metadata_field_verbatim_set($entity, $name, $items, $langcode, $entity_type) {
  461. $field = field_info_field($name);
  462. $langcode = entity_metadata_field_get_language($entity_type, $entity, $field, $langcode);
  463. $value = $field['cardinality'] == 1 ? array($items) : (array) $items;
  464. // Filter out any items set to NULL.
  465. $entity->{$name}[$langcode] = array_filter($value);
  466. // Empty the static field language cache, so the field system picks up any
  467. // possible new languages.
  468. drupal_static_reset('field_language');
  469. }
  470. /**
  471. * Helper for determining the field language to be used.
  472. *
  473. * Note that we cannot use field_language() as we are not about to display
  474. * values, but generally read/write values.
  475. *
  476. * @param $fallback
  477. * (optional) Whether to fall back to the entity default language, if no
  478. * value is available for the given language code yet.
  479. *
  480. * @return
  481. * The language code to use.
  482. */
  483. function entity_metadata_field_get_language($entity_type, $entity, $field, $langcode = LANGUAGE_NONE, $fallback = FALSE) {
  484. // Try to figure out the default language used by the entity.
  485. // With Drupal >= 7.15 we can use entity_language().
  486. if (function_exists('entity_language')) {
  487. $default_langcode = entity_language($entity_type, $entity);
  488. }
  489. else {
  490. $default_langcode = !empty($entity->language) ? $entity->language : LANGUAGE_NONE;
  491. }
  492. // Determine the right language to use.
  493. if ($default_langcode != LANGUAGE_NONE && field_is_translatable($entity_type, $field)) {
  494. $langcode = ($langcode != LANGUAGE_NONE) ? field_valid_language($langcode, $default_langcode) : $default_langcode;
  495. if (!isset($entity->{$field['field_name']}[$langcode]) && $fallback) {
  496. $langcode = $default_langcode;
  497. }
  498. return $langcode;
  499. }
  500. else {
  501. return LANGUAGE_NONE;
  502. }
  503. }
  504. /**
  505. * Callback for getting the sanitized text of 'text_formatted' properties.
  506. * This callback is used for both the 'value' and the 'summary'.
  507. */
  508. function entity_metadata_field_text_get($item, array $options, $name, $type, $context) {
  509. // $name is either 'value' or 'summary'.
  510. if (!isset($item['safe_' . $name])) {
  511. // Apply input formats.
  512. $langcode = isset($options['language']) ? $options['language']->language : '';
  513. $format = isset($item['format']) ? $item['format'] : filter_default_format();
  514. $item['safe_' . $name] = check_markup($item[$name], $format, $langcode);
  515. // To speed up subsequent calls, update $item with the 'safe_value'.
  516. $context['parent']->set($item);
  517. }
  518. return $item['safe_' . $name];
  519. }
  520. /**
  521. * Defines the list of all available text formats.
  522. */
  523. function entity_metadata_field_text_formats() {
  524. foreach (filter_formats() as $key => $format) {
  525. $formats[$key] = $format->name;
  526. }
  527. return $formats;
  528. }
  529. /**
  530. * Callback for getting the file entity of file fields.
  531. */
  532. function entity_metadata_field_file_get($item) {
  533. return $item['fid'];
  534. }
  535. /**
  536. * Callback for setting the file entity of file fields.
  537. */
  538. function entity_metadata_field_file_set(&$item, $property_name, $value) {
  539. $item['fid'] = $value;
  540. }
  541. /**
  542. * Callback for auto-creating file field $items.
  543. */
  544. function entity_metadata_field_file_create_item($property_name, $context) {
  545. // 'fid' is required, so 'file' has to be set as initial property.
  546. return array('display' => isset($context['field']['settings']['display_default']) ? $context['field']['settings']['display_default'] : 0);
  547. }
  548. /**
  549. * Callback for validating file field $items.
  550. */
  551. function entity_metadata_field_file_validate_item($items, $context) {
  552. // Allow NULL values.
  553. if (!isset($items)) {
  554. return TRUE;
  555. }
  556. // Stream-line $items for multiple vs non-multiple fields.
  557. $items = !entity_property_list_extract_type($context['type']) ? array($items) : (array) $items;
  558. foreach ($items as $item) {
  559. // File-field items require a valid file.
  560. if (!isset($item['fid']) || !file_load($item['fid'])) {
  561. return FALSE;
  562. }
  563. if (isset($context['property info']['display']) && !isset($item['display'])) {
  564. return FALSE;
  565. }
  566. }
  567. return TRUE;
  568. }
  569. /**
  570. * Access callback for the node entity.
  571. *
  572. * This function does not implement hook_node_access(), thus it may not be
  573. * called entity_metadata_node_access().
  574. *
  575. * @see entity_access()
  576. *
  577. * @param $op
  578. * The operation being performed. One of 'view', 'update', 'create' or
  579. * 'delete'.
  580. * @param $node
  581. * A node to check access for. Must be a node object. Must have nid,
  582. * except in the case of 'create' operations.
  583. * @param $account
  584. * The user to check for. Leave it to NULL to check for the global user.
  585. *
  586. * @throws EntityMalformedException
  587. *
  588. * @return boolean
  589. * TRUE if access is allowed, FALSE otherwise.
  590. */
  591. function entity_metadata_no_hook_node_access($op, $node = NULL, $account = NULL) {
  592. // First deal with the case where a $node is provided.
  593. if (isset($node)) {
  594. if (empty($node->vid) && in_array($op, array('create', 'update'))) {
  595. // This is a new node or the original node.
  596. if (isset($node->type)) {
  597. $op = empty($node->nid) || !empty($node->is_new) ? 'create' : 'update';
  598. return node_access($op, $op == 'create' ? $node->type : $node, $account);
  599. }
  600. else {
  601. throw new EntityMalformedException('Permission to create a node was requested but no node type was given.');
  602. }
  603. }
  604. // If a non-default revision is given, incorporate revision access.
  605. $default_revision = node_load($node->nid);
  606. if ($node->vid !== $default_revision->vid) {
  607. return _node_revision_access($node, $op, $account);
  608. }
  609. else {
  610. return node_access($op, $node, $account);
  611. }
  612. }
  613. // No node is provided. Check for access to all nodes.
  614. if (user_access('bypass node access', $account)) {
  615. return TRUE;
  616. }
  617. if (!user_access('access content', $account)) {
  618. return FALSE;
  619. }
  620. if ($op == 'view' && node_access_view_all_nodes($account)) {
  621. return TRUE;
  622. }
  623. return FALSE;
  624. }
  625. /**
  626. * Access callback for the user entity.
  627. */
  628. function entity_metadata_user_access($op, $entity = NULL, $account = NULL, $entity_type = NULL) {
  629. $account = isset($account) ? $account : $GLOBALS['user'];
  630. // Grant access to the users own user account and to the anonymous one.
  631. if (isset($entity->uid) && $op != 'delete' && (($entity->uid == $account->uid && $entity->uid) || (!$entity->uid && $op == 'view'))) {
  632. return TRUE;
  633. }
  634. if (user_access('administer users', $account)
  635. || user_access('access user profiles', $account) && $op == 'view' && (empty($entity) || !empty($entity->status))) {
  636. return TRUE;
  637. }
  638. return FALSE;
  639. }
  640. /**
  641. * Access callback for restricted user properties.
  642. */
  643. function entity_metadata_user_properties_access($op, $property, $entity = NULL, $account = NULL) {
  644. if (user_access('administer users', $account)) {
  645. return TRUE;
  646. }
  647. $account = isset($account) ? $account : $GLOBALS['user'];
  648. // Flag to indicate if this user entity is the own user account.
  649. $is_own_account = isset($entity->uid) && $account->uid == $entity->uid;
  650. switch ($property) {
  651. case 'name':
  652. // Allow view access to anyone with access to the entity.
  653. if ($op == 'view') {
  654. return TRUE;
  655. }
  656. // Allow edit access for own user name if the permission is satisfied.
  657. return $is_own_account && user_access('change own username', $account);
  658. case 'mail':
  659. // Allow access to own mail address.
  660. return $is_own_account;
  661. case 'roles':
  662. // Allow view access for own roles.
  663. return ($op == 'view' && $is_own_account);
  664. }
  665. return FALSE;
  666. }
  667. /**
  668. * Access callback for the comment entity.
  669. */
  670. function entity_metadata_comment_access($op, $entity = NULL, $account = NULL) {
  671. // When determining access to a comment, 'comment_access' does not take any
  672. // access restrictions to the comment's associated node into account. If a
  673. // comment has an associated node, the user must be able to view it in order
  674. // to access the comment.
  675. if (isset($entity->nid)) {
  676. if (!entity_access('view', 'node', node_load($entity->nid), $account)) {
  677. return FALSE;
  678. }
  679. }
  680. // Comment administrators are allowed to perform all operations on all
  681. // comments.
  682. if (user_access('administer comments', $account)) {
  683. return TRUE;
  684. }
  685. // Unpublished comments can never be accessed by non-admins.
  686. if (isset($entity->status) && $entity->status == COMMENT_NOT_PUBLISHED) {
  687. return FALSE;
  688. }
  689. if (isset($entity) && $op == 'update') {
  690. // Because 'comment_access' only checks the current user, we need to do our
  691. // own access checking if an account was specified.
  692. if (!isset($account)) {
  693. return comment_access('edit', $entity);
  694. }
  695. else {
  696. return $account->uid && $account->uid == $entity->uid && user_access('edit own comments', $account);
  697. }
  698. }
  699. if (user_access('access comments', $account) && $op == 'view') {
  700. return TRUE;
  701. }
  702. return FALSE;
  703. }
  704. /**
  705. * Access callback for restricted comment properties.
  706. */
  707. function entity_metadata_comment_properties_access($op, $property, $entity = NULL, $account = NULL) {
  708. return user_access('administer comments', $account);
  709. }
  710. /**
  711. * Access callback for the taxonomy entities.
  712. */
  713. function entity_metadata_taxonomy_access($op, $entity = NULL, $account = NULL, $entity_type = NULL) {
  714. // If user has administer taxonomy permission then no further checks.
  715. if (user_access('administer taxonomy', $account)) {
  716. return TRUE;
  717. }
  718. switch ($op) {
  719. case "view":
  720. if (user_access('access content', $account)) {
  721. return TRUE;
  722. }
  723. break;
  724. case "update":
  725. if ($entity_type == 'taxonomy_term') {
  726. return user_access("edit terms in $entity->vid", $account);
  727. }
  728. break;
  729. case "create":
  730. if ($entity_type == 'taxonomy_term') {
  731. // Check for taxonomy_access_fix contrib module which adds additional
  732. // permissions to create new terms in a given vocabulary.
  733. if (function_exists('taxonomy_access_fix_access')) {
  734. return taxonomy_access_fix_access('add terms', $entity->vocabulary_machine_name);
  735. }
  736. }
  737. break;
  738. case "delete":
  739. if ($entity_type == 'taxonomy_term') {
  740. return user_access("delete terms in $entity->vid", $account);
  741. }
  742. break;
  743. }
  744. return FALSE;
  745. }
  746. /**
  747. * Access callback for file entities.
  748. */
  749. function entity_metadata_file_access($op, $file = NULL, $account = NULL, $entity_type) {
  750. // We can only check access for the current user, so return FALSE on other accounts.
  751. global $user;
  752. if ($op == 'view' && isset($file) && (!isset($account) || $user->uid == $account->uid)) {
  753. // Invoke hook_file_download() to obtain access information.
  754. foreach (module_implements('file_download') as $module) {
  755. $result = module_invoke($module, 'file_download', $file->uri);
  756. if ($result == -1) {
  757. return FALSE;
  758. }
  759. }
  760. return TRUE;
  761. }
  762. return FALSE;
  763. }
  764. /**
  765. * Callback to determine access for properties which are fields.
  766. */
  767. function entity_metadata_field_access_callback($op, $name, $entity = NULL, $account = NULL, $entity_type) {
  768. $field = field_info_field($name);
  769. return field_access($op, $field, $entity_type, $entity, $account);
  770. }
  771. /**
  772. * Callback to create entity objects.
  773. */
  774. function entity_metadata_create_object($values = array(), $entity_type) {
  775. $info = entity_get_info($entity_type);
  776. // Make sure at least the bundle and label properties are set.
  777. if (isset($info['entity keys']['bundle']) && $key = $info['entity keys']['bundle']) {
  778. $values += array($key => NULL);
  779. }
  780. if (isset($info['entity keys']['label']) && $key = $info['entity keys']['label']) {
  781. $values += array($key => NULL);
  782. }
  783. $entity = (object) $values;
  784. $entity->is_new = TRUE;
  785. return $entity;
  786. }
  787. /**
  788. * Callback to create a new comment.
  789. */
  790. function entity_metadata_create_comment($values = array()) {
  791. $comment = (object) ($values + array(
  792. 'status' => COMMENT_PUBLISHED,
  793. 'pid' => 0,
  794. 'subject' => '',
  795. 'uid' => 0,
  796. 'language' => LANGUAGE_NONE,
  797. 'node_type' => NULL,
  798. 'is_new' => TRUE,
  799. ));
  800. $comment->cid = FALSE;
  801. return $comment;
  802. }
  803. /**
  804. * Callback to create a new node.
  805. */
  806. function entity_metadata_create_node($values = array()) {
  807. $node = (object) array(
  808. 'type' => $values['type'],
  809. 'language' => LANGUAGE_NONE,
  810. 'is_new' => TRUE,
  811. );
  812. // Set some defaults.
  813. $node_options = variable_get('node_options_' . $node->type, array('status', 'promote'));
  814. foreach (array('status', 'promote', 'sticky') as $key) {
  815. $node->$key = (int) in_array($key, $node_options);
  816. }
  817. if (module_exists('comment') && !isset($node->comment)) {
  818. $node->comment = variable_get("comment_$node->type", COMMENT_NODE_OPEN);
  819. }
  820. // Apply the given values.
  821. foreach ($values as $key => $value) {
  822. $node->$key = $value;
  823. }
  824. return $node;
  825. }
  826. /**
  827. * Callback to save a user account.
  828. */
  829. function entity_metadata_user_save($account) {
  830. $edit = (array) $account;
  831. // Don't save the hashed password as password.
  832. unset($edit['pass']);
  833. user_save($account, $edit);
  834. }
  835. /**
  836. * Callback to delete a file.
  837. * Watch out to not accidentilly implement hook_file_delete().
  838. */
  839. function entity_metadata_delete_file($fid) {
  840. file_delete(file_load($fid), TRUE);
  841. }
  842. /**
  843. * Callback to view nodes.
  844. */
  845. function entity_metadata_view_node($entities, $view_mode = 'full', $langcode = NULL) {
  846. $result = node_view_multiple($entities, $view_mode, 0, $langcode);
  847. // Make sure to key the result with 'node' instead of 'nodes'.
  848. return array('node' => reset($result));
  849. }
  850. /**
  851. * Callback to view comments.
  852. */
  853. function entity_metadata_view_comment($entities, $view_mode = 'full', $langcode = NULL) {
  854. $build = array();
  855. $nodes = array();
  856. // The comments, indexed by nid and then by cid.
  857. $nid_comments = array();
  858. foreach ($entities as $cid => $comment) {
  859. $nid = $comment->nid;
  860. $nodes[$nid] = $nid;
  861. $nid_comments[$nid][$cid] = $comment;
  862. }
  863. $nodes = node_load_multiple(array_keys($nodes));
  864. foreach ($nid_comments as $nid => $comments) {
  865. $node = isset($nodes[$nid]) ? $nodes[$nid] : NULL;
  866. $build += comment_view_multiple($comments, $node, $view_mode, 0, $langcode);
  867. }
  868. return array('comment' => $build);
  869. }
  870. /**
  871. * Callback to view an entity, for which just ENTITYTYPE_view() is available.
  872. */
  873. function entity_metadata_view_single($entities, $view_mode = 'full', $langcode = NULL, $entity_type) {
  874. $function = $entity_type . '_view';
  875. $build = array();
  876. foreach ($entities as $key => $entity) {
  877. $build[$entity_type][$key] = $function($entity, $view_mode, $langcode);
  878. }
  879. return $build;
  880. }
  881. /**
  882. * Callback to get the form of a node.
  883. */
  884. function entity_metadata_form_node($node) {
  885. // Pre-populate the form-state with the right form include.
  886. $form_state['build_info']['args'] = array($node);
  887. form_load_include($form_state, 'inc', 'node', 'node.pages');
  888. return drupal_build_form($node->type . '_node_form', $form_state);
  889. }
  890. /**
  891. * Callback to get the form of a comment.
  892. */
  893. function entity_metadata_form_comment($comment) {
  894. if (!isset($comment->node_type)) {
  895. $node = node_load($comment->nid);
  896. $comment->node_type = 'comment_node_' . $node->type;
  897. }
  898. return drupal_get_form($comment->node_type . '_form', $comment);
  899. }
  900. /**
  901. * Callback to get the form of a user account.
  902. */
  903. function entity_metadata_form_user($account) {
  904. // If $account->uid is set then we want a user edit form.
  905. // Otherwise we want the user register form.
  906. if (isset($account->uid)) {
  907. $form_id = 'user_profile_form';
  908. form_load_include($form_state, 'inc', 'user', 'user.pages');
  909. }
  910. else {
  911. $form_id = 'user_register_form';
  912. }
  913. $form_state['build_info']['args'] = array($account);
  914. return drupal_build_form($form_id, $form_state);
  915. }
  916. /**
  917. * Callback to get the form of a term.
  918. */
  919. function entity_metadata_form_taxonomy_term($term) {
  920. // Pre-populate the form-state with the right form include.
  921. $form_state['build_info']['args'] = array($term);
  922. form_load_include($form_state, 'inc', 'taxonomy', 'taxonomy.admin');
  923. return drupal_build_form('taxonomy_form_term', $form_state);
  924. }
  925. /**
  926. * Callback to get the form of a vocabulary.
  927. */
  928. function entity_metadata_form_taxonomy_vocabulary($vocab) {
  929. // Pre-populate the form-state with the right form include.
  930. $form_state['build_info']['args'] = array($vocab);
  931. form_load_include($form_state, 'inc', 'taxonomy', 'taxonomy.admin');
  932. return drupal_build_form('taxonomy_form_vocabulary', $form_state);
  933. }
  934. /**
  935. * Callback to get the form for entities using the entity API admin ui.
  936. */
  937. function entity_metadata_form_entity_ui($entity, $entity_type) {
  938. $info = entity_get_info($entity_type);
  939. $form_state = form_state_defaults();
  940. // Add in the include file as the form API does else with the include file
  941. // specified for the active menu item.
  942. if (!empty($info['admin ui']['file'])) {
  943. $path = isset($info['admin ui']['file path']) ? $info['admin ui']['file path'] : drupal_get_path('module', $info['module']);
  944. $form_state['build_info']['files']['entity_ui'] = $path . '/' . $info['admin ui']['file'];
  945. // Also load the include file.
  946. if (file_exists($form_state['build_info']['files']['entity_ui'])) {
  947. require_once DRUPAL_ROOT . '/' . $form_state['build_info']['files']['entity_ui'];
  948. }
  949. }
  950. return entity_ui_get_form($entity_type, $entity, $op = 'edit', $form_state);
  951. }
  952. /**
  953. * Callback for querying entity properties having their values stored in the
  954. * entities main db table.
  955. */
  956. function entity_metadata_table_query($entity_type, $property, $value, $limit) {
  957. $properties = entity_get_all_property_info($entity_type);
  958. $info = $properties[$property] + array('schema field' => $property);
  959. $query = new EntityFieldQuery();
  960. $query->entityCondition('entity_type', $entity_type, '=')
  961. ->propertyCondition($info['schema field'], $value, is_array($value) ? 'IN' : '=')
  962. ->range(0, $limit);
  963. $result = $query->execute();
  964. return !empty($result[$entity_type]) ? array_keys($result[$entity_type]) : array();
  965. }
  966. /**
  967. * Callback for querying entities by field values. This function just queries
  968. * for the value of the first specified column. Also it is only suitable for
  969. * fields that don't process the data, so it's stored the same way as returned.
  970. */
  971. function entity_metadata_field_query($entity_type, $property, $value, $limit) {
  972. $query = new EntityFieldQuery();
  973. $field = field_info_field($property);
  974. $columns = array_keys($field['columns']);
  975. $query->entityCondition('entity_type', $entity_type, '=')
  976. ->fieldCondition($field, $columns[0], $value, is_array($value) ? 'IN' : '=')
  977. ->range(0, $limit);
  978. $result = $query->execute();
  979. return !empty($result[$entity_type]) ? array_keys($result[$entity_type]) : array();
  980. }
  981. /**
  982. * Implements entity_uri() callback for file entities.
  983. */
  984. function entity_metadata_uri_file($file) {
  985. return array(
  986. 'path' => file_create_url($file->uri),
  987. );
  988. }