callbacks.inc 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076
  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'])) {
  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 ($op == 'create') {
  595. if (isset($node->type)) {
  596. return node_access($op, $node->type, $account);
  597. }
  598. else {
  599. throw new EntityMalformedException('Permission to create a node was requested but no node type was given.');
  600. }
  601. }
  602. // If a non-default revision is given, incorporate revision access.
  603. $default_revision = node_load($node->nid);
  604. if ($node->vid !== $default_revision->vid) {
  605. return _node_revision_access($node, $op, $account);
  606. }
  607. else {
  608. return node_access($op, $node, $account);
  609. }
  610. }
  611. // No node is provided. Check for access to all nodes.
  612. if (user_access('bypass node access', $account)) {
  613. return TRUE;
  614. }
  615. if (!user_access('access content', $account)) {
  616. return FALSE;
  617. }
  618. if ($op == 'view' && node_access_view_all_nodes($account)) {
  619. return TRUE;
  620. }
  621. return FALSE;
  622. }
  623. /**
  624. * Access callback for the user entity.
  625. */
  626. function entity_metadata_user_access($op, $entity = NULL, $account = NULL, $entity_type = NULL) {
  627. $account = isset($account) ? $account : $GLOBALS['user'];
  628. // Grant access to the users own user account and to the anonymous one.
  629. if (isset($entity->uid) && $op != 'delete' && (($entity->uid == $account->uid && $entity->uid) || (!$entity->uid && $op == 'view'))) {
  630. return TRUE;
  631. }
  632. if (user_access('administer users', $account)
  633. || user_access('access user profiles', $account) && $op == 'view' && (empty($entity) || !empty($entity->status))) {
  634. return TRUE;
  635. }
  636. return FALSE;
  637. }
  638. /**
  639. * Access callback for restricted user properties.
  640. */
  641. function entity_metadata_user_properties_access($op, $property, $entity = NULL, $account = NULL) {
  642. if (user_access('administer users', $account)) {
  643. return TRUE;
  644. }
  645. $account = isset($account) ? $account : $GLOBALS['user'];
  646. // Flag to indicate if this user entity is the own user account.
  647. $is_own_account = isset($entity->uid) && $account->uid == $entity->uid;
  648. switch ($property) {
  649. case 'name':
  650. // Allow view access to anyone with access to the entity.
  651. if ($op == 'view') {
  652. return TRUE;
  653. }
  654. // Allow edit access for own user name if the permission is satisfied.
  655. return $is_own_account && user_access('change own username', $account);
  656. case 'mail':
  657. // Allow access to own mail address.
  658. return $is_own_account;
  659. case 'roles':
  660. // Allow view access for own roles.
  661. return ($op == 'view' && $is_own_account);
  662. }
  663. return FALSE;
  664. }
  665. /**
  666. * Access callback for the comment entity.
  667. */
  668. function entity_metadata_comment_access($op, $entity = NULL, $account = NULL) {
  669. // When determining access to a comment, 'comment_access' does not take any
  670. // access restrictions to the comment's associated node into account. If a
  671. // comment has an associated node, the user must be able to view it in order
  672. // to access the comment.
  673. if (isset($entity->nid)) {
  674. if (!entity_access('view', 'node', node_load($entity->nid), $account)) {
  675. return FALSE;
  676. }
  677. }
  678. // Comment administrators are allowed to perform all operations on all
  679. // comments.
  680. if (user_access('administer comments', $account)) {
  681. return TRUE;
  682. }
  683. // Unpublished comments can never be accessed by non-admins.
  684. if (isset($entity->status) && $entity->status == COMMENT_NOT_PUBLISHED) {
  685. return FALSE;
  686. }
  687. if (isset($entity) && $op == 'update') {
  688. // Because 'comment_access' only checks the current user, we need to do our
  689. // own access checking if an account was specified.
  690. if (!isset($account)) {
  691. return comment_access('edit', $entity);
  692. }
  693. else {
  694. return $account->uid && $account->uid == $entity->uid && user_access('edit own comments', $account);
  695. }
  696. }
  697. if (user_access('access comments', $account) && $op == 'view') {
  698. return TRUE;
  699. }
  700. return FALSE;
  701. }
  702. /**
  703. * Access callback for restricted comment properties.
  704. */
  705. function entity_metadata_comment_properties_access($op, $property, $entity = NULL, $account = NULL) {
  706. return user_access('administer comments', $account);
  707. }
  708. /**
  709. * Access callback for the taxonomy entities.
  710. */
  711. function entity_metadata_taxonomy_access($op, $entity = NULL, $account = NULL, $entity_type = NULL) {
  712. if ($entity_type == 'taxonomy_vocabulary') {
  713. return user_access('administer taxonomy', $account);
  714. }
  715. if (isset($entity) && $op == 'update' && !isset($account) && taxonomy_term_edit_access($entity)) {
  716. return TRUE;
  717. }
  718. if (user_access('administer taxonomy', $account) || user_access('access content', $account) && $op == 'view') {
  719. return TRUE;
  720. }
  721. return FALSE;
  722. }
  723. /**
  724. * Access callback for file entities.
  725. */
  726. function entity_metadata_file_access($op, $file = NULL, $account = NULL, $entity_type) {
  727. // We can only check access for the current user, so return FALSE on other accounts.
  728. global $user;
  729. if ($op == 'view' && isset($file) && (!isset($account) || $user->uid == $account->uid)) {
  730. // Invoke hook_file_download() to obtain access information.
  731. foreach (module_implements('file_download') as $module) {
  732. $result = module_invoke($module, 'file_download', $file->uri);
  733. if ($result == -1) {
  734. return FALSE;
  735. }
  736. }
  737. return TRUE;
  738. }
  739. return FALSE;
  740. }
  741. /**
  742. * Callback to determine access for properties which are fields.
  743. */
  744. function entity_metadata_field_access_callback($op, $name, $entity = NULL, $account = NULL, $entity_type) {
  745. $field = field_info_field($name);
  746. return field_access($op, $field, $entity_type, $entity, $account);
  747. }
  748. /**
  749. * Callback to create entity objects.
  750. */
  751. function entity_metadata_create_object($values = array(), $entity_type) {
  752. $info = entity_get_info($entity_type);
  753. // Make sure at least the bundle and label properties are set.
  754. if (isset($info['entity keys']['bundle']) && $key = $info['entity keys']['bundle']) {
  755. $values += array($key => NULL);
  756. }
  757. if (isset($info['entity keys']['label']) && $key = $info['entity keys']['label']) {
  758. $values += array($key => NULL);
  759. }
  760. $entity = (object) $values;
  761. $entity->is_new = TRUE;
  762. return $entity;
  763. }
  764. /**
  765. * Callback to create a new comment.
  766. */
  767. function entity_metadata_create_comment($values = array()) {
  768. $comment = (object) ($values + array(
  769. 'status' => COMMENT_PUBLISHED,
  770. 'pid' => 0,
  771. 'subject' => '',
  772. 'uid' => 0,
  773. 'language' => LANGUAGE_NONE,
  774. 'node_type' => NULL,
  775. 'is_new' => TRUE,
  776. ));
  777. $comment->cid = FALSE;
  778. return $comment;
  779. }
  780. /**
  781. * Callback to create a new node.
  782. */
  783. function entity_metadata_create_node($values = array()) {
  784. $node = (object) array(
  785. 'type' => $values['type'],
  786. 'language' => LANGUAGE_NONE,
  787. 'is_new' => TRUE,
  788. );
  789. // Set some defaults.
  790. $node_options = variable_get('node_options_' . $node->type, array('status', 'promote'));
  791. foreach (array('status', 'promote', 'sticky') as $key) {
  792. $node->$key = (int) in_array($key, $node_options);
  793. }
  794. if (module_exists('comment') && !isset($node->comment)) {
  795. $node->comment = variable_get("comment_$node->type", COMMENT_NODE_OPEN);
  796. }
  797. // Apply the given values.
  798. foreach ($values as $key => $value) {
  799. $node->$key = $value;
  800. }
  801. return $node;
  802. }
  803. /**
  804. * Callback to save a user account.
  805. */
  806. function entity_metadata_user_save($account) {
  807. $edit = (array) $account;
  808. // Don't save the hashed password as password.
  809. unset($edit['pass']);
  810. user_save($account, $edit);
  811. }
  812. /**
  813. * Callback to delete a file.
  814. * Watch out to not accidentilly implement hook_file_delete().
  815. */
  816. function entity_metadata_delete_file($fid) {
  817. file_delete(file_load($fid), TRUE);
  818. }
  819. /**
  820. * Callback to view nodes.
  821. */
  822. function entity_metadata_view_node($entities, $view_mode = 'full', $langcode = NULL) {
  823. $result = node_view_multiple($entities, $view_mode, 0, $langcode);
  824. // Make sure to key the result with 'node' instead of 'nodes'.
  825. return array('node' => reset($result));
  826. }
  827. /**
  828. * Callback to view comments.
  829. */
  830. function entity_metadata_view_comment($entities, $view_mode = 'full', $langcode = NULL) {
  831. $build = array();
  832. $nodes = array();
  833. // The comments, indexed by nid and then by cid.
  834. $nid_comments = array();
  835. foreach ($entities as $cid => $comment) {
  836. $nid = $comment->nid;
  837. $nodes[$nid] = $nid;
  838. $nid_comments[$nid][$cid] = $comment;
  839. }
  840. $nodes = node_load_multiple(array_keys($nodes));
  841. foreach ($nid_comments as $nid => $comments) {
  842. $node = isset($nodes[$nid]) ? $nodes[$nid] : NULL;
  843. $build += comment_view_multiple($comments, $node, $view_mode, 0, $langcode);
  844. }
  845. return array('comment' => $build);
  846. }
  847. /**
  848. * Callback to view an entity, for which just ENTITYTYPE_view() is available.
  849. */
  850. function entity_metadata_view_single($entities, $view_mode = 'full', $langcode = NULL, $entity_type) {
  851. $function = $entity_type . '_view';
  852. $build = array();
  853. foreach ($entities as $key => $entity) {
  854. $build[$entity_type][$key] = $function($entity, $view_mode, $langcode);
  855. }
  856. return $build;
  857. }
  858. /**
  859. * Callback to get the form of a node.
  860. */
  861. function entity_metadata_form_node($node) {
  862. // Pre-populate the form-state with the right form include.
  863. $form_state['build_info']['args'] = array($node);
  864. form_load_include($form_state, 'inc', 'node', 'node.pages');
  865. return drupal_build_form($node->type . '_node_form', $form_state);
  866. }
  867. /**
  868. * Callback to get the form of a comment.
  869. */
  870. function entity_metadata_form_comment($comment) {
  871. if (!isset($comment->node_type)) {
  872. $node = node_load($comment->nid);
  873. $comment->node_type = 'comment_node_' . $node->type;
  874. }
  875. return drupal_get_form($comment->node_type . '_form', $comment);
  876. }
  877. /**
  878. * Callback to get the form of a user account.
  879. */
  880. function entity_metadata_form_user($account) {
  881. // If $account->uid is set then we want a user edit form.
  882. // Otherwise we want the user register form.
  883. if (isset($account->uid)) {
  884. $form_id = 'user_profile_form';
  885. form_load_include($form_state, 'inc', 'user', 'user.pages');
  886. }
  887. else {
  888. $form_id = 'user_register_form';
  889. }
  890. $form_state['build_info']['args'] = array($account);
  891. return drupal_build_form($form_id, $form_state);
  892. }
  893. /**
  894. * Callback to get the form of a term.
  895. */
  896. function entity_metadata_form_taxonomy_term($term) {
  897. // Pre-populate the form-state with the right form include.
  898. $form_state['build_info']['args'] = array($term);
  899. form_load_include($form_state, 'inc', 'taxonomy', 'taxonomy.admin');
  900. return drupal_build_form('taxonomy_form_term', $form_state);
  901. }
  902. /**
  903. * Callback to get the form of a vocabulary.
  904. */
  905. function entity_metadata_form_taxonomy_vocabulary($vocab) {
  906. // Pre-populate the form-state with the right form include.
  907. $form_state['build_info']['args'] = array($vocab);
  908. form_load_include($form_state, 'inc', 'taxonomy', 'taxonomy.admin');
  909. return drupal_build_form('taxonomy_form_vocabulary', $form_state);
  910. }
  911. /**
  912. * Callback to get the form for entities using the entity API admin ui.
  913. */
  914. function entity_metadata_form_entity_ui($entity, $entity_type) {
  915. $info = entity_get_info($entity_type);
  916. $form_state = form_state_defaults();
  917. // Add in the include file as the form API does else with the include file
  918. // specified for the active menu item.
  919. if (!empty($info['admin ui']['file'])) {
  920. $path = isset($info['admin ui']['file path']) ? $info['admin ui']['file path'] : drupal_get_path('module', $info['module']);
  921. $form_state['build_info']['files']['entity_ui'] = $path . '/' . $info['admin ui']['file'];
  922. // Also load the include file.
  923. if (file_exists($form_state['build_info']['files']['entity_ui'])) {
  924. require_once DRUPAL_ROOT . '/' . $form_state['build_info']['files']['entity_ui'];
  925. }
  926. }
  927. return entity_ui_get_form($entity_type, $entity, $op = 'edit', $form_state);
  928. }
  929. /**
  930. * Callback for querying entity properties having their values stored in the
  931. * entities main db table.
  932. */
  933. function entity_metadata_table_query($entity_type, $property, $value, $limit) {
  934. $properties = entity_get_all_property_info($entity_type);
  935. $info = $properties[$property] + array('schema field' => $property);
  936. $query = new EntityFieldQuery();
  937. $query->entityCondition('entity_type', $entity_type, '=')
  938. ->propertyCondition($info['schema field'], $value, is_array($value) ? 'IN' : '=')
  939. ->range(0, $limit);
  940. $result = $query->execute();
  941. return !empty($result[$entity_type]) ? array_keys($result[$entity_type]) : array();
  942. }
  943. /**
  944. * Callback for querying entities by field values. This function just queries
  945. * for the value of the first specified column. Also it is only suitable for
  946. * fields that don't process the data, so it's stored the same way as returned.
  947. */
  948. function entity_metadata_field_query($entity_type, $property, $value, $limit) {
  949. $query = new EntityFieldQuery();
  950. $field = field_info_field($property);
  951. $columns = array_keys($field['columns']);
  952. $query->entityCondition('entity_type', $entity_type, '=')
  953. ->fieldCondition($field, $columns[0], $value, is_array($value) ? 'IN' : '=')
  954. ->range(0, $limit);
  955. $result = $query->execute();
  956. return !empty($result[$entity_type]) ? array_keys($result[$entity_type]) : array();
  957. }
  958. /**
  959. * Implements entity_uri() callback for file entities.
  960. */
  961. function entity_metadata_uri_file($file) {
  962. return array(
  963. 'path' => file_create_url($file->uri),
  964. );
  965. }