tripal_ws.rest_v0.1.inc 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945
  1. <?php
  2. /**
  3. *
  4. */
  5. function tripal_ws_services_v0_1($api_url, $ws_path, $params) {
  6. // Set some initial variables.
  7. $response = array();
  8. $version = 'v0.1';
  9. // Set some defaults for the response.
  10. $response['@context'] = array();
  11. // Lump everything ito a try block so that if there is a problem we can
  12. // throw an error and have that returned in the response.
  13. try {
  14. // The services is the first argument
  15. $service = (count($ws_path) > 0) ? $ws_path[0] : '';
  16. switch ($service) {
  17. case 'doc':
  18. tripal_ws_services_v0_1_handle_doc_service($api_url, $response);
  19. break;
  20. case 'content':
  21. tripal_ws_services_v0_1_handle_content_service($api_url, $response, $ws_path, $params);
  22. break;
  23. case 'vocab':
  24. tripal_ws_services_v0_1_handle_vocab_service($api_url, $response, $ws_path);
  25. break;
  26. default:
  27. tripal_ws_services_v0_1_handle_no_service($api_url, $response);
  28. }
  29. }
  30. catch (Exception $e) {
  31. watchdog('tripal_ws', $e->getMessage(), array(), WATCHDOG_ERROR);
  32. $message = $e->getMessage();
  33. drupal_add_http_header('Status', '400 Bad Request');
  34. }
  35. return $response;
  36. }
  37. /**
  38. *
  39. * @param $api_url
  40. * @param $response
  41. * @param $ws_path
  42. */
  43. function tripal_ws_services_v0_1_handle_content_service($api_url, &$response, $ws_path, $params) {
  44. // Get the content type.
  45. $ctype = (count($ws_path) > 1) ? $ws_path[1] : '';
  46. $entity_id = (count($ws_path) > 2) ? $ws_path[2] : '';
  47. // If we have no content type then list all of the available content types.
  48. if (!$ctype) {
  49. tripal_ws_services_v0_1_get_content_types($api_url, $response);
  50. }
  51. // If we don't have an entity ID then show a paged list of entities with
  52. // the given type.
  53. else if ($ctype and !$entity_id) {
  54. tripal_ws_services_v0_1_get_content_type($api_url, $response, $ws_path, $ctype, $params);
  55. }
  56. // If we have a content type and an entity ID then show the entity
  57. else {
  58. tripal_ws_services_v0_1_get_content($api_url, $response, $ws_path, $ctype, $entity_id, $params);
  59. }
  60. }
  61. /**
  62. *
  63. * @param $api_url
  64. * @param $response
  65. * @param $ws_path
  66. */
  67. function tripal_ws_services_v0_1_handle_vocab_service($api_url, &$response, $ws_path) {
  68. // Get the vocab name.
  69. $vocabulary = (count($ws_path) > 1) ? $ws_path[1] : '';
  70. $accession = (count($ws_path) > 2) ? $ws_path[2] : '';
  71. // If we have no $vocabulary type then list all of the available vocabs.
  72. if (!$vocabulary) {
  73. tripal_ws_services_v0_1_get_vocabs($api_url, $response);
  74. }
  75. // If we don't have a $vocabulary then show a paged list of terms.
  76. else if ($vocabulary and !$accession) {
  77. tripal_ws_services_v0_1_get_vocab($api_url, $response, $ws_path, $vocabulary);
  78. }
  79. // If we have a content type and an entity ID then show the entity
  80. else if ($vocabulary and $accession) {
  81. tripal_ws_services_v0_1_get_term($api_url, $response, $ws_path, $vocabulary, $accession);
  82. }
  83. }
  84. /**
  85. *
  86. * @param $api_url
  87. * @param $response
  88. */
  89. function tripal_ws_services_v0_1_get_vocabs($api_url, &$response) {
  90. // First, add the vocabularies used into the @context section.
  91. $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
  92. $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
  93. // Next add in the ID for tihs resource.
  94. $response['@id'] = url($api_url . '/vocab', array('absolute' => TRUE));
  95. // Start the list.
  96. $response['@type'] = 'Collection';
  97. $response['totalItems'] = 0;
  98. $response['label'] = 'Content Types';
  99. $response['member'] = array();
  100. $vocabs = db_select('tripal_vocab', 'tv')
  101. ->fields('tv')
  102. ->execute();
  103. // Iterate through the vocabularies and add an entry in the collection.
  104. $i = 0;
  105. while ($vocab = $vocabs->fetchObject()) {
  106. // Add the bundle as a content type.
  107. $response['member'][] = array(
  108. '@id' => url($api_url . '/vocab/' . urlencode($vocab->vocabulary), array('absolute' => TRUE)),
  109. '@type' => 'vocabulary',
  110. 'vocabulary' => $vocab->vocabulary,
  111. );
  112. $i++;
  113. }
  114. $response['totalItems'] = $i;
  115. // Lastly, add in the terms used into the @context section.
  116. $response['@context']['Collection'] = 'hydra:Collection';
  117. $response['@context']['totalItems'] = 'hydra:totalItems';
  118. $response['@context']['member'] = 'hydra:member';
  119. $response['@context']['label'] = 'rdfs:label';
  120. $response['@context']['description'] = 'hydra:description';
  121. }
  122. /**
  123. *
  124. * @param $api_url
  125. * @param $response
  126. * @param $ws_path
  127. */
  128. function tripal_ws_services_v0_1_get_vocab($api_url, &$response, $ws_path, $vocabulary) {
  129. // First, add the vocabularies used into the @context section.
  130. $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
  131. $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
  132. $response['@context']['schema'] = 'https://schema.org/';
  133. // Next add in the ID for tihs resource.
  134. $response['@id'] = url($api_url . '/vocab/' . $vocabulary, array('absolute' => TRUE));
  135. // Get the vocabulary
  136. $vocab = tripal_load_vocab_entity(array('vocabulary' => $vocabulary));
  137. // Start the list.
  138. $response['@type'] = 'Collection';
  139. $response['totalItems'] = 0;
  140. $response['label'] = vocabulary . " vocabulary collection";
  141. $response['comment'] = 'The following list of terms may not be the full ' .
  142. 'list for the vocabulary. The terms listed here are only those ' .
  143. 'that have associated content on this site.';
  144. // Get the list of terms for this vocab.
  145. $query = db_select('tripal_term', 'tt')
  146. ->fields('tt', array('id'))
  147. ->condition('vocab_id', $vocab->id)
  148. ->orderBy('accession', 'DESC');
  149. // Iterate through the entities and add them to the list.
  150. $terms = $query->execute();
  151. $i = 0;
  152. while($term = $terms->fetchObject()) {
  153. $term = tripal_load_term_entity(array('term_id' => $term->id));
  154. $response['member'][] = array(
  155. '@id' => url($api_url . '/vocab/' . urlencode($vocabulary) . '/' . urlencode($term->accession), array('absolute' => TRUE)),
  156. '@type' => 'vocabulary_term',
  157. 'vocabulary' => $vocab->vocabulary,
  158. 'accession' => $term->accession,
  159. 'name' => $term->name,
  160. 'definition' => $term->definition,
  161. );
  162. $i++;
  163. }
  164. $response['totalItems'] = $i;
  165. // Lastly, add in the terms used into the @context section.
  166. $response['@context']['Collection'] = 'hydra:Collection';
  167. $response['@context']['totalItems'] = 'hydra:totalItems';
  168. $response['@context']['member'] = 'hydra:member';
  169. $response['@context']['label'] = 'rdfs:label';
  170. $response['@context']['comment'] = 'rdfs:comment';
  171. $response['@context']['itemPage'] = 'schema:itemPage';
  172. }
  173. /**
  174. *
  175. * @param $api_url
  176. * @param $response
  177. * @param $ws_path
  178. */
  179. function tripal_ws_services_v0_1_get_term($api_url, &$response, $ws_path, $vocabulary, $accession) {
  180. // First, add the vocabularies used into the @context section.
  181. $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
  182. $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
  183. $response['@context']['schema'] = 'https://schema.org/';
  184. // Get the term.
  185. $term = tripal_load_term_entity(array('vocabulary' => $vocabulary, 'accession' => $accession));
  186. // Next add in the ID and Type for this resources.
  187. $response['@id'] = url($api_url . '/vocab/' . urlencode($vocabulary) . '/' . urlencode($accession), array('absolute' => TRUE));
  188. $response['@type'] = 'vocabulary_term';
  189. $response['label'] = $term->name;
  190. $response['vocabulary'] = $vocabulary;
  191. $response['accession'] = $accession;
  192. $response['name'] = $term->name;
  193. $response['definition'] = $term->definition;
  194. if ($term->url) {
  195. $response['URL'] = $term->url;
  196. }
  197. // Lastly, add in the terms used into the @context section.
  198. $response['@context']['label'] = 'rdfs:label';
  199. $response['@context']['itemPage'] = 'schema:itemPage';
  200. }
  201. /**
  202. * Provides a collection (list) of all of the content types.
  203. *
  204. * @param $api_url
  205. * @param $response
  206. */
  207. function tripal_ws_services_v0_1_get_content_types($api_url, &$response) {
  208. // First, add the vocabularies used into the @context section.
  209. $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
  210. $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
  211. // Next add in the ID for tihs resource.
  212. $response['@id'] = url($api_url . '/content', array('absolute' => TRUE));
  213. // Start the list.
  214. $response['@type'] = 'Collection';
  215. $response['totalItems'] = 0;
  216. $response['label'] = 'Content Types';
  217. $response['member'] = array();
  218. // Get the list of published terms (these are the bundle IDs)
  219. $bundles = db_select('tripal_bundle', 'tb')
  220. ->fields('tb')
  221. ->orderBy('tb.label', 'ASC')
  222. ->execute();
  223. // Iterate through the terms and add an entry in the collection.
  224. $i = 0;
  225. while ($bundle = $bundles->fetchObject()) {
  226. $entity = entity_load('TripalTerm', array('id' => $bundle->term_id));
  227. $term = reset($entity);
  228. $vocab = $term->vocab;
  229. $response['@context'][$term->name] = $term->url;
  230. // Get the bundle description. If no description is provided then
  231. // use the term definition
  232. $description = tripal_get_bundle_variable('description', $bundle->id);
  233. if (!$description) {
  234. $description = $term->definition;
  235. }
  236. // Add the bundle as a content type.
  237. $response['member'][] = array(
  238. '@id' => url($api_url . '/content/' . urlencode($bundle->label), array('absolute' => TRUE)),
  239. '@type' => $term->name,
  240. 'label' => $bundle->label,
  241. 'description' => $description,
  242. );
  243. $i++;
  244. }
  245. $response['totalItems'] = $i;
  246. // Lastly, add in the terms used into the @context section.
  247. $response['@context']['Collection'] = 'hydra:Collection';
  248. $response['@context']['totalItems'] = 'hydra:totalItems';
  249. $response['@context']['member'] = 'hydra:member';
  250. $response['@context']['label'] = 'rdfs:label';
  251. $response['@context']['description'] = 'hydra:description';
  252. }
  253. /**
  254. *
  255. * @param $api_url
  256. * @param $response
  257. * @param $ws_path
  258. */
  259. function tripal_ws_services_v0_1_get_content_type($api_url, &$response, $ws_path, $ctype, $params) {
  260. // First, add the vocabularies used into the @context section.
  261. $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
  262. $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
  263. // Next add in the ID for this resource.
  264. $URL = url($api_url . '/content/' . $ctype, array('absolute' => TRUE));
  265. $response['@id'] = $URL;
  266. // Get the TripalBundle, TripalTerm and TripalVocab type for this type.
  267. $bundle = tripal_load_bundle_entity(array('label' => $ctype));
  268. $term = entity_load('TripalTerm', array('id' => $bundle->term_id));
  269. $term = reset($term);
  270. $response['@context'][$term->name] = $term->url;
  271. // Start the list.
  272. $response['@type'] = 'Collection';
  273. $response['totalItems'] = 0;
  274. $response['label'] = $bundle->label . " collection";
  275. // Iterate through the fields and create a $field_mapping array that makes
  276. // it easier to determine which filter criteria belongs to which field. The
  277. // key is the label for the field and the value is the field name. This way
  278. // user's can use the field label or the field name to form a query.
  279. $field_mapping = array();
  280. $fields = field_info_fields();
  281. foreach ($fields as $field) {
  282. if (array_key_exists('TripalEntity', $field['bundles'])) {
  283. foreach ($field['bundles']['TripalEntity'] as $bundle_name) {
  284. if ($bundle_name == $bundle->name) {
  285. $instance = field_info_instance('TripalEntity', $field['field_name'], $bundle_name);
  286. if (array_key_exists('term_accession', $instance['settings'])){
  287. $vocabulary = $instance['settings']['term_vocabulary'];
  288. $accession = $instance['settings']['term_accession'];
  289. $term = tripal_get_term_details($vocabulary, $accession);
  290. $key = $term['name'];
  291. $key = strtolower(preg_replace('/ /', '_', $key));
  292. $field_mapping[$key] = $field['field_name'];
  293. $field_mapping[$field['field_name']] = $field['field_name'];
  294. }
  295. }
  296. }
  297. }
  298. }
  299. // Convert the filters to their field names
  300. $new_params = array();
  301. foreach ($params as $param => $value) {
  302. // Ignore non filter parameters
  303. if ($param == 'page' or $param == 'limit') {
  304. continue;
  305. }
  306. // Break apart any operators
  307. $key = $param;
  308. $op = '=';
  309. $matches = array();
  310. if (preg_match('/^(.+);(.+)$/', $key, $matches)) {
  311. $key = $matches[1];
  312. $op = $matches[2];
  313. }
  314. // Break apart any subkeys and pull the first one out for the term name key.
  315. $subkeys = explode(',', $key);
  316. if (count($subkeys) > 0) {
  317. $key = array_shift($subkeys);
  318. }
  319. $column_name = $key;
  320. // Map the values in the filters to their appropriate field names.
  321. if (array_key_exists($key, $field_mapping)) {
  322. $field_name = $field_mapping[$key];
  323. if (count($subkeys) > 0) {
  324. $column_name .= '.' . implode('.', $subkeys);
  325. }
  326. $new_params[$field_name]['value'] = $value;
  327. $new_params[$field_name]['op'] = $op;
  328. }
  329. else {
  330. throw new Exception("The filter term, '$key', is not available for use.");
  331. }
  332. }
  333. // Get the list of entities for this bundle.
  334. $query = new TripalFieldQuery();
  335. $query->entityCondition('entity_type', 'TripalEntity');
  336. $query->entityCondition('bundle', $bundle->name);
  337. foreach($new_params as $field_name => $details) {
  338. $value = $details['value'];
  339. switch ($details['op']) {
  340. case 'eq':
  341. $op = '=';
  342. break;
  343. case 'gt':
  344. $op = '>';
  345. break;
  346. case 'gte':
  347. $op = '>=';
  348. break;
  349. case 'lt':
  350. $op = '<';
  351. break;
  352. case 'lte':
  353. $op = '<=';
  354. break;
  355. case 'ne':
  356. $op = '<>';
  357. break;
  358. case 'contains':
  359. $op = 'CONTAINS';
  360. break;
  361. case 'starts':
  362. $op = 'STARTS WITH';
  363. break;
  364. default:
  365. $op = '=';
  366. }
  367. // We pass in the $column_name as an identifier for any sub fields
  368. // that are present for the fields.
  369. $query->fieldCondition($field_name, $column_name, $value, $op);
  370. }
  371. // Perform the query just as a count first to get the number of records.
  372. $cquery = clone $query;
  373. $cquery->count();
  374. $num_records = $cquery->execute();
  375. if (!$num_records) {
  376. $num_records = 0;
  377. }
  378. // Add in the pager to the response.
  379. $response['totalItems'] = $num_records;
  380. $limit = array_key_exists('limit', $params) ? $params['limit'] : 25;
  381. $total_pages = ceil($num_records / $limit);
  382. $page = array_key_exists('page', $params) ? $params['page'] : 1;
  383. if ($num_records > 0) {
  384. $response['view'] = array(
  385. '@id' => $URL . "?page=$page&limit=$limit",
  386. '@type' => 'PartialCollectionView',
  387. 'first' => $URL . "?page=1&limit=$limit",
  388. 'last' => $URL . "?page=$total_pages&limit=$limit",
  389. );
  390. $prev = $page - 1;
  391. $next = $page + 1;
  392. if ($prev > 0) {
  393. $response['view']['previous'] = $URL . "?page=$prev&limit=$limit";
  394. }
  395. if ($next < $total_pages) {
  396. $response['view']['next'] = $URL . "?page=$next&limit=$limit";
  397. }
  398. }
  399. // Now perform the query.
  400. $start = ($page - 1) * $limit;
  401. $query->range($start, $limit);
  402. $results = $query->execute();
  403. // Iterate through the entities and add them to the list.
  404. $i = 0;
  405. foreach ($results['TripalEntity'] as $entity_id => $stub) {
  406. $vocabulary = '';
  407. $term_name = '';
  408. // We don't need all of the attached fields for an entity so, we'll
  409. // not use the entity_load() function. Instead just pull it from the
  410. // database table.
  411. $query = db_select('tripal_entity', 'TE');
  412. $query->join('tripal_term', 'TT', 'TE.term_id = TT.id');
  413. $query->fields('TE');
  414. $query->fields('TT', array('name'));
  415. $query->condition('TE.id', $entity_id);
  416. $entity = $query->execute()->fetchObject();
  417. //$entity = tripal_load_entity('TripalEntity', array($entity->id));
  418. $response['member'][] = array(
  419. '@id' => url($api_url . '/content/' . urlencode($ctype) . '/' . $entity->id, array('absolute' => TRUE)),
  420. '@type' => $entity->name,
  421. 'label' => $entity->title,
  422. 'itemPage' => url('/bio_data/' . $entity->id, array('absolute' => TRUE)),
  423. );
  424. $i++;
  425. }
  426. // Lastly, add in the terms used into the @context section.
  427. $response['@context']['Collection'] = 'hydra:Collection';
  428. $response['@context']['totalItems'] = 'hydra:totalItems';
  429. $response['@context']['member'] = 'hydra:member';
  430. $response['@context']['label'] = 'rdfs:label';
  431. $response['@context']['itemPage'] = 'schema:itemPage';
  432. // $response['operation'][] = array(
  433. // '@type' => 'hydra:CreateResourceOperation',
  434. // 'hydra:method' => 'PUT'
  435. // );
  436. // $response['query'] = array(
  437. // '@id' => $response['@id'],
  438. // '@type' => 'IriTemplate',
  439. // "template" => $response['@id'] . "{?name,}",
  440. // "mapping" => array(
  441. // array(
  442. // "hydra:variable" => 'name',
  443. // "hydra:property" => 'name',
  444. // )
  445. // )
  446. // );
  447. }
  448. /**
  449. *
  450. * @param unknown $response
  451. * @param unknown $ws_path
  452. * @param unknown $ctype
  453. * @param unknown $entity_id
  454. * @param unknown $params
  455. */
  456. function tripal_ws_services_v0_1_get_content_add_fields($entity, $bundle, $api_url, &$response, $ws_path, $ctype, $entity_id, $params) {
  457. // Get information about the fields attached to this bundle and sort them
  458. // in the order they were set for the display.
  459. // TODO: should we allow for custom ordering of fields for web services
  460. // or use the default display ordering?
  461. $instances = field_info_instances('TripalEntity', $bundle->name);
  462. uasort($instances, function($a, $b) {
  463. $a_weight = (is_array($a) && isset($a['display']['default']['weight'])) ? $a['display']['default']['weight'] : 0;
  464. $b_weight = (is_array($b) && isset($b['display']['default']['weight'])) ? $b['display']['default']['weight'] : 0;
  465. if ($a_weight == $b_weight) {
  466. return 0;
  467. }
  468. return ($a_weight < $b_weight) ? -1 : 1;
  469. });
  470. // Iterate through the fields and add each value to the response.
  471. //$response['fields'] = $fields;
  472. foreach ($instances as $field_name => $instance) {
  473. // Ignore the content_type field provided by Tripal.
  474. if ($field_name == 'content_type') {
  475. continue;
  476. }
  477. // Skip hidden fields.
  478. if ($instance['display']['default']['type'] == 'hidden') {
  479. continue;
  480. }
  481. // Get the information about this field. It will have settings different
  482. // from the instance.
  483. $field = field_info_field($field_name);
  484. // By default, the label for the key in the output should be the
  485. // term from the vocabulary that the field is assigned. But in the
  486. // case that the field is not assigned a term, we must use the field name.
  487. $field_name = $instance['field_name'];
  488. $field_settings = $field['settings'];
  489. $key = $field_name;
  490. $key_adj = strtolower(preg_replace('/ /', '_', $key));
  491. if (array_key_exists('semantic_web', $field_settings) and $field_settings['semantic_web']) {
  492. list($vocabulary, $accession) = explode(':', $field_settings['semantic_web']);
  493. $term = tripal_get_term_details($vocabulary, $accession);
  494. if ($term) {
  495. $key = $term['name'];
  496. $key_adj = strtolower(preg_replace('/ /', '_', $key));
  497. $response['@context'][$key_adj] = array(
  498. '@id' => $term['url'],
  499. );
  500. }
  501. }
  502. // If this field should not be attached by default then just add a link
  503. // so that the caller can get the information separately.
  504. $instance_settings = $instance['settings'];
  505. if (array_key_exists('auto_attach', $instance_settings) and
  506. $instance_settings['auto_attach'] != TRUE) {
  507. $response['@context'][$key_adj]['@type'] = '@id';
  508. $response[$key_adj] = url($api_url . '/content/' . $ctype . '/' . $entity->id . '/' . urlencode($key), array('absolute' => TRUE));
  509. continue;
  510. }
  511. // Get the details for this field for the JSON-LD response.
  512. tripal_ws_services_v0_1_get_content_add_field($key_adj, $entity, $field, $instance, $api_url, $response);
  513. }
  514. // Lastly, add in the terms used into the @context section.
  515. $response['@context']['label'] = 'rdfs:label';
  516. $response['@context']['itemPage'] = 'schema:itemPage';
  517. // $response['operation'][] = array(
  518. // '@type' => 'hydra:DeleteResourceOperation',
  519. // 'hydra:method' => 'DELETE'
  520. // );
  521. // $response['operation'][] = array(
  522. // '@type' => 'hydra:ReplaceResourceOperation',
  523. // 'hydra:method' => 'POST'
  524. // );
  525. }
  526. /**
  527. *
  528. * @param unknown $field_arg
  529. * @param unknown $api_url
  530. * @param unknown $response
  531. * @param unknown $ws_path
  532. * @param unknown $ctype
  533. * @param unknown $entity_id
  534. * @param unknown $params
  535. */
  536. function tripal_ws_services_v0_1_get_content_find_field($field_arg, $ctype, $entity_id) {
  537. $bundle = tripal_load_bundle_entity(array('label' => $ctype));
  538. $entity = tripal_load_entity('TripalEntity', array('id' => $entity_id));
  539. $entity = reset($entity);
  540. $term = NULL;
  541. // Find the field whose term matches the one provied.
  542. $value = array();
  543. $instances = field_info_instances('TripalEntity', $bundle->name);
  544. foreach ($instances as $instance) {
  545. $field_name = $instance['field_name'];
  546. $field = field_info_field($field_name);
  547. $field_settings = $field['settings'];
  548. if (array_key_exists('semantic_web', $field_settings) and
  549. $field_settings['semantic_web']) {
  550. list($vocabulary, $accession) = explode(':', $field_settings['semantic_web']);
  551. $temp_term = tripal_get_term_details($vocabulary, $accession);
  552. if ($temp_term['name'] == $field_arg) {
  553. return array($entity, $bundle, $field, $instance, $temp_term);
  554. }
  555. }
  556. }
  557. }
  558. /**
  559. *
  560. * @param unknown $api_url
  561. * @param unknown $response
  562. * @param unknown $ws_path
  563. * @param unknown $ctype
  564. * @param unknown $entity_id
  565. * @param unknown $params
  566. * @return number
  567. */
  568. function tripal_ws_services_v0_1_get_content($api_url, &$response, $ws_path, $ctype, $entity_id, $params) {
  569. // First, add the vocabularies used into the @context section.
  570. $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
  571. $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
  572. // If we have an argument in the 4th element (3rd index) then the user
  573. // is requesting to epxand the details of a field that was not
  574. // initially attached to the enity.
  575. $field_arg = '';
  576. if (array_key_exists(3, $ws_path)) {
  577. $field_arg = urldecode($ws_path[3]);
  578. list($entity, $bundle, $field, $instance, $term) = tripal_ws_services_v0_1_get_content_find_field($field_arg, $ctype, $entity_id);
  579. // If we couldn't match this field argument to a field and entity then return
  580. if (!$entity or !$field) {
  581. return;
  582. }
  583. // Next add in the ID and Type for this resources.
  584. $key = $term['name'];
  585. $response['@context'][$key] = $term['url'];
  586. $response['@id'] = url($api_url . '/content/' . $ctype . '/' . $entity->id . '/' . urlencode($key), array('absolute' => TRUE));
  587. // Attach the field and then add it's values to the response.
  588. field_attach_load($entity->type, array($entity->id => $entity), FIELD_LOAD_CURRENT,
  589. array('field_id' => $field['id']));
  590. tripal_ws_services_v0_1_get_content_add_field($key, $entity, $field, $instance, $api_url, $response, TRUE);
  591. return;
  592. }
  593. // If we don't have a 4th argument then we're loading the base record.
  594. // Get the TripalBundle, TripalTerm and TripalVocab type for this type.
  595. $bundle = tripal_load_bundle_entity(array('label' => $ctype));
  596. $term = entity_load('TripalTerm', array('id' => $bundle->term_id));
  597. $term = reset($term);
  598. $vocab = $term->vocab;
  599. // Add the vocabulary for this content type to the @context section.
  600. if (!array_key_exists($vocab->vocabulary, $response['@context'])) {
  601. // If there is no URL prefix then use this API's vocabulary API
  602. if (property_exists($term, 'urlprefix')) {
  603. $response['@context'][$vocab->vocabulary] = $term->urlprefix;
  604. }
  605. else {
  606. $response['@context'][$vocab->vocabulary] = url($api_url . '/vocab/' . $vocab->vocabulary . '/', array('absolute' => TRUE));
  607. }
  608. }
  609. // Get the TripalEntity
  610. $entity = tripal_load_entity('TripalEntity', array('id' => $entity_id));
  611. $entity = reset($entity);
  612. // Next add in the ID and Type for this resources.
  613. $response['@id'] = url($api_url . '/content/' . $ctype . '/' . $entity_id, array('absolute' => TRUE));
  614. $response['@type'] = $term->name;
  615. $response['@context'][$term->name] = $term->url;
  616. $response['label'] = $entity->title;
  617. $response['itemPage'] = url('/bio_data/' . $entity->id, array('absolute' => TRUE));
  618. tripal_ws_services_v0_1_get_content_add_fields($entity, $bundle, $api_url, $response, $ws_path, $ctype, $entity_id, $params);
  619. //tripal_ws_services_v0_1_write_context($response, $ctype);
  620. }
  621. /**
  622. *
  623. * @param $response
  624. * @param $ctype
  625. */
  626. function tripal_ws_services_v0_1_write_context(&$response, $ctype) {
  627. // We want to write the context to a file. Try to open the lock file. if we
  628. // can't then just leave the context in the response.
  629. $context_uri = file_build_uri("ws.v0_1.$ctype.context.jsonld");
  630. $context_file = drupal_realpath($context_uri);
  631. $context_URL = file_create_url($context_uri);
  632. $lock_fp = fopen("$context_file.lock", "w");
  633. if (flock($lock_fp, LOCK_EX|LOCK_NB)) {
  634. $fp = fopen("$context_file", "w");
  635. if ($fp) {
  636. $context = array('@context' => $response['@context']);
  637. fwrite($fp, json_encode($context));
  638. fflush($fp);
  639. // Release the lock.
  640. flock($lock_fp, LOCK_UN);
  641. // Close the files.
  642. fclose($lock_fp);
  643. fclose($fp);
  644. // Change the context to point to the file
  645. $response['@context'] = $context_URL;
  646. }
  647. }
  648. }
  649. /**
  650. *
  651. */
  652. function tripal_ws_services_v0_1_get_content_add_field($key, $entity, $field, $instance, $api_url, &$response, $is_field_page = NULL) {
  653. // Get the field settings.
  654. $field_name = $field['field_name'];
  655. $field_settings = $field['settings'];
  656. $items = field_get_items('TripalEntity', $entity, $field_name);
  657. if (!$items) {
  658. return;
  659. }
  660. $values = array();
  661. for ($i = 0; $i < count($items); $i++) {
  662. // See if the keys in the values array have been mapped to semantic
  663. // web terms.
  664. if (array_key_exists('semantic_web', $items[$i])) {
  665. foreach ($items[$i]['semantic_web'] as $k => $v) {
  666. $matches = array();
  667. if (preg_match('/^(.+):(.+)$/', $v, $matches)) {
  668. $vocabulary = $matches[1];
  669. $accession = $matches[2];
  670. $term = tripal_get_term_details($vocabulary, $accession);
  671. if ($k == 'type') {
  672. $items[$i]['value']['@type'] = $items[$i]['value']['type'];
  673. unset($items[$i]['value']['type']);
  674. $response['@context'][$term['name']] = $term['url'];
  675. }
  676. else {
  677. $response['@context'][$k] = $term['url'];
  678. }
  679. }
  680. }
  681. }
  682. // Recurse through the values array and set the entity elemtns
  683. // and add the fields to the context.
  684. tripal_ws_services_v0_1_get_content_add_field_context($items[$i], $response, $api_url);
  685. // Add the remaining values to the $values array.
  686. $values[] = $items[$i]['value'];
  687. }
  688. // If we only have one value then set the response with just the value.
  689. if (count($values) == 1) {
  690. // If the value is an array and this is the field page then all of those
  691. // key/value pairs should be added directly to the response.
  692. if (is_array($values[0])) {
  693. if ($is_field_page) {
  694. foreach ($values[0] as $k => $v) {
  695. $response[$k] = $v;
  696. }
  697. }
  698. else {
  699. $response[$key] = $values[0];
  700. }
  701. }
  702. // If the value is not an array it's a scalar so add it as is to the
  703. // response.
  704. else {
  705. $response[$key] = $values[0];
  706. }
  707. }
  708. // If we have more than one value then set the response to be a collection.
  709. if (count($values) > 1) {
  710. // If this is the field page then the Collection is added directly to the
  711. // response, otherwise, it's added under the field $key.
  712. if ($is_field_page) {
  713. $response['@type'] = 'Collection';
  714. $response['totalItems'] = count($values);
  715. $response['label'] = $instance['label'];
  716. $response['member'] = $values;
  717. }
  718. else {
  719. $response[$key] = array(
  720. '@type' => 'Collection',
  721. 'totalItems' => count($values),
  722. 'label' => $instance['label'],
  723. 'member' => $values,
  724. );
  725. }
  726. }
  727. }
  728. /**
  729. *
  730. */
  731. function tripal_ws_services_v0_1_get_content_add_field_context(&$items, &$response, $api_url) {
  732. foreach ($items as $key => $value) {
  733. if (is_array($value)) {
  734. tripal_ws_services_v0_1_get_content_add_field_context($items[$key], $response, $api_url);
  735. continue;
  736. }
  737. if ($key == 'entity') {
  738. list($item_etype, $item_eid) = explode(':', $items['entity']);
  739. if ($item_eid) {
  740. $item_entity = tripal_load_entity($item_etype, array($item_eid));
  741. $item_entity = reset($item_entity);
  742. $item_term = tripal_load_term_entity(array('term_id' => $item_entity->term_id));
  743. $items['@id'] = url($api_url . '/content/' . $item_term->name . '/' . $item_eid, array('absolute' => TRUE));
  744. }
  745. unset($items['entity']);
  746. }
  747. }
  748. }
  749. /**
  750. * Provides the Hydra compatible apiDocumentation page that describes this API.
  751. *
  752. * @param $api_url
  753. * @param $response
  754. */
  755. function tripal_ws_services_v0_1_handle_doc_service($api_url, &$response) {
  756. // First, add the vocabularies used into the @context section.
  757. $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
  758. $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
  759. // Next add in the ID for tihs resource.
  760. $site_name = variable_get('site_name', '');
  761. $response['@id'] = url($api_url . '/doc/', array('absolute' => TRUE));
  762. $response['title'] = $site_name . ": RESTful Web Services API";
  763. $response['entrypoint'] = url($api_url, array('absolute' => TRUE));
  764. $response['description'] = "A fully queryable REST API using JSON-LD and " .
  765. "discoverable using the WC3 Hydra specification.";
  766. // Lastly, add in the terms used into the @context section.
  767. $response['@context']['title'] = 'hydra:title';
  768. $response['@context']['entrypoint'] = array(
  769. "@id" => "hydra:entrypoint",
  770. "@type" => "@id",
  771. );
  772. $response['@context']['description'] = 'hydra:description';
  773. }
  774. /**
  775. * This function specifies the types of resources avaiable via the API.
  776. *
  777. * @param $api_url
  778. * @param $response
  779. * @param $ws_path
  780. */
  781. function tripal_ws_services_v0_1_handle_no_service($api_url, &$response) {
  782. // First, add the vocabularies used into the @context section.
  783. $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
  784. $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
  785. $response['@context']['dc'] = 'http://purl.org/dc/dcmitype/';
  786. $response['@context']['schema'] = 'https://schema.org/';
  787. // Next add in the ID for tihs resource.
  788. $response['@id'] = url($api_url, array('absolute' => TRUE));
  789. // Start the list.
  790. $response['@type'] = 'Collection';
  791. $response['totalItems'] = 0;
  792. $response['label'] = 'Services';
  793. $response['member'] = array();
  794. // Start the list.
  795. $response['member'][] = array(
  796. '@id' => url($api_url . '/content/', array('absolute' => TRUE)),
  797. '@type' => 'Service',
  798. 'label' => 'Content Types',
  799. 'description' => 'Provides acesss to the biological and ' .
  800. 'ancilliary data available on this site. Each content type ' .
  801. 'represents biological data that is defined in a controlled vocabulary '.
  802. '(e.g. Sequence Ontology term: gene (SO:0000704)).',
  803. );
  804. $response['member'][] = array(
  805. '@id' => url($api_url . '/doc/', array('absolute' => TRUE)),
  806. '@type' => 'Service',
  807. 'label' => 'API Documentation',
  808. 'description' => 'The WC3 Hydra compatible documentation for this API.',
  809. );
  810. $response['member'][] = array(
  811. '@id' => url($api_url . '/vocab/', array('absolute' => TRUE)),
  812. '@type' => 'Service',
  813. 'label' => 'Vocabulary',
  814. 'description' => 'Defines in-house locally defined vocabulary terms that ' .
  815. 'have been added specifically for this site. These terms are typically ' .
  816. 'added because no other appropriate term exists in another community-vetted '.
  817. 'controlled vocabulary.',
  818. );
  819. $response['totalItems'] = count($response['member']);
  820. $response['@context']['Collection'] = 'hydra:Collection';
  821. $response['@context']['totalItems'] = 'hydra:totalItems';
  822. $response['@context']['member'] = 'hydra:member';
  823. $response['@context']['Service'] = 'dc:Service';
  824. $response['@context']['label'] = 'rdfs:label';
  825. $response['@context']['description'] = 'hydra:description';
  826. }