tripal_ws.rest_v0.1.inc 36 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048
  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. $order = array();
  302. $order_dir = array();
  303. $URL_add = array();
  304. foreach ($params as $param => $value) {
  305. $URL_add[] = "$param=$value";
  306. // Ignore non filter parameters
  307. if ($param == 'page' or $param == 'limit') {
  308. continue;
  309. }
  310. // Handle order separately
  311. if ($param == 'order') {
  312. $temp = explode(',', $value);
  313. foreach ($temp as $key) {
  314. $matches = array();
  315. $dir = 'ASC';
  316. // The user can provide a direction by separating the field key and the
  317. // direction with a '|' character.
  318. if (preg_match('/^(.*)\|(.*)$/', $key, $matches)) {
  319. $key = $matches[1];
  320. if ($matches[2] == 'ASC' or $matches[2] == 'DESC') {
  321. $dir = $matches[2];
  322. }
  323. else {
  324. // TODO: handle error of providing an incorrect direction.
  325. }
  326. }
  327. if (array_key_exists($key, $field_mapping)) {
  328. $order[$field_mapping[$key]] = $key;
  329. $order_dir[] = $dir;
  330. }
  331. else {
  332. // TODO: handle error of providing a non existing field name.
  333. }
  334. }
  335. continue;
  336. }
  337. // Break apart any operators
  338. $key = $param;
  339. $op = '=';
  340. $matches = array();
  341. if (preg_match('/^(.+);(.+)$/', $key, $matches)) {
  342. $key = $matches[1];
  343. $op = $matches[2];
  344. }
  345. // Break apart any subkeys and pull the first one out for the term name key.
  346. $subkeys = explode(',', $key);
  347. if (count($subkeys) > 0) {
  348. $key = array_shift($subkeys);
  349. }
  350. $column_name = $key;
  351. // Map the values in the filters to their appropriate field names.
  352. if (array_key_exists($key, $field_mapping)) {
  353. $field_name = $field_mapping[$key];
  354. if (count($subkeys) > 0) {
  355. $column_name .= '.' . implode('.', $subkeys);
  356. }
  357. $new_params[$field_name]['value'] = $value;
  358. $new_params[$field_name]['op'] = $op;
  359. }
  360. else {
  361. throw new Exception("The filter term, '$key', is not available for use.");
  362. }
  363. }
  364. // Get the list of entities for this bundle.
  365. $query = new TripalFieldQuery();
  366. $query->entityCondition('entity_type', 'TripalEntity');
  367. $query->entityCondition('bundle', $bundle->name);
  368. foreach($new_params as $field_name => $details) {
  369. $value = $details['value'];
  370. switch ($details['op']) {
  371. case 'eq':
  372. $op = '=';
  373. break;
  374. case 'gt':
  375. $op = '>';
  376. break;
  377. case 'gte':
  378. $op = '>=';
  379. break;
  380. case 'lt':
  381. $op = '<';
  382. break;
  383. case 'lte':
  384. $op = '<=';
  385. break;
  386. case 'ne':
  387. $op = '<>';
  388. break;
  389. case 'contains':
  390. $op = 'CONTAINS';
  391. break;
  392. case 'starts':
  393. $op = 'STARTS WITH';
  394. break;
  395. default:
  396. $op = '=';
  397. }
  398. // We pass in the $column_name as an identifier for any sub fields
  399. // that are present for the fields.
  400. $query->fieldCondition($field_name, $column_name, $value, $op);
  401. }
  402. // Perform the query just as a count first to get the number of records.
  403. $cquery = clone $query;
  404. $cquery->count();
  405. $num_records = $cquery->execute();
  406. $num_records = count($num_records['TripalEntity']);
  407. if (!$num_records) {
  408. $num_records = 0;
  409. }
  410. // Add in the pager to the response.
  411. $response['totalItems'] = $num_records;
  412. $limit = array_key_exists('limit', $params) ? $params['limit'] : 25;
  413. $total_pages = ceil($num_records / $limit);
  414. $page = array_key_exists('page', $params) ? $params['page'] : 1;
  415. if ($num_records > 0) {
  416. $response['view'] = array(
  417. '@id' => $URL . '?' . implode('&', array_merge($URL_add, array("page=$page", "limit=$limit"))),
  418. '@type' => 'PartialCollectionView',
  419. 'first' => $URL . '?' . implode('&', array_merge($URL_add, array("page=1", "limit=$limit"))),
  420. 'last' => $URL . '?' . implode('&', array_merge($URL_add, array("page=$total_pages", "limit=$limit"))),
  421. );
  422. $prev = $page - 1;
  423. $next = $page + 1;
  424. if ($prev > 0) {
  425. $response['view']['previous'] = $URL . '?' . implode('&', array_merge($URL_add, array("page=$prev", "limit=$limit")));
  426. }
  427. if ($next < $total_pages) {
  428. $response['view']['next'] = $URL . '?' . implode('&', array_merge($URL_add, array("page=$next", "limit=$limit")));
  429. }
  430. }
  431. // Set the query order
  432. $order_keys = array_keys($order);
  433. for($i = 0; $i < count($order_keys); $i++) {
  434. $query->fieldOrderBy($order_keys[$i], $order[$order_keys[$i]], $order_dir[$i]);
  435. }
  436. // Set the query range
  437. $start = ($page - 1) * $limit;
  438. $query->range($start, $limit);
  439. // Now perform the query.
  440. $results = $query->execute();
  441. // Iterate through the entities and add them to the list.
  442. $i = 0;
  443. foreach ($results['TripalEntity'] as $entity_id => $stub) {
  444. $vocabulary = '';
  445. $term_name = '';
  446. // We don't need all of the attached fields for an entity so, we'll
  447. // not use the entity_load() function. Instead just pull it from the
  448. // database table.
  449. $query = db_select('tripal_entity', 'TE');
  450. $query->join('tripal_term', 'TT', 'TE.term_id = TT.id');
  451. $query->fields('TE');
  452. $query->fields('TT', array('name'));
  453. $query->condition('TE.id', $entity_id);
  454. $entity = $query->execute()->fetchObject();
  455. //$entity = tripal_load_entity('TripalEntity', array($entity->id));
  456. $response['member'][] = array(
  457. '@id' => url($api_url . '/content/' . urlencode($ctype) . '/' . $entity->id, array('absolute' => TRUE)),
  458. '@type' => $entity->name,
  459. 'label' => $entity->title,
  460. 'itemPage' => url('/bio_data/' . $entity->id, array('absolute' => TRUE)),
  461. );
  462. $i++;
  463. }
  464. // Lastly, add in the terms used into the @context section.
  465. $response['@context']['Collection'] = 'hydra:Collection';
  466. $response['@context']['totalItems'] = 'hydra:totalItems';
  467. $response['@context']['member'] = 'hydra:member';
  468. $response['@context']['label'] = 'rdfs:label';
  469. $response['@context']['itemPage'] = 'schema:itemPage';
  470. // $response['operation'][] = array(
  471. // '@type' => 'hydra:CreateResourceOperation',
  472. // 'hydra:method' => 'PUT'
  473. // );
  474. // $response['query'] = array(
  475. // '@id' => $response['@id'],
  476. // '@type' => 'IriTemplate',
  477. // "template" => $response['@id'] . "{?name,}",
  478. // "mapping" => array(
  479. // array(
  480. // "hydra:variable" => 'name',
  481. // "hydra:property" => 'name',
  482. // )
  483. // )
  484. // );
  485. }
  486. /**
  487. *
  488. * @param unknown $response
  489. * @param unknown $ws_path
  490. * @param unknown $ctype
  491. * @param unknown $entity_id
  492. * @param unknown $params
  493. */
  494. function tripal_ws_services_v0_1_get_content_add_fields($entity, $bundle, $api_url, &$response, $ws_path, $ctype, $entity_id, $params) {
  495. // Get information about the fields attached to this bundle and sort them
  496. // in the order they were set for the display.
  497. $instances = field_info_instances('TripalEntity', $bundle->name);
  498. uasort($instances, function($a, $b) {
  499. $a_weight = (is_array($a) && isset($a['widget']['weight'])) ? $a['widget']['weight'] : 0;
  500. $b_weight = (is_array($b) && isset($b['widget']['weight'])) ? $b['widget']['weight'] : 0;
  501. if ($a_weight == $b_weight) {
  502. return 0;
  503. }
  504. return ($a_weight < $b_weight) ? -1 : 1;
  505. });
  506. // Iterate through the fields and add each value to the response.
  507. //$response['fields'] = $fields;
  508. foreach ($instances as $field_name => $instance) {
  509. // Ignore the content_type field provided by Tripal.
  510. if ($field_name == 'content_type') {
  511. continue;
  512. }
  513. // Skip hidden fields.
  514. if ($instance['display']['default']['type'] == 'hidden') {
  515. continue;
  516. }
  517. // Get the information about this field. It will have settings different
  518. // from the instance.
  519. $field = field_info_field($field_name);
  520. // By default, the label for the key in the output should be the
  521. // term from the vocabulary that the field is assigned. But in the
  522. // case that the field is not assigned a term, we must use the field name.
  523. $field_name = $instance['field_name'];
  524. $vocabulary = $instance['settings']['term_vocabulary'];
  525. $accession = $instance['settings']['term_accession'];
  526. $term = tripal_get_term_details($vocabulary, $accession);
  527. if ($term) {
  528. $key = $term['name'];
  529. $key_adj = strtolower(preg_replace('/ /', '_', $key));
  530. // The term schema:url also points to a recource so we need
  531. // to make sure we set the type to be '@id'.
  532. if ($vocabulary == 'schema' and $accession == 'url') {
  533. $response['@context'][$key_adj] = array(
  534. '@id' => $term['url'],
  535. '@type' => '@id',
  536. );
  537. }
  538. else {
  539. $response['@context'][$key_adj] = $term['url'];
  540. }
  541. }
  542. else {
  543. continue;
  544. }
  545. // If this field should not be attached by default then just add a link
  546. // so that the caller can get the information separately.
  547. $instance_settings = $instance['settings'];
  548. if (array_key_exists('auto_attach', $instance['settings']) and
  549. $instance_settings['auto_attach'] == FALSE) {
  550. $response['@context'][$key_adj] = array(
  551. '@id' => $response['@context'][$key_adj],
  552. '@type' => '@id'
  553. );
  554. // Add a URL only if there are values. If there are no values then
  555. // don't add a URL which would make the end-user think they can get
  556. // that information.
  557. $items = field_get_items('TripalEntity', $entity, $field_name);
  558. if ($items and count($items) > 0 and $items[0]['value']) {
  559. $response[$key_adj] = url($api_url . '/content/' . $ctype . '/' . $entity->id . '/' . urlencode($key), array('absolute' => TRUE));
  560. }
  561. else {
  562. $response[$key_adj] = NULL;
  563. }
  564. continue;
  565. }
  566. // Get the details for this field for the JSON-LD response.
  567. tripal_ws_services_v0_1_get_content_add_field($key_adj, $entity, $field, $instance, $api_url, $response);
  568. }
  569. // Lastly, add in the terms used into the @context section.
  570. $response['@context']['label'] = 'https://www.w3.org/TR/rdf-schema/#ch_label';
  571. $response['@context']['itemPage'] = 'https://schema.org/ItemPage';
  572. // $response['operation'][] = array(
  573. // '@type' => 'hydra:DeleteResourceOperation',
  574. // 'hydra:method' => 'DELETE'
  575. // );
  576. // $response['operation'][] = array(
  577. // '@type' => 'hydra:ReplaceResourceOperation',
  578. // 'hydra:method' => 'POST'
  579. // );
  580. }
  581. /**
  582. *
  583. * @param unknown $field_arg
  584. * @param unknown $api_url
  585. * @param unknown $response
  586. * @param unknown $ws_path
  587. * @param unknown $ctype
  588. * @param unknown $entity_id
  589. * @param unknown $params
  590. */
  591. function tripal_ws_services_v0_1_get_content_find_field($field_arg, $ctype, $entity_id) {
  592. $bundle = tripal_load_bundle_entity(array('label' => $ctype));
  593. $entity = tripal_load_entity('TripalEntity', array('id' => $entity_id));
  594. $entity = reset($entity);
  595. $term = NULL;
  596. // Find the field whose term matches the one provied.
  597. $value = array();
  598. $instances = field_info_instances('TripalEntity', $bundle->name);
  599. foreach ($instances as $instance) {
  600. $field_name = $instance['field_name'];
  601. $field = field_info_field($field_name);
  602. $vocabulary = $instance['settings']['term_vocabulary'];
  603. $accession = $instance['settings']['term_accession'];
  604. $temp_term = tripal_get_term_details($vocabulary, $accession);
  605. if ($temp_term['name'] == $field_arg) {
  606. return array($entity, $bundle, $field, $instance, $temp_term);
  607. }
  608. }
  609. }
  610. /**
  611. *
  612. * @param unknown $api_url
  613. * @param unknown $response
  614. * @param unknown $ws_path
  615. * @param unknown $ctype
  616. * @param unknown $entity_id
  617. * @param unknown $params
  618. * @return number
  619. */
  620. function tripal_ws_services_v0_1_get_content($api_url, &$response, $ws_path, $ctype, $entity_id, $params) {
  621. // First, add the vocabularies used into the @context section.
  622. $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
  623. $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
  624. // If we have an argument in the 4th element (3rd index) then the user
  625. // is requesting to expand the details of a field that was not
  626. // initially attached to the enity.
  627. $field_arg = '';
  628. if (array_key_exists(3, $ws_path)) {
  629. $field_arg = urldecode($ws_path[3]);
  630. list($entity, $bundle, $field, $instance, $term) = tripal_ws_services_v0_1_get_content_find_field($field_arg, $ctype, $entity_id);
  631. // If we couldn't match this field argument to a field and entity then return
  632. if (!$entity or !$field) {
  633. return;
  634. }
  635. // Next add in the ID and Type for this resources.
  636. $key = $term['name'];
  637. $key_adj = strtolower(preg_replace('/ /', '_', $term['name']));
  638. $response['@context'][$key_adj] = $term['url'];
  639. $response['@id'] = url($api_url . '/content/' . $ctype . '/' . $entity->id . '/' . urlencode($key), array('absolute' => TRUE));
  640. // Attach the field and then add it's values to the response.
  641. field_attach_load($entity->type, array($entity->id => $entity),
  642. FIELD_LOAD_CURRENT, array('field_id' => $field['id']));
  643. tripal_ws_services_v0_1_get_content_add_field($key_adj, $entity, $field, $instance, $api_url, $response, TRUE);
  644. tripal_ws_services_v0_1_write_context($response, $ctype);
  645. return;
  646. }
  647. // If we don't have a 4th argument then we're loading the base record.
  648. // Get the TripalBundle, TripalTerm and TripalVocab type for this type.
  649. $bundle = tripal_load_bundle_entity(array('label' => $ctype));
  650. $term = entity_load('TripalTerm', array('id' => $bundle->term_id));
  651. $term = reset($term);
  652. $vocab = $term->vocab;
  653. // Add the vocabulary for this content type to the @context section.
  654. if (!array_key_exists($vocab->vocabulary, $response['@context'])) {
  655. // If there is no URL prefix then use this API's vocabulary API
  656. if (property_exists($term, 'urlprefix')) {
  657. $response['@context'][$vocab->vocabulary] = $term->urlprefix;
  658. }
  659. else {
  660. $response['@context'][$vocab->vocabulary] = url($api_url . '/vocab/' . $vocab->vocabulary . '/', array('absolute' => TRUE));
  661. }
  662. }
  663. // Get the TripalEntity
  664. $entity = tripal_load_entity('TripalEntity', array('id' => $entity_id));
  665. $entity = reset($entity);
  666. // Next add in the ID and Type for this resources.
  667. $response['@id'] = url($api_url . '/content/' . $ctype . '/' . $entity_id, array('absolute' => TRUE));
  668. $response['@type'] = $term->name;
  669. $response['@context'][$term->name] = $term->url;
  670. $response['label'] = $entity->title;
  671. $response['itemPage'] = url('/bio_data/' . $entity->id, array('absolute' => TRUE));
  672. tripal_ws_services_v0_1_get_content_add_fields($entity, $bundle, $api_url, $response, $ws_path, $ctype, $entity_id, $params);
  673. tripal_ws_services_v0_1_write_context($response, $ctype);
  674. }
  675. /**
  676. *
  677. * @param $response
  678. * @param $ctype
  679. */
  680. function tripal_ws_services_v0_1_write_context(&$response, $ctype) {
  681. // Save the response '@context' into a temporary file
  682. $context = array('@context' => $response['@context']);
  683. $file_name = drupal_tempnam(file_default_scheme() . '://', 'tws_context-') . '.json';
  684. $context_file = file_save_data(json_encode($context), $file_name, FILE_EXISTS_REPLACE );
  685. // Mark the file as temporary by setting it's status
  686. $context_file->status = 0;
  687. file_save($context_file);
  688. // Return the response with the '@context' section replaced with the file URL.
  689. $response['@context'] = file_create_url($context_file->uri);
  690. }
  691. /**
  692. *
  693. */
  694. function tripal_ws_services_v0_1_get_content_add_field($key, $entity, $field, $instance, $api_url, &$response, $is_field_page = NULL) {
  695. // Get the field settings.
  696. $field_name = $field['field_name'];
  697. $field_settings = $field['settings'];
  698. $items = field_get_items('TripalEntity', $entity, $field_name);
  699. if (!$items) {
  700. return;
  701. }
  702. // Give modules the opportunity to edit values for web services. This hook
  703. // really should be used sparingly. Where it helps is with non Tripal fields
  704. // that are added to a TripalEntity content type and it doesn't follow
  705. // the rules (e.g. Image field).
  706. drupal_alter('tripal_ws_value', $items, $field, $instance);
  707. $values = array();
  708. for ($i = 0; $i < count($items); $i++) {
  709. $values[$i] = tripal_ws_services_v0_1_rewrite_field_items_keys($items[$i]['value'], $response, $api_url);
  710. }
  711. // Add the $values array to the WS response.
  712. // If we only have one value then set the response with just the value.
  713. if (count($values) == 1) {
  714. // If the value is an array and this is the field page then all of those
  715. // key/value pairs should be added directly to the response.
  716. if (is_array($values[0])) {
  717. if ($is_field_page) {
  718. foreach ($values[0] as $k => $v) {
  719. $response[$k] = $v;
  720. }
  721. }
  722. else {
  723. $response[$key] = $values[0];
  724. }
  725. }
  726. // If the value is not an array it's a scalar so add it as is to the
  727. // response.
  728. else {
  729. $response[$key] = $values[0];
  730. }
  731. }
  732. // If we have more than one value then set the response to be a collection.
  733. if (count($values) > 1) {
  734. // If this is the field page then the Collection is added directly to the
  735. // response, otherwise, it's added under the field $key.
  736. if ($is_field_page) {
  737. $response['@type'] = 'Collection';
  738. $response['totalItems'] = count($values);
  739. $response['label'] = $instance['label'];
  740. $response['member'] = $values;
  741. }
  742. else {
  743. $response[$key] = array(
  744. '@type' => 'Collection',
  745. 'totalItems' => count($values),
  746. 'label' => $instance['label'],
  747. 'member' => $values,
  748. );
  749. }
  750. }
  751. }
  752. /**
  753. *
  754. */
  755. function tripal_ws_services_v0_1_rewrite_field_items_keys($value, &$response, $api_url) {
  756. $new_value = '';
  757. // If the value is an array rather than a scalar then map the sub elements
  758. // to controlled vocabulary terms.
  759. if (is_array($value)) {
  760. $temp = array();
  761. foreach ($value as $k => $v) {
  762. $matches = array();
  763. if (preg_match('/^(.+):(.+)$/', $k, $matches)) {
  764. $vocabulary = $matches[1];
  765. $accession = $matches[2];
  766. $term = tripal_get_term_details($vocabulary, $accession);
  767. $key_adj = strtolower(preg_replace('/ /', '_', $term['name']));
  768. if (is_array($v)) {
  769. $temp[$key_adj] = tripal_ws_services_v0_1_rewrite_field_items_keys($v, $response, $api_url);
  770. }
  771. else {
  772. $temp[$key_adj] = $v !== "" ? $v : NULL;
  773. }
  774. // The term schema:url also points to a recource so we need
  775. // to make sure we set the type to be '@id'.
  776. if ($vocabulary == 'schema' and $accession == 'url') {
  777. $response['@context'][$key_adj] = array(
  778. '@id' => $term['url'],
  779. '@type' => '@id',
  780. );
  781. }
  782. else {
  783. $response['@context'][$key_adj] = $term['url'];
  784. }
  785. }
  786. else {
  787. $temp[$k] = $v;
  788. }
  789. }
  790. $new_value = $temp;
  791. // Recurse through the values array and set the entity elements
  792. // and add the fields to the context.
  793. tripal_ws_services_v0_1_rewrite_field_items_entity($new_value, $response, $api_url);
  794. }
  795. else {
  796. $new_value = $value !== "" ? $value : NULL;
  797. }
  798. return $new_value;
  799. }
  800. /**
  801. *
  802. */
  803. function tripal_ws_services_v0_1_rewrite_field_items_entity(&$items, &$response, $api_url) {
  804. if (!$items) {
  805. return;
  806. }
  807. foreach ($items as $key => $value) {
  808. if (is_array($value)) {
  809. tripal_ws_services_v0_1_rewrite_field_items_entity($items[$key], $response, $api_url);
  810. continue;
  811. }
  812. if ($key == 'entity') {
  813. list($item_etype, $item_eid) = explode(':', $items['entity']);
  814. if ($item_eid) {
  815. $item_entity = tripal_load_entity($item_etype, array($item_eid));
  816. $item_entity = reset($item_entity);
  817. $item_term = tripal_load_term_entity(array('term_id' => $item_entity->term_id));
  818. $items['@id'] = url($api_url . '/content/' . $item_term->name . '/' . $item_eid, array('absolute' => TRUE));
  819. }
  820. unset($items['entity']);
  821. }
  822. }
  823. }
  824. /**
  825. * Provides the Hydra compatible apiDocumentation page that describes this API.
  826. *
  827. * @param $api_url
  828. * @param $response
  829. */
  830. function tripal_ws_services_v0_1_handle_doc_service($api_url, &$response) {
  831. // First, add the vocabularies used into the @context section.
  832. $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
  833. $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
  834. // Next add in the ID for tihs resource.
  835. $site_name = variable_get('site_name', '');
  836. $response['@id'] = url($api_url . '/doc/', array('absolute' => TRUE));
  837. $response['title'] = $site_name . ": RESTful Web Services API";
  838. $response['entrypoint'] = url($api_url, array('absolute' => TRUE));
  839. $response['description'] = "A fully queryable REST API using JSON-LD and " .
  840. "discoverable using the WC3 Hydra specification.";
  841. // Lastly, add in the terms used into the @context section.
  842. $response['@context']['title'] = 'hydra:title';
  843. $response['@context']['entrypoint'] = array(
  844. "@id" => "hydra:entrypoint",
  845. "@type" => "@id",
  846. );
  847. $response['@context']['description'] = 'hydra:description';
  848. }
  849. /**
  850. * This function specifies the types of resources avaiable via the API.
  851. *
  852. * @param $api_url
  853. * @param $response
  854. * @param $ws_path
  855. */
  856. function tripal_ws_services_v0_1_handle_no_service($api_url, &$response) {
  857. // First, add the vocabularies used into the @context section.
  858. $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
  859. $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
  860. $response['@context']['dc'] = 'http://purl.org/dc/dcmitype/';
  861. $response['@context']['schema'] = 'https://schema.org/';
  862. // Next add in the ID for tihs resource.
  863. $response['@id'] = url($api_url, array('absolute' => TRUE));
  864. // Start the list.
  865. $response['@type'] = 'Collection';
  866. $response['totalItems'] = 0;
  867. $response['label'] = 'Services';
  868. $response['member'] = array();
  869. // Start the list.
  870. $response['member'][] = array(
  871. '@id' => url($api_url . '/content/', array('absolute' => TRUE)),
  872. '@type' => 'Service',
  873. 'label' => 'Content Types',
  874. 'description' => 'Provides acesss to the biological and ' .
  875. 'ancilliary data available on this site. Each content type ' .
  876. 'represents biological data that is defined in a controlled vocabulary '.
  877. '(e.g. Sequence Ontology term: gene (SO:0000704)).',
  878. );
  879. $response['member'][] = array(
  880. '@id' => url($api_url . '/doc/', array('absolute' => TRUE)),
  881. '@type' => 'Service',
  882. 'label' => 'API Documentation',
  883. 'description' => 'The WC3 Hydra compatible documentation for this API.',
  884. );
  885. $response['member'][] = array(
  886. '@id' => url($api_url . '/vocab/', array('absolute' => TRUE)),
  887. '@type' => 'Service',
  888. 'label' => 'Vocabulary',
  889. 'description' => 'Defines in-house locally defined vocabulary terms that ' .
  890. 'have been added specifically for this site. These terms are typically ' .
  891. 'added because no other appropriate term exists in another community-vetted '.
  892. 'controlled vocabulary.',
  893. );
  894. $response['totalItems'] = count($response['member']);
  895. $response['@context']['Collection'] = 'hydra:Collection';
  896. $response['@context']['totalItems'] = 'hydra:totalItems';
  897. $response['@context']['member'] = 'hydra:member';
  898. $response['@context']['Service'] = 'dc:Service';
  899. $response['@context']['label'] = 'rdfs:label';
  900. $response['@context']['description'] = 'hydra:description';
  901. }
  902. /**
  903. * Implements hook_tripal_ws_value_alter().
  904. *
  905. * The hook_tripal_ws_value_alter is a hook created by the Tripal WS module.
  906. * It allows the modules to adjust the values of a field for display in
  907. * web services. This hook should be used sparingly. It is meant primarily
  908. * to adjust 3rd Party (non Tripal) fields so that they work with web
  909. * services.
  910. */
  911. function tripal_ws_tripal_ws_value_alter(&$items, $field, $instance) {
  912. // The image module doesn't properly set the 'value' field, so we'll do it
  913. // here.
  914. if($field['type'] == 'image' and $field['module'] == 'image') {
  915. foreach ($items as $delta => $details) {
  916. if ($items[$delta] and array_key_exists('uri', $items[$delta])) {
  917. $items[$delta]['value']['schema:url'] = file_create_url($items[$delta]['uri']);
  918. }
  919. }
  920. }
  921. }