tripal_ws.module 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. <?php
  2. require_once "api/tripal_ws.api.inc";
  3. require_once "includes/TripalWebService.inc";
  4. require_once "includes/TripalWebServiceResource.inc";
  5. require_once "includes/TripalWebServiceCollection.inc";
  6. /**
  7. * Implements hook_init()
  8. */
  9. function tripal_ws_init() {
  10. global $base_url;
  11. $version = 'v0.1';
  12. $api_url = $base_url . '/ws/' . $version;
  13. // Following the WC3 Hydra documentation, we want to add LINK to the header
  14. // of the site that indicates where the API documentation can be found.
  15. // This allows a hydra-enabled client to discover the API and use it.
  16. $attributes = array(
  17. 'rel' => 'http://www.w3.org/ns/hydra/core#apiDocumentation',
  18. 'href' => $api_url . '/ws-doc/',
  19. );
  20. drupal_add_html_head_link($attributes, $header = FALSE);
  21. }
  22. /**
  23. * Implements hook_menu().
  24. * Defines all menu items needed by Tripal Core
  25. *
  26. * @ingroup tripal_ws
  27. */
  28. function tripal_ws_menu() {
  29. // Web Services API callbacks.
  30. $items['ws'] = array(
  31. 'title' => 'Tripal Web Services API',
  32. 'page callback' => 'tripal_ws_services',
  33. 'access arguments' => array('access content'),
  34. 'type' => MENU_CALLBACK,
  35. );
  36. // Web Services API callbacks.
  37. $items['web-services'] = array(
  38. 'title' => 'Tripal Web Services API',
  39. 'page callback' => 'tripal_ws_get_services',
  40. 'access arguments' => array('access content'),
  41. 'type' => MENU_CALLBACK,
  42. );
  43. $items['remote/%/%/%/%'] = array(
  44. 'page callback' => 'tripal_ws_load_remote_entity',
  45. 'page arguments' => array(1, 2, 3, 4),
  46. 'access arguments' => array('access content'),
  47. 'type' => MENU_CALLBACK,
  48. );
  49. // Tripal Web Services setting groups
  50. $items['admin/tripal/storage/ws'] = array(
  51. 'title' => 'Remote Tripal Sites',
  52. 'description' => t("Create mashups of content using data from this site and remote Tripal sites."),
  53. 'weight' => 20,
  54. 'page callback' => 'system_admin_menu_block_page',
  55. 'access arguments' => array('administer tripal'),
  56. 'file' => 'system.admin.inc',
  57. 'file path' => drupal_get_path('module', 'system'),
  58. );
  59. $items['admin/tripal/storage/ws/tripal_sites'] = array(
  60. 'title' => 'Configuration',
  61. 'description' => t('Provides information about other Tripal sites.
  62. This allows data exchange and communication betwen Tripal
  63. enabled sites through the web services.'),
  64. 'page callback' => 'drupal_get_form',
  65. 'page arguments' => array('tripal_ws_tripal_sites_form'),
  66. 'access arguments' => array('administer tripal'),
  67. 'type' => MENU_NORMAL_ITEM,
  68. 'weight' => 0,
  69. 'file' => 'includes/tripal_ws.admin.inc',
  70. 'file path' => drupal_get_path('module', 'tripal_ws'),
  71. );
  72. $items['admin/tripal/storage/ws/tripal_sites/edit'] = array(
  73. 'title' => 'Add Tripal Site',
  74. 'description' => 'Add a Tripal site',
  75. 'page callback' => 'drupal_get_form',
  76. 'page arguments' => array('tripal_ws_tripal_sites_edit_form'),
  77. 'access arguments' => array('administer tripal'),
  78. 'file' => 'includes/tripal_ws.admin.inc',
  79. 'file path' => drupal_get_path('module', 'tripal_ws'),
  80. 'type' => MENU_LOCAL_ACTION,
  81. 'weight' => 2
  82. );
  83. $items['admin/tripal/storage/ws/tripal_sites/remove/%'] = array(
  84. 'title' => 'Remove Tripal Site',
  85. 'description' => 'Remove a Tripal site',
  86. 'page callback' => 'drupal_get_form',
  87. 'page arguments' => array('tripal_ws_tripal_sites_remove_form', 6),
  88. 'access arguments' => array('administer tripal'),
  89. 'file' => 'includes/tripal_ws.admin.inc',
  90. 'file path' => drupal_get_path('module', 'tripal_ws'),
  91. 'type' => MENU_CALLBACK,
  92. 'weight' => 2
  93. );
  94. return $items;
  95. }
  96. /**
  97. * The callback function for all RESTful web services.
  98. *
  99. */
  100. function tripal_ws_get_services() {
  101. drupal_add_http_header('Content-Type', 'application/json');
  102. try {
  103. $ws_path = func_get_args();
  104. $args = $_GET;
  105. unset($args['q']);
  106. // The web services should never be cached.
  107. drupal_page_is_cacheable(FALSE);
  108. // The Tripal web services bath will be:
  109. // [base_path]/web-services/[service name]/v[major_version].[minor_version]
  110. $matches = array();
  111. $service = '';
  112. $major_version = '';
  113. $minor_version = '';
  114. $list_services = FALSE;
  115. // If there is no path then we should list all of the services available.
  116. if (empty($ws_path)) {
  117. tripal_ws_list_services();
  118. return;
  119. }
  120. // A service path will have the service name in $ws_path[0] and the
  121. // version in $ws_path[1]. If we check that the version is correctly
  122. // formatted then we can look for the service class and invoke it.
  123. else if (preg_match("/^v(\d+)\.(\d+)$/", $ws_path[1], $matches)) {
  124. $service_type = $ws_path[0];
  125. $major_version = $matches[1];
  126. $minor_version = $matches[2];
  127. $service_version = 'v' . $major_version . '.' . $minor_version;
  128. }
  129. // If the URL doesn't match then return not found.
  130. else {
  131. throw new Exception("Unsupported service URL. Web service URLs must be of the following format: ");
  132. }
  133. // Get the service that matches the service_name
  134. $service = NULL;
  135. $services = tripal_get_web_services();
  136. foreach ($services as $service_class) {
  137. tripal_load_include_web_service_class($service_class);
  138. if ($service_class::$type == $service_type) {
  139. $service = new $service_class();
  140. if ($service->getVersion() == $service_version) {
  141. break;
  142. }
  143. $service = NULL;
  144. }
  145. }
  146. // If a service was not provided then return an error.
  147. if (!$service) {
  148. throw new Exception('The service type, "' . $service_type . '", is not available');
  149. }
  150. // Adjust the path to remove the service type and the version.
  151. $adj_path = $ws_path;
  152. array_shift($adj_path);
  153. array_shift($adj_path);
  154. // Now call the service to handle the request.
  155. $service->setPath($adj_path);
  156. $service->setParams($args);
  157. $service->handleRequest();
  158. $response = $service->getResponse();
  159. print drupal_json_encode($response);
  160. }
  161. catch (Exception $e) {
  162. $service = new TripalWebService();
  163. $service->setError($e->getMessage());
  164. $response = $service->getResponse();
  165. print drupal_json_encode($response);
  166. }
  167. }
  168. /**
  169. * Generates the list of services as the "home page" for Tripal web services.
  170. */
  171. function tripal_ws_list_services() {
  172. global $base_url;
  173. // Create an instance of the TriaplWebService class and use it to build
  174. // the entry point for the web serivces.
  175. $service = new TripalWebService();
  176. $services = tripal_get_web_services();
  177. // Create the parent resource which is a collection.
  178. $resource = new TripalWebServiceResource();
  179. $resource->addContextItem('entrypoint', 'hydra:entrypoint');
  180. $resource->setType('entrypoint');
  181. // Now add the member to the collection
  182. foreach ($services as $service_class) {
  183. tripal_load_include_web_service_class($service_class);
  184. $service = new $service_class();
  185. $version = $service->getVersion();
  186. $iri = $base_url . '/web-services/' . $service_class::$type . '/' . $version;
  187. $resource->addContextItem($service_class::$type, '');
  188. $resource->addProperty($service_class::$type, $iri);
  189. }
  190. // For discoverability add the document webservice.
  191. $service->setResource($resource);
  192. $response = $service->getResponse();
  193. // Using the TripalWebService class to build this entry point was
  194. // conveneint but it wasn't quite meant for this way of working with it.
  195. // So make some last minute fixes before returning.
  196. $response['@id'] = $iri = $base_url . '/web-services';
  197. print drupal_json_encode($response);
  198. }
  199. /**
  200. * The callback function for all RESTful web services.
  201. *
  202. */
  203. function tripal_ws_services() {
  204. $ws_path = func_get_args();
  205. $params = $_GET;
  206. unset($params['q']);
  207. // The web services should never be cached.
  208. drupal_page_is_cacheable(FALSE);
  209. // Using the provided version number, determine which web services
  210. // verion to call.
  211. $version = array_shift($ws_path);
  212. if ($version and preg_match('/v\d+\.\d+/', $version)) {
  213. $api_url = 'ws/' . $version;
  214. // Add the file with the appropriate web services.
  215. module_load_include('inc', 'tripal_ws', 'includes/tripal_ws.rest_' . $version);
  216. $version = preg_replace('/\./', '_', $version);
  217. $function = 'tripal_ws_services_' . $version;
  218. $response = array();
  219. if (function_exists($function)) {
  220. $response = $function($api_url, $ws_path, $params);
  221. }
  222. }
  223. else {
  224. // TODO: What do we do if no version is provided?
  225. }
  226. drupal_add_http_header('Content-Type', 'application/json');
  227. print drupal_json_encode($response);
  228. }
  229. /**
  230. *
  231. * @param $entities
  232. * @param $type
  233. */
  234. function tripal_ws_entity_load($entities, $type) {
  235. foreach ($entities as $entity) {
  236. }
  237. }
  238. /**
  239. *
  240. * @param $site_id
  241. * @param $api_version
  242. * @param $ctype
  243. * @param $id
  244. *
  245. * @return
  246. */
  247. function tripal_ws_load_remote_entity($site_id, $api_version, $ctype, $id) {
  248. // Get the content type on this site
  249. $bundle = tripal_load_bundle_entity(array('label' => $ctype));
  250. $term = entity_load('TripalTerm', array('id' => $bundle->term_id));
  251. $term = reset($term);
  252. $vocab = $term->vocab;
  253. $query = db_select('tripal_sites', 'ts');
  254. $query->fields('ts');
  255. $query->condition('id', $site_id);
  256. $site = $query->execute()->fetchObject();
  257. if (!$site) {
  258. return 'Could not find specified site.';
  259. }
  260. // Get the content from the web services of the remote site.
  261. $url = $site->url . "/ws/v0.1/content/" . $ctype . "/" . $id;
  262. $json = file_get_contents($url);
  263. $response = json_decode($json, TRUE);
  264. // Set the title for this page to match the title provided.
  265. drupal_set_title($response['label']);
  266. // Attribute this data to the proper source.
  267. $source_url = l($response['label'], $response['itemPage'], array('attributes' => array('target' => '_blank')));
  268. $content = '<div><strong>Source:</strong> ' . $site->name . ': ' . $source_url . '</div>';
  269. // Fake an entity so we can display this content using the same
  270. // entity type on this site.
  271. $entity = new TripalEntity(array(), 'TripalEntity');
  272. $entity->id = 807;
  273. $entity->type = 'TripalEntity';
  274. $entity->bundle = $bundle->name;
  275. $entity->term_id = $term->id;
  276. $entity->title = $response['label'];
  277. $entity->uid = 1;
  278. $entity->status = 1;
  279. // Get the fields and create a list of those that are attached to the bundle.
  280. $fields = field_info_fields();
  281. $my_fields = array();
  282. foreach ($fields as $field) {
  283. if (isset($field['bundles']['TripalEntity'])) {
  284. foreach ($field['bundles']['TripalEntity'] as $bundle_name) {
  285. if ($bundle_name == $bundle->name) {
  286. $my_fields[] = $field;
  287. }
  288. }
  289. }
  290. }
  291. // Add in the value for the 'content_type' field.
  292. $entity->content_type = array();
  293. $entity->content_type['und'][0]['value'] = $bundle->label;
  294. // For each field we know about that should be attached to our bundle,
  295. // see if we can find a corresponding entry in the results returned from
  296. // the web service call. If so, then add the field to our fake entity.
  297. foreach ($my_fields as $field) {
  298. // Get the semantic web term for this field.
  299. $field_name = $field['field_name'];
  300. $settings = $field['settings'];
  301. // If the field does not have a semantic web mapping, then skip it.
  302. if (!isset($settings['semantic_web'])) {
  303. continue;
  304. }
  305. // Convert the term into it's db and accession elements and look it up
  306. // for more details.
  307. list($vocabulary, $accession) = explode(':', $settings['semantic_web']);
  308. $term = tripal_get_term_details($vocabulary, $accession);
  309. // Convert the term to lowercase and remove spaces so we can compare
  310. // correctly.
  311. $term_name = strtolower(preg_replace('/ /', '_', $term['name']));
  312. // TODO: check for the term in the response makes the assumption
  313. // that the term is the same on both sides. This may not be true. The
  314. // acutal vocab and accession for both terms should be compared.
  315. if (isset($response[$term_name])) {
  316. // If this field is of type '@id' then this links out to another
  317. // URL where that information can be retrieved. We'll have to
  318. // handle that separately.
  319. if (isset($response['@context'][$term_name]['@type']) and
  320. $response['@context'][$term_name]['@type'] == '@id') {
  321. $subquery = json_decode(file_get_contents($response[$term_name]), TRUE);
  322. // If the result is a collection then we want to add each value with
  323. // it's own delta value.
  324. if (array_key_exists('@type', $subquery) and $subquery['@type'] == 'Collection') {
  325. $i = 0;
  326. $f = array();
  327. foreach ($subquery['member'] as $member) {
  328. $f['und'][$i]['value'] = $member;
  329. $i++;
  330. }
  331. $entity->$field_name = $f;
  332. }
  333. // If the result is not a collection then just add it.
  334. else {
  335. unset($subquery['@context']);
  336. unset($subquery['@id']);
  337. $f = array();
  338. $f['und'][0]['value'] = $subquery;
  339. $entity->$field_name = $f;
  340. }
  341. }
  342. // For all fields that are currently attached, add the field and
  343. // value to the entity.
  344. else {
  345. $f = array();
  346. $f['und'][0]['value'] = $response[$term_name];
  347. $entity->$field_name = $f;
  348. }
  349. }
  350. }
  351. // Generate the View for this entity
  352. $entities = array();
  353. $entities[] = $entity;
  354. $view = entity_view('TripalEntity', $entities);
  355. $content .= drupal_render($view['TripalEntity'][807]);
  356. return $content;
  357. }