uuid_services.module 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. <?php
  2. /**
  3. * @file
  4. * UUID Services module functions.
  5. */
  6. /**
  7. * Implements hook_menu().
  8. */
  9. function uuid_services_menu() {
  10. $items['admin/config/services/uuid-services'] = array(
  11. 'title' => 'UUID Services',
  12. 'description' => 'Configure settings for UUID Services.',
  13. 'access arguments' => array('administer services'),
  14. 'page callback' => 'drupal_get_form',
  15. 'page arguments' => array('uuid_services_settings'),
  16. 'file' => 'uuid_services.admin.inc',
  17. );
  18. return $items;
  19. }
  20. /**
  21. * Implements hook_services_resources_alter().
  22. *
  23. * Alter all resources that support UUIDs, to make use this functionality when
  24. * exposing them through Services.
  25. *
  26. * Since we are working with UUID enabled entities, the 'create' method is
  27. * redundant. Instead, clients should do a PUT to '<entity_type>/<uuid>'. This
  28. * will route through the 'update' method and create the entity if it doesn't
  29. * exist. This is the most logical thing to do, since it's up to the client to
  30. * generate and set the UUID on the entity.
  31. */
  32. function uuid_services_services_resources_alter(&$resources, &$endpoint) {
  33. foreach (entity_get_info() as $entity_type => $entity_info) {
  34. if (isset($entity_info['uuid']) && $entity_info['uuid'] == TRUE && (isset($resources[$entity_type]) || variable_get('uuid_services_support_all_entity_types', FALSE))) {
  35. unset($resources[$entity_type]['operations']['create']);
  36. // Alter 'retrieve' method to use UUID enabled functions and arguments.
  37. $resources[$entity_type]['operations']['retrieve']['help'] = t('Retrieve %label entities based on UUID.', array('%label' => $entity_info['label']));
  38. $resources[$entity_type]['operations']['retrieve']['callback'] = '_uuid_services_entity_retrieve';
  39. $resources[$entity_type]['operations']['retrieve']['access callback'] = '_uuid_services_entity_access';
  40. $resources[$entity_type]['operations']['retrieve']['access arguments'] = array('view');
  41. $resources[$entity_type]['operations']['retrieve']['access arguments append'] = TRUE;
  42. $resources[$entity_type]['operations']['retrieve']['args'] = array(
  43. // This argument isn't exposed in the service, only used internally..
  44. array(
  45. 'name' => 'entity_type',
  46. 'description' => t('The entity type.'),
  47. 'type' => 'string',
  48. 'default value' => $entity_type,
  49. 'optional' => TRUE,
  50. ),
  51. array(
  52. 'name' => 'uuid',
  53. 'description' => t('The %label UUID.', array('%label' => $entity_info['label'])),
  54. 'type' => 'text',
  55. 'source' => array('path' => 0),
  56. ),
  57. );
  58. // Alter 'update' method to use UUID enabled functions and arguments.
  59. $resources[$entity_type]['operations']['update']['help'] = t('Update or create %label entities based on UUID. The payload must be formatted according to the <a href="!url">OData protocol</a>.', array('%label' => $entity_info['label'], '!url' => 'http://www.odata.org/developers/protocols'));
  60. $resources[$entity_type]['operations']['update']['callback'] = '_uuid_services_entity_update';
  61. $resources[$entity_type]['operations']['update']['access callback'] = '_uuid_services_entity_access';
  62. $resources[$entity_type]['operations']['update']['access arguments'] = array('update');
  63. $resources[$entity_type]['operations']['update']['access arguments append'] = TRUE;
  64. $resources[$entity_type]['operations']['update']['args'] = array(
  65. // This argument isn't exposed in the service, only used internally..
  66. array(
  67. 'name' => 'entity_type',
  68. 'description' => t('The entity type.'),
  69. 'type' => 'string',
  70. 'default value' => $entity_type,
  71. 'optional' => TRUE,
  72. ),
  73. array(
  74. 'name' => 'uuid',
  75. 'description' => t('The %label UUID.', array('%label' => $entity_info['label'])),
  76. 'type' => 'text',
  77. 'source' => array('path' => 0),
  78. ),
  79. array(
  80. 'name' => 'entity',
  81. 'description' => t('The %label entity object.', array('%label' => $entity_info['label'])),
  82. 'type' => 'struct',
  83. 'source' => 'data',
  84. ),
  85. );
  86. // Alter 'delete' method to use UUID enabled functions and arguments.
  87. $resources[$entity_type]['operations']['delete']['help'] = t('Delete %label entities based on UUID.', array('%label' => $entity_info['label']));
  88. $resources[$entity_type]['operations']['delete']['callback'] = '_uuid_services_entity_delete';
  89. $resources[$entity_type]['operations']['delete']['access callback'] = '_uuid_services_entity_access';
  90. $resources[$entity_type]['operations']['delete']['access arguments'] = array('delete');
  91. $resources[$entity_type]['operations']['delete']['access arguments append'] = TRUE;
  92. $resources[$entity_type]['operations']['delete']['args'] = array(
  93. // This argument isn't exposed in the service, only used internally..
  94. array(
  95. 'name' => 'entity_type',
  96. 'description' => t('The entity type.'),
  97. 'type' => 'string',
  98. 'default value' => $entity_type,
  99. 'optional' => TRUE,
  100. ),
  101. array(
  102. 'name' => 'uuid',
  103. 'description' => t('The %label UUID.', array('%label' => $entity_info['label'])),
  104. 'type' => 'text',
  105. 'source' => array('path' => 0),
  106. ),
  107. );
  108. }
  109. }
  110. }
  111. /**
  112. * Callback for the 'retrieve' method.
  113. *
  114. * @see entity_uuid_load()
  115. */
  116. function _uuid_services_entity_retrieve($entity_type, $uuid) {
  117. try {
  118. $entities = entity_uuid_load($entity_type, array($uuid));
  119. $entity = reset($entities);
  120. return $entity;
  121. }
  122. catch (Exception $exception) {
  123. watchdog_exception('uuid_services', $exception);
  124. return services_error($exception, 406, $uuid);
  125. }
  126. }
  127. /**
  128. * Callback for the 'update' method.
  129. *
  130. * @see entity_uuid_save()
  131. */
  132. function _uuid_services_entity_update($entity_type, $uuid, $entity) {
  133. try {
  134. $controller = entity_get_controller($entity_type);
  135. if ($controller instanceof EntityAPIControllerInterface) {
  136. $entity = $controller->create($entity);
  137. }
  138. else {
  139. $entity = (object) $entity;
  140. }
  141. $entity->uuid_services = TRUE;
  142. entity_uuid_save($entity_type, $entity);
  143. return $entity;
  144. }
  145. catch (Exception $exception) {
  146. watchdog_exception('uuid_services', $exception);
  147. return services_error($exception, 406, $entity);
  148. }
  149. }
  150. /**
  151. * Callback for the 'delete' method.
  152. *
  153. * @see entity_uuid_delete()
  154. */
  155. function _uuid_services_entity_delete($entity_type, $uuid) {
  156. try {
  157. $uuid_exist = (bool) entity_get_id_by_uuid($entity_type, array($uuid));
  158. if (!$uuid_exist) {
  159. /* UUID not found. Don't try to delete something that doesn't exist. */
  160. $args = array('@uuid' => $uuid, '@type' => $entity_type);
  161. watchdog('uuid_services', 'UUID @uuid not found for entity type @type', $args, WATCHDOG_WARNING);
  162. return TRUE;
  163. }
  164. $return = entity_uuid_delete($entity_type, array($uuid)) !== FALSE;
  165. return $return;
  166. }
  167. catch (Exception $exception) {
  168. watchdog_exception('uuid_services', $exception);
  169. return services_error($exception, 406, $uuid);
  170. }
  171. }
  172. /**
  173. * Access callback.
  174. *
  175. * @param string $op
  176. * The operation we are trying to do on the entity. Can only be:
  177. * - "view"
  178. * - "update"
  179. * - "delete"
  180. * See 'uuid_services_services_resources_alter()' for an explanation why
  181. * 'create' is missing.
  182. * @param array $args
  183. * The arguments passed to the method. The keys are holding the following:
  184. * 0. <entity_type>
  185. * 1. <uuid>
  186. * 2. <entity> (only available if $op == 'update')
  187. */
  188. function _uuid_services_entity_access($op, $args) {
  189. try {
  190. // Fetch the information we have to work with.
  191. $entity_type = $args[0];
  192. // Load functions always deal with multiple entities. So does this lookup
  193. // function. But in practice this will always only be one id.
  194. $entity_ids = entity_get_id_by_uuid($entity_type, array($args[1]));
  195. $entity = NULL;
  196. if (!empty($args[2])) {
  197. $entity = entity_create($entity_type, $args[2]);
  198. // We have to make the entity local (i.e. only have local references), for
  199. // access functions to work on it.
  200. entity_make_entity_local($entity_type, $entity);
  201. }
  202. // Fetch the local entity if we've got an id.
  203. elseif (!empty($entity_ids)) {
  204. $entities = entity_load($entity_type, $entity_ids);
  205. $entity = reset($entities);
  206. }
  207. // If we've been routed to the 'update' method and the entity we are
  208. // operating on doesn't exist yet, that should be reflected.
  209. if ($op == 'update' && empty($entity_ids)) {
  210. $op = 'create';
  211. }
  212. // If the user doesn't exist return 406 like services does.
  213. if (($entity_type == 'user' && empty($entity) && $op == 'view')) {
  214. return services_error(t('There is no user with UUID @uuid.', array('@uuid' => $args[1])), 406);;
  215. }
  216. // The following code is taken from entity_access() with some extra logic
  217. // to handle the case where an entity type is not defining an access
  218. // callback. With this logic, it's important that all entity types that
  219. // needs access control have an access callback defined.
  220. if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
  221. return $info[$entity_type]['access callback']($op, $entity, NULL, $entity_type);
  222. }
  223. return TRUE;
  224. }
  225. catch (Exception $exception) {
  226. watchdog_exception('uuid_services', $exception);
  227. return services_error($exception, 406, $entity_type);
  228. }
  229. }