$vocab['sw_url'] . 'apiDocumentation', 'href' => $api_url . '/doc/v0.1', ); drupal_add_html_head_link($attributes, $header = FALSE); } /** * Implements hook_menu(). * Defines all menu items needed by Tripal Core * * @ingroup tripal_ws */ function tripal_ws_menu() { // Web Services API callbacks. $items['web-services'] = array( 'title' => 'Tripal Web Services API', 'page callback' => 'tripal_ws_get_services', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); $items['remote/%/%/%/%'] = array( 'page callback' => 'tripal_ws_load_remote_entity', 'page arguments' => array(1, 2, 3, 4), 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); // Tripal Web Services setting groups $items['admin/tripal/storage/ws'] = array( 'title' => 'Remote Tripal Sites', 'description' => t("Create mashups of content using data from this site and remote Tripal sites."), 'weight' => 20, 'page callback' => 'system_admin_menu_block_page', 'access arguments' => array('administer tripal'), 'file' => 'system.admin.inc', 'file path' => drupal_get_path('module', 'system'), ); $items['admin/tripal/storage/ws/tripal_sites'] = array( 'title' => 'Configuration', 'description' => t('Provides information about other Tripal sites. This allows data exchange and communication betwen Tripal enabled sites through the web services.'), 'page callback' => 'drupal_get_form', 'page arguments' => array('tripal_ws_tripal_sites_form'), 'access arguments' => array('administer tripal'), 'type' => MENU_NORMAL_ITEM, 'weight' => 0, 'file' => 'includes/tripal_ws.admin.inc', 'file path' => drupal_get_path('module', 'tripal_ws'), ); $items['admin/tripal/storage/ws/tripal_sites/edit'] = array( 'title' => 'Add Tripal Site', 'description' => 'Add a Tripal site', 'page callback' => 'drupal_get_form', 'page arguments' => array('tripal_ws_tripal_sites_edit_form'), 'access arguments' => array('administer tripal'), 'file' => 'includes/tripal_ws.admin.inc', 'file path' => drupal_get_path('module', 'tripal_ws'), 'type' => MENU_LOCAL_ACTION, 'weight' => 2 ); $items['admin/tripal/storage/ws/tripal_sites/remove/%'] = array( 'title' => 'Remove Tripal Site', 'description' => 'Remove a Tripal site', 'page callback' => 'drupal_get_form', 'page arguments' => array('tripal_ws_tripal_sites_remove_form', 6), 'access arguments' => array('administer tripal'), 'file' => 'includes/tripal_ws.admin.inc', 'file path' => drupal_get_path('module', 'tripal_ws'), 'type' => MENU_CALLBACK, 'weight' => 2 ); return $items; } /** * The callback function for all RESTful web services. * */ function tripal_ws_get_services() { global $base_url; $service_path = $base_url . '/web-services'; // This should go out as ld+json drupal_add_http_header('Content-Type', 'application/ld+json'); // Add a link header for the vocabulary service so that clients // know where to find the docs. tripal_load_include_web_service_class('TripalDocService_v0_1'); $service = new TripalDocService_v0_1($service_path); $vocab = tripal_get_vocabulary_details('hydra'); drupal_add_http_header('Link', '<' . $service->getServicePath() . '>; rel="' . $vocab['sw_url'] . 'apiDocumentation"'); drupal_add_http_header('Cache-Control', "no-cache"); try { $ws_path = func_get_args(); $args = $_GET; unset($args['q']); // The web services should never be cached. drupal_page_is_cacheable(FALSE); // The Tripal web services bath will be: // [base_path]/web-services/[service name]/v[major_version].[minor_version] $matches = array(); $service = ''; $major_version = ''; $minor_version = ''; $list_services = FALSE; // If there is no path then we should list all of the services available. if (empty($ws_path)) { tripal_ws_list_services(); return; } // A service path will have the service name in $ws_path[0] and the // version in $ws_path[1]. If we check that the version is correctly // formatted then we can look for the service class and invoke it. else if (preg_match("/^v(\d+)\.(\d+)$/", $ws_path[1], $matches)) { $service_type = $ws_path[0]; $major_version = $matches[1]; $minor_version = $matches[2]; $service_version = 'v' . $major_version . '.' . $minor_version; } // If the URL doesn't match then return not found. else { throw new Exception("Unsupported service URL: '" . $ws_path[1] . "'"); } // Get the service that matches the service_name $service = NULL; $services = tripal_get_web_services(); foreach ($services as $service_class) { tripal_load_include_web_service_class($service_class); if ($service_class::$type == $service_type) { $service = new $service_class($service_path); if ($service->getVersion() == $service_version) { break; } $service = NULL; } } // If a service was not provided then return an error. if (!$service) { throw new Exception('The service type, "' . $service_type . '", is not available'); } // Adjust the path to remove the service type and the version. $adj_path = $ws_path; array_shift($adj_path); array_shift($adj_path); // Now call the service to handle the request. $service->setPath($adj_path); $service->setParams($args); $service->handleRequest(); $response = $service->getResponse(); print drupal_json_encode($response); } catch (Exception $e) { $service = new TripalWebService($service_path); $service->setError($e->getMessage()); $response = $service->getResponse(); print drupal_json_encode($response); } } /** * Generates the list of services as the "home page" for Tripal web services. */ function tripal_ws_list_services() { global $base_url; $base_path = $base_url . '/web-services'; // Create an instance of the TriaplWebService class and use it to build // the entry point for the web serivces. $service = new TripalWebService($base_path); // Get the list of web service classes. $services = tripal_get_web_services(); // Create the parent resource which is a collection. $resource = new TripalWebServiceResource($base_path); // Add the vocabulary to the context. tripal_load_include_web_service_class('TripalDocService_v0_1'); $service = new TripalDocService_v0_1($base_path); $resource->addContextItem('vocab', $service->getServicePath() . '#'); $resource->addContextItem('EntryPoint', 'vocab:EntryPoint'); $resource->setType('EntryPoint'); // Now add the services as properties. foreach ($services as $service_class) { tripal_load_include_web_service_class($service_class); if ($service_class == 'TripalDocService_v0_1') { continue; } $service = new $service_class($base_path); $resource->addContextItem($service_class::$type, array( '@id' => 'vocab:EntryPoint/' . $service_class::$type, '@type' => '@id', )); $resource->addProperty($service_class::$type, $service->getServicePath()); } $service->setResource($resource); $response = $service->getResponse(); print drupal_json_encode($response); } /** * The callback function for all RESTful web services. * */ function tripal_ws_services() { $ws_path = func_get_args(); $params = $_GET; unset($params['q']); // The web services should never be cached. drupal_page_is_cacheable(FALSE); // Using the provided version number, determine which web services // verion to call. $version = array_shift($ws_path); if ($version and preg_match('/v\d+\.\d+/', $version)) { $api_url = 'ws/' . $version; // Add the file with the appropriate web services. module_load_include('inc', 'tripal_ws', 'includes/tripal_ws.rest_' . $version); $version = preg_replace('/\./', '_', $version); $function = 'tripal_ws_services_' . $version; $response = array(); if (function_exists($function)) { $response = $function($api_url, $ws_path, $params); } } else { // TODO: What do we do if no version is provided? } drupal_add_http_header('Content-Type', 'application/ld+json'); print drupal_json_encode($response); } /** * * @param $site_id * @param $api_version * @param $ctype * @param $id * * @return */ function tripal_ws_load_remote_entity($site_id, $api_version, $ctype, $id) { // Get the content type on this site $bundle = tripal_load_bundle_entity(array('label' => $ctype)); $term = entity_load('TripalTerm', array('id' => $bundle->term_id)); $term = reset($term); $vocab = $term->vocab; $query = db_select('tripal_sites', 'ts'); $query->fields('ts'); $query->condition('id', $site_id); $site = $query->execute()->fetchObject(); if (!$site) { return 'Could not find specified site.'; } // Get the content from the web services of the remote site. $url = $site->url . "/ws/v0.1/content/" . $ctype . "/" . $id; $json = file_get_contents($url); $response = json_decode($json, TRUE); // Set the title for this page to match the title provided. drupal_set_title($response['label']); // Attribute this data to the proper source. $source_url = l($response['label'], $response['ItemPage'], array('attributes' => array('target' => '_blank'))); $content = '
Source: ' . $site->name . ': ' . $source_url . '
'; // Fake an entity so we can display this content using the same // entity type on this site. $entity = new TripalEntity(array(), 'TripalEntity'); $entity->id = 807; $entity->type = 'TripalEntity'; $entity->bundle = $bundle->name; $entity->term_id = $term->id; $entity->title = $response['label']; $entity->uid = 1; $entity->status = 1; // Get the fields and create a list of those that are attached to the bundle. $fields = field_info_fields(); $my_fields = array(); foreach ($fields as $field) { if (isset($field['bundles']['TripalEntity'])) { foreach ($field['bundles']['TripalEntity'] as $bundle_name) { if ($bundle_name == $bundle->name) { $my_fields[] = $field; } } } } // Add in the value for the 'content_type' field. $entity->content_type = array(); $entity->content_type['und'][0]['value'] = $bundle->label; // For each field we know about that should be attached to our bundle, // see if we can find a corresponding entry in the results returned from // the web service call. If so, then add the field to our fake entity. foreach ($my_fields as $field) { // Get the semantic web term for this field. $field_name = $field['field_name']; $settings = $field['settings']; // If the field does not have a semantic web mapping, then skip it. if (!isset($settings['semantic_web'])) { continue; } // Convert the term into it's db and accession elements and look it up // for more details. list($vocabulary, $accession) = explode(':', $settings['semantic_web']); $term = tripal_get_term_details($vocabulary, $accession); // Convert the term to lowercase and remove spaces so we can compare // correctly. $term_name = strtolower(preg_replace('/ /', '_', $term['name'])); // TODO: check for the term in the response makes the assumption // that the term is the same on both sides. This may not be true. The // acutal vocab and accession for both terms should be compared. if (isset($response[$term_name])) { // If this field is of type '@id' then this links out to another // URL where that information can be retrieved. We'll have to // handle that separately. if (isset($response['@context'][$term_name]['@type']) and $response['@context'][$term_name]['@type'] == '@id') { $subquery = json_decode(file_get_contents($response[$term_name]), TRUE); // If the result is a collection then we want to add each value with // it's own delta value. if (array_key_exists('@type', $subquery) and $subquery['@type'] == 'Collection') { $i = 0; $f = array(); foreach ($subquery['member'] as $member) { $f['und'][$i]['value'] = $member; $i++; } $entity->$field_name = $f; } // If the result is not a collection then just add it. else { unset($subquery['@context']); unset($subquery['@id']); $f = array(); $f['und'][0]['value'] = $subquery; $entity->$field_name = $f; } } // For all fields that are currently attached, add the field and // value to the entity. else { $f = array(); $f['und'][0]['value'] = $response[$term_name]; $entity->$field_name = $f; } } } // Generate the View for this entity $entities = array(); $entities[] = $entity; $view = entity_view('TripalEntity', $entities); $content .= drupal_render($view['TripalEntity'][807]); return $content; } function tripal_ws_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) { // Don't let the user change the cardinality of web services fields if ($form['#instance']['entity_type'] == 'TripalEntity') { if ($form['#field']['storage']['type'] == 'field_tripal_ws_storage') { $form['field']['cardinality']['#access'] = FALSE; $form['instance']['required']['#access'] = FALSE; } } } /* * Returns the decoded json data for a specific field. */ function tripal_ws_remote_data_single_field_pull($field, $entity_url){ $options = array(); $full_url = $entity_url . '/' . $field; $data = drupal_http_request($full_url, $options); if(!empty($data)){ $data = drupal_json_decode($data->data); } return $data; } /** * Implements hook_entity_info_alter() * * Add the web services display as a view mode. */ function tripal_ws_entity_info_alter(&$entity_info) { // Set the controller class for nodes to an alternate implementation of the // DrupalEntityController interface. $entity_info['TripalEntity']['view modes'] += array( 'tripal_ws' => array( 'label' => t('Tripal Web Services'), 'custom settings' => FALSE, ), ); }