remote__data_formatter.inc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. <?php
  2. class remote__data_formatter extends WebServicesFieldFormatter {
  3. // The default label for this field.
  4. public static $default_label = 'Remote Data';
  5. // The list of field types for which this formatter is appropriate.
  6. public static $field_types = ['remote__data'];
  7. // The list of default settings for this formatter.
  8. public static $default_settings = [
  9. 'setting1' => 'default_value',
  10. ];
  11. /**
  12. * @see TripalFieldFormatter::view()
  13. */
  14. public function view(&$element, $entity_type, $entity, $langcode, $items, $display) {
  15. $content = '';
  16. // Get the settings
  17. $settings = $display['settings'];
  18. $field_name = $this->field['field_name'];
  19. // Get any subfields and the header label. Shift the array because the
  20. // results should already be the value of the first entry.
  21. $rd_field_name = $this->instance['settings']['data_info']['rd_field_name'];
  22. $subfields = explode(',', $rd_field_name);
  23. $header_label = $this->getHeaderLabel($subfields);
  24. $flabel = array_shift($subfields);
  25. // Get the site logo if one is provided
  26. $site_logo = $this->instance['settings']['data_info']['site_logo'];
  27. if ($site_logo) {
  28. $site_logo = file_load($site_logo);
  29. }
  30. // Get the site name where the data came from.
  31. $site_id_ws = $this->instance['settings']['data_info']['remote_site'];
  32. $site = db_select('tripal_sites', 'ts')
  33. ->fields('ts', ['name', 'url'])
  34. ->condition('ts.id', $site_id_ws)
  35. ->execute()
  36. ->fetchObject();
  37. // Iterate through the results and create a generic table.
  38. $rows = [];
  39. $headers = [''];
  40. foreach ($items as $index => $item) {
  41. if (!$item['value'] or empty($item['value'])) {
  42. continue;
  43. }
  44. $value = $item['value'];
  45. $error = $item['error'];
  46. $warning = $item['warning'];
  47. // If there is an error or warning then clear the cache for this field
  48. // so that next time the page is loaded it will try to reload again.
  49. if ($error or $warning) {
  50. $cid = "field:TripalEntity:" . $entity->id . ':' . $field_name;
  51. cache_clear_all($cid, 'cache_field');
  52. if ($item['admin_message']) {
  53. $severity = TRIPAL_ERROR;
  54. if ($warning) {
  55. $severity = TRIPAL_WARNING;
  56. }
  57. $value .= tripal_set_message($item['admin_message'] . 'The query URL was: ' . l($item['query_str'], $item['query_str'], ['attributes' => ['target' => '_blank']]),
  58. $severity, ['return_html' => TRUE]);
  59. }
  60. $rows[] = [$value];
  61. continue;
  62. }
  63. $remote_entity_label = array_key_exists('label', $item) ? $item['remote_entity']['label'] : '';
  64. $remote_entity_page = $item['remote_entity']['ItemPage'];
  65. $remote_entity_link = t('View !data on %site',
  66. [
  67. '!data' => l('this data', $remote_entity_page, ['attributes' => ['target' => '_blank']]),
  68. '%site' => $site->name,
  69. ]);
  70. // If this is a collection then handle it as a list of members.
  71. if (array_key_exists('members', $value)) {
  72. foreach ($value['members'] as $subvalue) {
  73. $subvalue = $this->refineSubValue($subvalue, $subfields, $header_label);
  74. $rows[] = [$subvalue];
  75. }
  76. }
  77. else {
  78. if (count($subfields) > 0) {
  79. $subvalue = $this->refineSubValue($value, $subfields, $header_label);
  80. $rows[] = [$subvalue];
  81. }
  82. else {
  83. if (array_key_exists($flabel, $value)) {
  84. $rows[] = [$value[$flabel]];
  85. }
  86. else {
  87. $value['Link'] = l('View content on ' . $site->name, $remote_entity_page, ['attributes' => ['target' => '_blank']]);
  88. $rows[] = [$value];
  89. }
  90. }
  91. }
  92. }
  93. // TODO: we need to handle paged elements.
  94. $has_sub_tables = FALSE;
  95. for ($i = 0; $i < count($rows); $i++) {
  96. if (is_array($rows[$i][0])) {
  97. $rows[$i][0] = $this->createTable($rows[$i]);
  98. $has_sub_tables = TRUE;
  99. }
  100. else {
  101. $rows[$i] = [
  102. 'colspan' => 2,
  103. 'data' => $rows[$i],
  104. ];
  105. }
  106. }
  107. // If we don't have tables for each row then we'll put everything into
  108. // a table.
  109. if (!$has_sub_tables) {
  110. $headers = [$header_label . '(s)'];
  111. $content .= theme_table([
  112. 'header' => $headers,
  113. 'rows' => $rows,
  114. 'attributes' => [
  115. 'class' => 'tripal-remote--data-field-table',
  116. ],
  117. 'sticky' => FALSE,
  118. 'caption' => "",
  119. 'colgroups' => [],
  120. 'empty' => 'There are no results.',
  121. ]);
  122. }
  123. else {
  124. for ($i = 0; $i < count($rows); $i++) {
  125. if (count($rows) > 1) {
  126. $content .= '<span class="tripal-remote--data-field-table-label">' . $header_label . ' ' . ($i + 1) . '</span>';
  127. }
  128. $content .= $rows[$i][0];
  129. }
  130. }
  131. $content .= '<p>';
  132. if ($site) {
  133. $content .= t('This content provided by !site.', [
  134. '!site' => l($site->name, $site->url,
  135. ['attributes' => ["target" => '_blank']]),
  136. ]);
  137. }
  138. else {
  139. tripal_report_error('tripal_ws', TRIPAL_ERROR, 'Unable to find remote site while attempting to format results.');
  140. }
  141. if (is_object($site_logo)) {
  142. $content .= '<img class="tripal-remote--data-field-logo" src="' . file_create_url($site_logo->uri) . '"><br/>';
  143. }
  144. if (count($items) == 1) {
  145. $content .= isset($remote_entity_link) ? $remote_entity_link : '';
  146. }
  147. $content .= '</p>';
  148. // Return the content for this field.
  149. $element[0] = [
  150. '#type' => 'markup',
  151. '#markup' => '<div class="tripal-remote--data-field">' . $content . '</div>',
  152. ];
  153. }
  154. /**
  155. * Retrieves the header label given the subfields criteria.
  156. *
  157. * @param $subfields
  158. * An array of the sequence of subfields.
  159. */
  160. private function getHeaderLabel($subfields) {
  161. $subfield = array_shift($subfields);
  162. $header_label = ucwords(preg_replace('/_/', ' ', $subfield));
  163. if (count($subfields) > 0) {
  164. $header_label .= ' ' . $this->getHeaderLabel($subfields);
  165. }
  166. return $header_label;
  167. }
  168. /**
  169. * Adjusts the items array to contain only the section/subsection desired.
  170. *
  171. * The field settings can indicate a field with sub fields that should
  172. * be displayed (e.g. organism,genus or relationship,clause). We want
  173. * to adjust the item to only include what the user requested.
  174. *
  175. * @param $values
  176. * @param $subfields
  177. */
  178. private function refineSubValue($values, $subfields) {
  179. // Remove unwanted elements.
  180. unset($values['@id']);
  181. unset($values['@context']);
  182. unset($values['@type']);
  183. unset($values['remote_entity']);
  184. $subfield = array_shift($subfields);
  185. if (array_key_exists($subfield, $values)) {
  186. if (is_array($values[$subfield]) and count($subfields) > 0) {
  187. return $this->refineSubvalue($values[$subfield], $subfields);
  188. }
  189. else {
  190. return $values[$subfield];
  191. }
  192. }
  193. else {
  194. return $values;
  195. }
  196. }
  197. /**
  198. * A recursive function for displaying an item in a table.
  199. *
  200. * @param $item
  201. * An item from the $items array passed to the view() function.
  202. *
  203. * @return
  204. * An HTML formatted Table.
  205. */
  206. private function createTable($item, &$pkey = '', &$rows = [], $depth = 0) {
  207. foreach ($item as $key => $value) {
  208. // Skip JSON-LD keys.
  209. if (preg_match('/^\@/', $key)) {
  210. continue;
  211. }
  212. $key = preg_replace('/_/', ' ', $key);
  213. $key = ucwords($key);
  214. if ($pkey) {
  215. $key = $pkey . ' ' . $key;
  216. }
  217. if (is_array($value)) {
  218. $this->createTable($value, $key, $rows, $depth + 1);
  219. }
  220. else {
  221. $rows[] = [
  222. 'data' => [
  223. $key,
  224. $value,
  225. ],
  226. 'no_striping' => TRUE,
  227. ];
  228. }
  229. }
  230. if ($depth == 0) {
  231. $headers = ['Data Type', 'Value'];
  232. return theme_table([
  233. 'header' => $headers,
  234. 'rows' => $rows,
  235. 'attributes' => [
  236. 'class' => 'tripal-remote--data-field-table',
  237. ],
  238. 'sticky' => FALSE,
  239. 'caption' => "",
  240. 'colgroups' => [],
  241. 'empty' => 'There are no results.',
  242. ]);
  243. }
  244. }
  245. /**
  246. * A recursive function for creating an HTML dictionary list from
  247. * the results for the item provided.
  248. *
  249. * @param $item
  250. * An item from the $items array passed to the view() function.
  251. *
  252. * @return
  253. * An HTML formatted DL.
  254. */
  255. private function createDL($item, &$pkey = '', &$content = '', $depth = 0) {
  256. if ($depth == 0) {
  257. $content = '<dl class="tripal-remote--data-field-dl">';
  258. }
  259. foreach ($item as $key => $value) {
  260. // Skip JSON-LD keys.
  261. if (preg_match('/^\@/', $key)) {
  262. continue;
  263. }
  264. $key = preg_replace('/_/', ' ', $key);
  265. $key = ucwords($key);
  266. if ($pkey) {
  267. $key = $pkey . ' ' . $key;
  268. }
  269. if (is_array($value)) {
  270. $this->createDL($value, $key, $content, $depth + 1);
  271. }
  272. else {
  273. $content .= '<dt>' . $key . '&nbsp;:&nbsp;</dt><dd>' . $value . '</dd>';
  274. }
  275. }
  276. if ($depth == 0) {
  277. $content .= '</dl>';
  278. return $content;
  279. }
  280. }
  281. /**
  282. * A recursive function for creating an HTML dictionary list from
  283. * the results for the item provided.
  284. *
  285. * @param $item
  286. * An item from the $items array passed to the view() function.
  287. *
  288. * @return
  289. * An HTML formatted DL.
  290. */
  291. private function createNestedDL($item) {
  292. $content = '<dl>';
  293. foreach ($item as $key => $value) {
  294. // Skip JSON-LD keys.
  295. if (preg_match('/^\@/', $key)) {
  296. continue;
  297. }
  298. $key = preg_replace('/_/', ' ', $key);
  299. $key = ucwords($key);
  300. if (is_array($value)) {
  301. $value = $this->createDL($value);
  302. }
  303. $content .= '<dt>' . $key . '</dt><dd>' . $value . '</dd>';
  304. }
  305. $content .= '</dl>';
  306. return $content;
  307. }
  308. /**
  309. * Provides a summary of the formatter settings.
  310. *
  311. * This function corresponds to the hook_field_formatter_settings_summary()
  312. * function of the Drupal Field API.
  313. *
  314. * On the 'Manage Display' page of the content type administration page,
  315. * fields are allowed to provide a settings form. This settings form can
  316. * be used to allow the site admin to define how the field should be
  317. * formatted. The settings are then available for the formatter()
  318. * function of this class. This function provides a text-based description
  319. * of the settings for the site developer to see. It appears on the manage
  320. * display page inline with the field. A field must always return a
  321. * value in this function if the settings form gear button is to appear.
  322. *
  323. * See the hook_field_formatter_settings_summary() function for more
  324. * information.
  325. *
  326. * @param $field
  327. * @param $instance
  328. * @param $view_mode
  329. *
  330. * @return string
  331. * A string that provides a very brief summary of the field settings
  332. * to the user.
  333. *
  334. */
  335. public function settingsSummary($view_mode) {
  336. }
  337. }