TripalWebService.inc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741
  1. <?php
  2. class TripalWebService {
  3. // --------------------------------------------------------------------------
  4. // EDITABLE STATIC CONSTANTS
  5. //
  6. // The following constants SHOULD be set for each descendent class. They are
  7. // used by the static functions to provide information to Drupal about
  8. // the field and it's default widget and formatter.
  9. // --------------------------------------------------------------------------
  10. /**
  11. * The human-readable label for this web service.
  12. */
  13. public static $label = 'Base WebService';
  14. /**
  15. * A bit of text to describe what this service provides.
  16. */
  17. public static $description = 'This is the base class for Tripal web services as is not meant to be used on it\'s own';
  18. /**
  19. * A machine-readable type for this service. This name must be unique
  20. * among all Tripal web services and is used to form the URL to access
  21. * this service.
  22. */
  23. public static $type = 'services';
  24. // --------------------------------------------------------------------------
  25. // PROTECTED CLASS MEMBERS -- DO NOT OVERRIDE
  26. // --------------------------------------------------------------------------
  27. /**
  28. * The resource that will be returned by the webservice given the
  29. * arguments provided. This is a private
  30. */
  31. protected $resource;
  32. /**
  33. * An array containing the elements of the URL path. Each level of the
  34. * URL appears in a separate element of the array. The service type and
  35. * version are automatically removed from the array.
  36. */
  37. protected $path;
  38. /**
  39. * The set of paramters provided to the sesrvice. These are the values
  40. * that would occur in a URL after the question mark in an HTTP GET or
  41. * the data items of an HTTP POST.
  42. */
  43. protected $params;
  44. /**
  45. * The URL at which Tripal web services are found. This is used
  46. * for creating the IRI for resources.
  47. */
  48. protected $base_path;
  49. /**
  50. * The list of documented classes used by this service. For the web service
  51. * to be discoverable all of the entity classes and their collections
  52. * must be defined.
  53. */
  54. protected $documentation;
  55. // --------------------------------------------------------------------------
  56. // CONSTRUCTORS
  57. // --------------------------------------------------------------------------
  58. /**
  59. * Implements the constructor.
  60. */
  61. public function __construct($base_path) {
  62. if (!$base_path) {
  63. throw new Exception('Please provide a $base_path argument when creating a new TripalWebService.');
  64. }
  65. // Create a default resource so that the service always some something.
  66. $this->resource = new TripalWebServiceResource($base_path);
  67. // Intialize the private members variables.
  68. $this->path = array();
  69. $this->params = array();
  70. $this->base_path = $base_path;
  71. $this->documentation = array();
  72. $this->addDocClass(array(
  73. "id" => "http://www.w3.org/ns/hydra/core#Resource",
  74. "term" => 'hydra:Resource',
  75. "title" => "Resource",
  76. 'subClassOf' => NULL,
  77. ));
  78. }
  79. // --------------------------------------------------------------------------
  80. // OVERRIDEABLE FUNCTIONS
  81. // --------------------------------------------------------------------------
  82. /**
  83. * Responds to the request argument provided to the service.
  84. *
  85. * This function should be implemented by a TripalWebService child class.
  86. *
  87. */
  88. public function handleRequest() {
  89. // TODO: make sure the $this->path and $this->params are set before
  90. // continuing.
  91. }
  92. // --------------------------------------------------------------------------
  93. // CLASS FUNCTIONS -- DO NOT OVERRIDE
  94. // --------------------------------------------------------------------------
  95. /**
  96. * Sets the URL path for the resource being called.
  97. *
  98. * @param $path
  99. * An array containing the elements of the URL path. Each level of the
  100. * URL appears in a separate element of the array. The service type and
  101. * version are automatically removed from the array. For example, a
  102. * URL of the type http://localhost/web-services/content/v0.1/Gene/sequence
  103. * will result in a $path array containing the following:
  104. * @code
  105. * array(
  106. * 'Gene',
  107. * 'sequence',
  108. * );
  109. * @endcode
  110. *
  111. * @param unknown $path
  112. */
  113. public function setPath($path) {
  114. $this->path = $path;
  115. }
  116. /**
  117. * Sets the parameters for the resource.
  118. *
  119. * @param $params
  120. * The set of paramters provided to the sesrvice. These are the values
  121. * that would occur in a URL after the question mark in an HTTP GET or
  122. * the data items of an HTTP POST.
  123. */
  124. public function setParams($params) {
  125. $this->params = $params;
  126. }
  127. /**
  128. * Retrieves the version number for this web service.
  129. *
  130. * Each web service must have version number built into the name of the
  131. * class. The version number appears at the end of the class name, begins
  132. * with a lower-case 'v' and is followed by two numbers (major and minor) that
  133. * are separated by an underscore. This function identifies the version
  134. * from the class name and returns it here in a human-readable format.
  135. *
  136. * @param $sanitize
  137. * Set to TRUE to convert the period to underscore.
  138. *
  139. * @return
  140. * The version number for this web service.
  141. */
  142. public function getVersion($sanitize = FALSE) {
  143. $class = get_class($this);
  144. $major_version = '';
  145. $minor_version = '';
  146. if (preg_match('/v(\d+)_(\d+)$/', $class, $matches)) {
  147. $major_version = $matches[1];
  148. $minor_version = $matches[2];
  149. if ($sanitize) {
  150. return 'v' . $major_version . '_' . $minor_version;
  151. }
  152. return 'v' . $major_version . '.' . $minor_version;
  153. }
  154. return '';
  155. }
  156. /**
  157. * Retrieves the context section of the response.
  158. *
  159. * The JSON-LD response constists of two sections the '@context' section
  160. * and the data section. This function only returns the context section
  161. * of the response.
  162. *
  163. * @return
  164. * An associative array containing the context section of the response.
  165. */
  166. public function getContext() {
  167. return $this->resource->getContext();
  168. }
  169. /**
  170. * Returns the full web service response.
  171. *
  172. * The response includes both the @context and data sections of the
  173. * JSON-LD response.
  174. *
  175. * @return
  176. * An associative array containing that can be converted to JSON.
  177. */
  178. public function getResponse() {
  179. $class = get_called_class();
  180. $context_filename = $class::$type . '.' . $this->getVersion(TRUE);
  181. foreach ($this->path as $element) {
  182. $context_filename .= '.' . $element;
  183. }
  184. $context_filename .= '.json';
  185. $context_filename = strtolower($context_filename);
  186. $context_dir = 'public://tripal/ws/context';
  187. $context_file_path = $context_dir . '/' . $context_filename;
  188. // Make sure the user directory exists
  189. if (!file_prepare_directory($context_dir, FILE_CREATE_DIRECTORY)) {
  190. throw new Exception('Could not access the tripal/ws/context directory on the server for storing web services context files.');
  191. }
  192. $context = $this->resource ? $this->resource->getContext() : array();
  193. $context = array(
  194. '@context' => $context
  195. );
  196. $cfh = fopen($context_file_path, "w");
  197. if (flock($cfh, LOCK_EX)) {
  198. fwrite($cfh, json_encode($context));
  199. flock($cfh, LOCK_UN);
  200. fclose($cfh);
  201. }
  202. else {
  203. throw new Exception(t('Error locking file: @file.', array('@file' => $context_file_path)));
  204. }
  205. $fid = db_select('file_managed', 'fm')
  206. ->fields('fm', array('fid'))
  207. ->condition('uri', $context_file_path)
  208. ->execute()
  209. ->fetchField();
  210. // Save the context file so Drupal can manage it and remove the file later.
  211. if (!$fid) {
  212. $context_file = new stdClass();
  213. $context_file->uri = $context_file_path;
  214. $context_file->filename = $context_filename;
  215. $context_file->filemime = 'application/ld+json';
  216. $context_file->uid = 0;
  217. file_save($context_file);
  218. }
  219. $type = $this->resource ? $this->resource->getType() : 'unknown';
  220. $json_ld = array(
  221. '@context' => file_create_url($context_file_path),
  222. '@id' => '',
  223. '@type' => $type,
  224. );
  225. $data = $this->getData();
  226. return array_merge($json_ld, $data);
  227. }
  228. /**
  229. * Retrieves the service URL for this service.
  230. */
  231. public function getServicePath() {
  232. $class = get_class($this);
  233. $version = $this->getVersion();
  234. $type = $class::$type;
  235. return $this->base_path . '/' . $type . '/' . $version;
  236. }
  237. /**
  238. * Retrieves the data section of the response.
  239. *
  240. * The JSON-LD response constists of two sections the '@context' section
  241. * and the data section. This function only returns the data section
  242. * of the response.
  243. *
  244. * @return
  245. * An associative array containing the data section of the response.
  246. */
  247. public function getData() {
  248. if ($this->resource) {
  249. return $this->resource->getData();
  250. }
  251. return array();
  252. }
  253. /**
  254. * Sets the resource to be returned by this web service.
  255. *
  256. * @param $resource.
  257. * An implementation of a TripalWebServiceResource.
  258. */
  259. public function setResource($resource) {
  260. // Make sure the $servcie provides is a TripalWebServcie class.
  261. if (!is_a($resource, 'TripalWebServiceResource')) {
  262. throw new Exception("Cannot add a new resource to this web service as it is not a TripalWebServiceResource.");
  263. }
  264. $this->resource = $resource;
  265. }
  266. /**
  267. * Set an error for the service.
  268. *
  269. * @param $message
  270. * The error message to report.
  271. */
  272. public function setError($message) {
  273. $this->resource = new TripalWebServiceResource($this->base_path);
  274. $this->resource->setID('error');
  275. $this->resource->addContextItem('error', 'rdfs:error');
  276. $this->resource->setType('error');
  277. $this->resource->addProperty('error', $message);
  278. }
  279. /**
  280. * Retrieves an array contining the supported classes.
  281. *
  282. * Supported classe are resources provided by this web services and the
  283. * operations supported by those classes.
  284. *
  285. * @return
  286. * An array of TripalWebServiceResource objects that follow the Hydra
  287. * documentation for documenting supported classes.
  288. */
  289. public function getDocumentation() {
  290. return $this->documentation;
  291. }
  292. /**
  293. * Defines an entity class for the web services.
  294. *
  295. * A class defines a resource including information about its properties
  296. * and the actions that can be performed. Each class as a unique @id,
  297. * title, type and description. The $details parameter is used to provide
  298. * this information. Additionally, a resource may support Create, Read
  299. * Update and Delete (CRUD) operations. Those are defined using the
  300. * $ops argument. Finally, resources may have properties (or fields). These
  301. * properties should be defined using the $props arugment.
  302. *
  303. * @param $details.
  304. * An array of key/value pairs providing the details for the class. Valid
  305. * keys include:
  306. * - id: The unique IRI for this class.
  307. * - term: the accession for the term for this class.
  308. * - title: The title for the resource that this Class represents.
  309. * - description: (Optional). A description of the resource.
  310. * - subClassOf: (Optional). If this class is a subclass of another
  311. * class then the value should be the @id of the parent class. This
  312. * defaults to hydra:Resource. If no subClass is desired, set it
  313. * to NULL.
  314. * - type: (Optional). Indicates the type of class. Defaults to
  315. * hydra:Class
  316. * @param $ops
  317. * If the resource supports CRUD functionality then those functions should
  318. * be defined using this argument. This is an associative array with
  319. * the following keys: GET, POST, PUT, DELETE. These keys, if provided,
  320. * indicate that a resource of this type can be retrieved (GET),
  321. * created (POST), updated (PUT) or deleted (DELETE). The value for each
  322. * key should be an associative array that supports the following keys:
  323. * = type: the @id that determines the type of operation. This type
  324. * should be specific to the resource, and it need not be a term
  325. * from a real vocabulary. Use the '_:' prefix for the term. For
  326. * example, for an 'Event' resource with a GET method, an appropriate
  327. * type would be '_:event_retrieve'.
  328. * - label: A label that describes the operation in the
  329. * context of the resource.
  330. * - description: A description for the operation. Can be set to NULL
  331. * for no description.
  332. * - expects: The information expected by the Web API.
  333. * - returns: The @id of a Class that will be returned by
  334. * the operation. Set to NULL if there is no return value.
  335. * - statusCodes: An array of status codes that could be returned when
  336. * an error occurs. Each element of the array is an associative
  337. * array with two key/value pairs: 'code' and 'description'. Set the
  338. * 'code' to be the HTTP code that can be returned on error and the
  339. * 'description' to an error message appropriate for the error. For
  340. * example an HTTP 404 error means "Not Found". You can sepecify this
  341. * code and provide a description appropriate for the method and
  342. * class.
  343. * @param $props.
  344. * Defines the list of properties that the resource provides. This is an
  345. * array of properties where each property is an associative array
  346. * containing the following keys:
  347. * - type: The @id of the type of property. Alternatively, this can
  348. * be an instance of a TripalWebServiceResource when the property
  349. * must support operations such as a property of type hydra:Link.
  350. * - title: (Optional). The human-readable title of this property. If
  351. * this key is absent then the title will be set to the term's title.
  352. * - description: (Optional). A short description of this property. If
  353. * this key is absent then the description will be set to the term's
  354. * description.
  355. * - required: Set to TRUE to indicate if this property is a required
  356. * field when creating a new resource.
  357. * - readable: Set to TRUE to indicate that the client can retrieve the
  358. * property's value?
  359. * altered by an update.
  360. * - writeable: Set to TRUE if the client can alter the value of the
  361. * property.
  362. * - domain: the @id of the rdfs:domain.
  363. * - range: the @id of the rdfs:range.
  364. */
  365. protected function addDocClass($details = array(), $ops = array(), $props = array()) {
  366. $supported_class = new TripalWebServiceResource($this->getServicePath());
  367. // Add the context which all classes will need
  368. $supported_class->addContextItem('description', 'rdfs:comment');
  369. $supported_class->addContextItem('subClassOf', 'hydra:subClassOf');
  370. $supported_class->addContextItem('description', 'rdfs:comment');
  371. $supported_class->addContextItem('label', 'rdfs:label');
  372. // Set the Class ID.
  373. $class_id = $details['id'];
  374. $accession = $details['term'];
  375. if ($accession != $class_id) {
  376. $supported_class->addContextItem($accession, $class_id);
  377. }
  378. $supported_class->setID($accession);
  379. // Set the class Type.
  380. if (array_key_exists('type', $details)) {
  381. $supported_class->setType($details['type']);
  382. }
  383. else {
  384. $supported_class->setType('hydra:Class');
  385. }
  386. // Set title and description.
  387. $supported_class->addProperty('hydra:title', $details['title']);
  388. $supported_class->addProperty('hydra:description', array_key_exists('description', $details) ? $details['description'] : NULL);
  389. // Set the sub class.
  390. if (array_key_exists('subClassOf', $details)) {
  391. if ($details['subClassOf']) {
  392. $supported_class->addProperty('subClassOf', $details['subClassOf']);
  393. }
  394. }
  395. else {
  396. $supported_class->addProperty('subClassOf', 'hydra:Resource');
  397. }
  398. // Now add the supported operations.
  399. $class_ops = array();
  400. foreach ($ops as $op => $op_details) {
  401. $class_ops[] = $this->generateDocClassOp($supported_class, $op, $op_details);
  402. }
  403. $supported_class->addContextItem('supportedOperation', 'hydra:supportedOperation');
  404. $supported_class->addProperty('supportedOperation', $class_ops);
  405. // Now add in the supported proprerties.
  406. $class_props = array();
  407. foreach ($props as $prop) {
  408. $class_props[] = $this->generateDocClassProp($supported_class, $prop);
  409. }
  410. $supported_class->addContextItem('supportedProperty', 'hydra:supportedProperty');
  411. $supported_class->addProperty('supportedProperty', $class_props);
  412. $this->documentation[] = $supported_class;
  413. }
  414. /**
  415. * A helper function for the addClass() function for generating a property.
  416. */
  417. private function generateDocClassProp($supported_class, $prop) {
  418. $supported_class->addContextItem('property', array(
  419. "@id" => "hydra:property",
  420. "@type" => "@id"
  421. ));
  422. $supported_class->addContextItem('domain', array(
  423. "@id" => "rdfs:domain",
  424. "@type" => "@id"
  425. ));
  426. $supported_class->addContextItem('range', array(
  427. "@id" => "rdfs:range",
  428. "@type" => "@id"
  429. ));
  430. $supported_class->addContextItem('readable', 'hydra:readable');
  431. $supported_class->addContextItem('writeable', 'hydra:writeable');
  432. $supported_class->addContextItem('required', 'hydra:required');
  433. $supported_class->addContextItem('tripal_formatters', 'tripal:tripal_formatters');
  434. $class_prop = array(
  435. 'property' => $prop['type'],
  436. 'hydra:title' => $prop['title'],
  437. 'hydra:description' => array_key_exists('description', $prop) ? $prop['description'] : NULL,
  438. 'required' => array_key_exists('required', $prop) ? $prop['required'] : NULL,
  439. 'readable' => array_key_exists('readonly', $prop) ? $prop['readonly'] : NULL,
  440. 'writeable' => array_key_exists('writeonly', $prop) ? $prop['writeonly'] : NULL,
  441. );
  442. if (array_key_exists('domain', $prop)) {
  443. $class_prop['domain'] = $prop['domain'];
  444. }
  445. if (array_key_exists('range', $prop)) {
  446. $class_prop['range'] = $prop['range'];
  447. }
  448. if (array_key_exists('operations', $prop)) {
  449. $class_prop['supportedOperation'] = array();
  450. foreach ($prop['operations'] as $op => $op_details) {
  451. $class_prop['supportedOperation'][] = $this->generateOp($supported_class, $op, $op_details);
  452. }
  453. }
  454. if (array_key_exists('tripal_formatters', $prop)) {
  455. $class_prop['tripal_formatters'] = array_keys($prop['tripal_formatters']);
  456. }
  457. return $class_prop;
  458. }
  459. /**
  460. * A helper function for the addClass() function for generating an operation.
  461. */
  462. private function generateDocClassOp($supported_class, $op, $op_details) {
  463. if ($op != 'GET' and $op != 'PUT' and $op != 'DELETE' and $op != 'POST') {
  464. throw new Exception(t('The method, @method, provided to the TripalWebService::addClass function is not an oppropriate operations.', array('@method' => $op)));
  465. }
  466. if (!array_key_exists('type', $op_details)) {
  467. throw new Exception(t('Please provide a type in the operations array passed to the TripalWebService::addClass() function: @details', array('@details' => print_r($op_details, TRUE))));
  468. }
  469. if (!array_key_exists('label', $op_details)) {
  470. throw new Exception(t('Please provide a label in the operations array passed to the TripalWebService::addClass() function: @details', array('@details' => print_r($op_details, TRUE))));
  471. }
  472. $class_op = new TripalWebServiceResource($this->base_path);
  473. $class_op->setID($op_details['type']);
  474. switch($op) {
  475. case 'GET':
  476. $class_op->setType('hydra:Operation');
  477. break;
  478. case 'POST':
  479. $class_op->setType('http://schema.org/AddAction');
  480. break;
  481. case 'DELETE':
  482. $class_op->setType('http://schema.org/DeleteAction');
  483. break;
  484. case 'PUT':
  485. $class_op->setType('http://schema.org/UpdateAction');
  486. break;
  487. }
  488. $class_op->addContextItem('method', 'hydra:method');
  489. $class_op->addContextItem('label', 'rdfs:label');
  490. $class_op->addContextItem('description', 'rdfs:comment');
  491. $class_op->addContextItem('expects', array(
  492. "@id" => "hydra:expects",
  493. "@type" => "@id"
  494. ));
  495. $class_op->addContextItem('returns', array(
  496. "@id" => "hydra:returns",
  497. "@type" => "@id"
  498. ));
  499. $class_op->addContextItem('statusCodes', 'hydra:statusCodes');
  500. $class_op->addContextItem('code', 'hydra:statusCode');
  501. $class_op->addProperty('method', $op);
  502. $class_op->addProperty('label', array_key_exists('label', $op_details) ? $op_details['label'] : 'Retrieves an instance of this resource');
  503. $class_op->addProperty('description', array_key_exists('description', $op_details) ? $op_details['description'] : NULL);
  504. $status_codes = array_key_exists('statusCodes', $op_details) ? $op_details['statusCodes'] : array();
  505. foreach ($status_codes as $code) {
  506. if (array_key_exists('code', $code) and array_key_exists('description', $code)) {
  507. $class_op->addProperty('statusCodes', $code);
  508. }
  509. else {
  510. throw new Exception(t('The status code provided to TripalWebService::addClass function is improperly formatted @code.', array('@code' => print_r($code, TRUE))));
  511. }
  512. }
  513. if (count($status_codes) == 0) {
  514. $class_op->addProperty('statusCodes', array());
  515. }
  516. $class_op->addProperty('expects', array_key_exists('expects', $op_details) ? $op_details['expects'] : NULL);
  517. $class_op->addProperty('returns', array_key_exists('returns', $op_details) ? $op_details['returns'] : ' "http://www.w3.org/2002/07/owl#Nothing",');
  518. return $class_op;
  519. }
  520. /**
  521. * Converts a term array into an value appropriate for an @id or @type.
  522. *
  523. * @param $term
  524. * The term array.
  525. * @param $santize
  526. * An array of keywords indicating how to santize the key. By default,
  527. * no sanitizing occurs. The two valid types are 'lowercase', and 'spacing'
  528. * where 'lowercase' converts the term name to all lowercase and
  529. * 'spacing' replaces any spaces with underscores.
  530. *
  531. * @return
  532. * The id (the term name but with spaces replaced with underscores).
  533. */
  534. protected function getContextTerm($term, $sanitize = array()) {
  535. if (!$term) {
  536. $backtrace = debug_backtrace();
  537. throw new Exception('getContextTerm: Please provide a non NUll or non empty $term.');
  538. }
  539. if (!$term['name']) {
  540. throw new Exception('getContextTerm: The provided term does not have a name: ' . print_r($term, TRUE));
  541. }
  542. $key = $term['name'];
  543. $key_adj = $key;
  544. if (in_array('spacing', $sanitize)) {
  545. $key_adj = preg_replace('/ /', '_', $key_adj);
  546. }
  547. if (in_array('lowercase', $sanitize)) {
  548. $key_adj = strtolower($key_adj);
  549. }
  550. return $key_adj;
  551. }
  552. /**
  553. * Adds a term to the '@context' section for a given resource.
  554. *
  555. * @param $resource
  556. * A TripalWebServiceResource instance.
  557. * @param $term
  558. * The term array.
  559. * @param $santize
  560. * An array of keywords indicating how to santize the key. By default,
  561. * no sanitizing occurs. The two valid types are 'lowercase', and 'spacing'
  562. * where 'lowercase' converts the term name to all lowercase and
  563. * 'spacing' replaces any spaces with underscores.
  564. * @return
  565. * The id (the term name but with spaces replaced with underscores).
  566. */
  567. protected function addContextTerm($resource, $term, $sanitize = array()) {
  568. if (!is_a($resource, 'TripalWebServiceResource')) {
  569. throw new Exception('addContextTerm: Please provide a $resource of type TripalWebServiceResource.');
  570. }
  571. if (!$term) {
  572. $backtrace = debug_backtrace();
  573. throw new Exception('addContextTerm: Please provide a non NUll or non empty $term.');
  574. }
  575. if (!$term['name']) {
  576. throw new Exception('addContextTerm: The provided term does not have a name: ' . print_r($term, TRUE));
  577. }
  578. // Make sure the vocab is present
  579. $vocab = $term['vocabulary'];
  580. $this->addContextVocab($resource, $vocab);
  581. // Sanitize the term key
  582. $key_adj = $this->getContextTerm($term, $sanitize);
  583. // Create the compact IRI
  584. $compact_iri = $vocab['short_name'] . ':' . $term['accession'];
  585. // If the full naming is indicated in the service parameters then
  586. // set the full name of the keys to include the vocab short name.
  587. if (array_key_exists('full_keys', $this->params)) {
  588. //$key_adj = $vocab['short_name'] . ':' . $key_adj;
  589. }
  590. $iri = $term['url'];
  591. $resource->addContextItem($key_adj, $compact_iri);
  592. $resource->addContextItem($compact_iri, $iri);
  593. return $key_adj;
  594. }
  595. /**
  596. * Adds a vocabulary to the '@context' section for a given resource.
  597. *
  598. * @param $resource
  599. * A TripalWebServiceResource instance.
  600. * @param $vocab
  601. * The vocabulary array.
  602. */
  603. protected function addContextVocab($resource, $vocab) {
  604. if (!is_a($resource, 'TripalWebServiceResource')) {
  605. throw new Exception('addContextVocab: Please provide a $resource of type TripalWebServiceResource.');
  606. }
  607. $key = $vocab['short_name'];
  608. // The URL here should be the URL prefix not the database home
  609. // page. But unfortunately, the URL prefix can't be guaranteed to be
  610. // a true prefix. Not all databases place by the rules. For this reason,
  611. // we can never use JSON-LD compact IRIs. :-(
  612. $iri = $vocab['sw_url'];
  613. $resource->addContextItem($key, $iri);
  614. }
  615. /**
  616. * Adds a key/value property to the given resource.
  617. *
  618. * @param $resource
  619. * A TripalWebServiceResource instance.
  620. * @param $term
  621. * The term array for the key.
  622. * @param $value
  623. * The value to add.
  624. * @param $santize
  625. * An array of keywords indicating how to santize the key. By default,
  626. * no sanitizing occurs. The two valid types are 'lowercase', and 'spacing'
  627. * where 'lowercase' converts the term name to all lowercase and
  628. * 'spacing' replaces any spaces with underscores.
  629. * @return $key
  630. * The key (the term name but with spaces replaced with underscores).
  631. */
  632. public function addResourceProperty($resource, $term, $value, $sanitize = array()) {
  633. if (!is_a($resource, 'TripalWebServiceResource')) {
  634. throw new Exception('addProperty: Please provide a $resource of type TripalWebServiceResource.');
  635. }
  636. // First add the term
  637. $key = $this->addContextTerm($resource, $term, $sanitize);
  638. // Then add the property.
  639. $resource->addProperty($key, $value);
  640. }
  641. /**
  642. * Sets the type for the given resource.
  643. *
  644. * The type exist in the context of the web service.
  645. *
  646. * @param $resource
  647. * A TripalWebServiceResource instance.
  648. * @param $type
  649. * The type
  650. * @param $santize
  651. * An array of keywords indicating how to santize the key. By default,
  652. * no sanitizing occurs. The two valid types are 'lowercase', and 'spacing'
  653. * where 'lowercase' converts the term name to all lowercase and
  654. * 'spacing' replaces any spaces with underscores.
  655. */
  656. public function setResourceType($resource, $term, $sanitize = array('spacing')) {
  657. if (!is_a($resource, 'TripalWebServiceResource')) {
  658. throw new Exception('addProperty: Please provide a $resource of type TripalWebServiceResource.');
  659. }
  660. // First add the term
  661. $key = $this->addContextTerm($resource, $term, $sanitize);
  662. // Then set the type
  663. $resource->setType($key);
  664. }
  665. }