tripal.fields.inc 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942
  1. <?php
  2. /**
  3. * Implements hook_field_create_instance().
  4. */
  5. function tripal_field_create_instance($instance) {
  6. $field = field_info_field($instance['field_name']);
  7. $field_class = $field['type'];
  8. if (tripal_load_include_field_class($field_class)) {
  9. $field = new $field_class($field, $instance);
  10. return $field->createInstance();
  11. }
  12. }
  13. /**
  14. * Implements hook_field_info().
  15. *
  16. * We want the Tripal module to handle all TripalFields. This will allow
  17. * other modules to be more easily disabled/enabled because Drupal won't
  18. * let a module be disabled if it supports fields that are actively attached
  19. * to bundles. Therefore any module that provides a new TripalField will be
  20. * discovered and listed for Drupal by this function.
  21. */
  22. function tripal_field_info() {
  23. $info = [];
  24. $field_types = tripal_get_field_types();
  25. foreach ($field_types as $field_type) {
  26. $info[$field_type] = $field_type::info();
  27. }
  28. return $info;
  29. }
  30. /**
  31. * Implements hook_info_alter().
  32. */
  33. function tripal_field_info_alter(&$info) {
  34. foreach ($info as $field_name => $details) {
  35. // Make sure all fields have a term setting so we can map
  36. // all fields to a vocabulary term for the semantic web.
  37. if (array_key_exists('instance_settings', $details)) {
  38. if (!array_key_exists('term_vocabulary', $details['instance_settings'])) {
  39. $info[$field_name]['instance_settings']['term_vocabulary'] = '';
  40. }
  41. if (!array_key_exists('term_name', $details['instance_settings'])) {
  42. $info[$field_name]['instance_settings']['term_name'] = '';
  43. }
  44. if (!array_key_exists('term_accession', $details['instance_settings'])) {
  45. $info[$field_name]['instance_settings']['term_accession'] = '';
  46. }
  47. if (!array_key_exists('term_fixed', $details['instance_settings'])) {
  48. $info[$field_name]['instance_settings']['term_fixed'] = FALSE;
  49. }
  50. if (!array_key_exists('auto_attach', $details['instance_settings'])) {
  51. $info[$field_name]['instance_settings']['auto_attach'] = TRUE;
  52. }
  53. }
  54. else {
  55. $info[$field_name]['instance_settings']['term_vocabulary'] = '';
  56. $info[$field_name]['instance_settings']['term_name'] = '';
  57. $info[$field_name]['instance_settings']['term_accession'] = '';
  58. $info[$field_name]['instance_settings']['term_fixed'] = FALSE;
  59. $info[$field_name]['instance_settings']['auto_attach'] = TRUE;
  60. }
  61. }
  62. }
  63. /**
  64. * Implements hook_field_widget_info();
  65. */
  66. function tripal_field_widget_info() {
  67. $info = [];
  68. $widgets = tripal_get_field_widgets();
  69. foreach ($widgets as $widget) {
  70. $info[$widget] = $widget::info();
  71. }
  72. return $info;
  73. }
  74. /**
  75. * Implements hook_field_widget_info_alter();
  76. */
  77. function tripal_field_widget_info_alter(&$info) {
  78. }
  79. /**
  80. * Implements hook_field_formatter_info().
  81. */
  82. function tripal_field_formatter_info() {
  83. $info = [];
  84. $formatters = tripal_get_field_formatters();
  85. foreach ($formatters as $formatter) {
  86. $info[$formatter] = $formatter::info();
  87. }
  88. return $info;
  89. }
  90. /**
  91. * Implements hook_field_formatter_info_alter();
  92. */
  93. function tripal_field_formatter_info_alter(&$info) {
  94. }
  95. /**
  96. * Implements hook_bundle_create().
  97. *
  98. * This is a Tripal defined hook and is called in the
  99. * TripalBundleController::create()
  100. * function to allow modules to perform tasks when a bundle is created.
  101. */
  102. function tripal_bundle_create($bundle, $args) {
  103. $field_type = 'rdfs__type';
  104. $field_name = 'rdfs__type';
  105. // Add the field, unless it already exists.
  106. if (!field_info_field($field_name)) {
  107. $field = field_create_field([
  108. 'field_name' => $field_name,
  109. 'type' => $field_type,
  110. 'cardinality' => 1,
  111. 'locked' => FALSE,
  112. 'storage' => [
  113. 'type' => 'tripal_no_storage',
  114. ],
  115. ]);
  116. }
  117. // Add an instance of the field to the bundle.
  118. if (!field_info_instance($bundle->type, $field_name, $bundle->name)) {
  119. $instance = field_create_instance([
  120. 'field_name' => $field_name,
  121. 'entity_type' => 'TripalEntity',
  122. 'bundle' => $bundle->name,
  123. 'label' => 'Resource Type',
  124. 'description' => 'The resource type',
  125. 'required' => FALSE,
  126. 'settings' => [
  127. 'auto_attach' => TRUE,
  128. 'term_vocabulary' => 'rdfs',
  129. 'term_accession' => 'type',
  130. 'term_name' => 'type',
  131. ],
  132. 'widget' => [
  133. 'type' => 'rdfs__type_widget',
  134. 'settings' => [
  135. 'display_label' => 1,
  136. ],
  137. ],
  138. 'display' => [
  139. 'default' => [
  140. 'label' => 'inline',
  141. 'type' => 'rdfs__type_formatter',
  142. 'settings' => [],
  143. ],
  144. ],
  145. ]);
  146. }
  147. }
  148. /**
  149. * Implements hook_field_formatter_view().
  150. */
  151. function tripal_field_formatter_view($entity_type, $entity, $field,
  152. $instance, $langcode, $items, $display) {
  153. // Don't show any fields that don't have a controlled vocabulary term in
  154. // the database.
  155. $vocabulary = $instance['settings']['term_vocabulary'];
  156. $accession = $instance['settings']['term_accession'];
  157. $term = tripal_get_term_details($vocabulary, $accession);
  158. if (!$term) {
  159. tripal_set_message(t("The controlled vocabulary term, ':term (:term_name)', assigned to the
  160. field, ':field', is not in the database. The field cannot be shown.
  161. Please add the term and the field will appear below. ",
  162. [
  163. ':field' => $field['field_name'],
  164. ':term' => $vocabulary . ":" . $accession,
  165. ':term_name' => $instance['settings']['term_name'],
  166. ]),
  167. TRIPAL_WARNING);
  168. return;
  169. }
  170. $element = [];
  171. $formatter_class = $display['type'];
  172. $is_loaded = tripal_load_include_field_class($formatter_class);
  173. if ($is_loaded) {
  174. $formatter = new $formatter_class($field, $instance);
  175. $formatter->view($element, $entity_type, $entity, $langcode, $items, $display);
  176. }
  177. return $element;
  178. }
  179. /**
  180. * Simple provides a message indicating that the field cannot be deleted.
  181. *
  182. * This function is used in the tripal_menu_alter() function. We alter the
  183. * menu created for managing fields to use this call back which
  184. * prints a message that the field cannot be deleted.
  185. */
  186. function tripal_field_no_delete() {
  187. drupal_set_message('This field cannot be removed.', 'warning');
  188. return '';
  189. }
  190. /**
  191. *
  192. * Implements hook_form_FORM_ID_alter().
  193. *
  194. * The field_ui_field_overview_form_ is used for adding and reordering the
  195. * fields attached to a bundle. It also includes edit and delete links and
  196. * links for editing field types and widgets.
  197. *
  198. * This alter function is used to add a new 'Supported By' column to
  199. * the table to let the user know where fields are storing their data.
  200. */
  201. function tripal_form_field_ui_field_overview_form_alter(&$form, &$form_state, $form_id) {
  202. $used_terms = [];
  203. // If this isn't a TripalEntity content type then skip this form.
  204. if ($form['#entity_type'] != 'TripalEntity') {
  205. return;
  206. }
  207. // Add the 'Storage Location' to the table header.
  208. $form['fields']['#header'][] = 'Term';
  209. $form['fields']['#header'][] = 'Supported By * ';
  210. // Add the storage location as the final column for each field.
  211. $storage_info = module_invoke_all('field_storage_info');
  212. foreach (element_children($form['fields']) as $field_name) {
  213. $field = field_info_field($field_name);
  214. $instance = field_info_instance('TripalEntity', $field_name, $form['#bundle']);
  215. // Warn users if a field is missing a term.
  216. if ($instance and $instance['entity_type'] == 'TripalEntity' and
  217. array_key_exists('settings', $instance) and is_array($instance['settings']) and
  218. (!array_key_exists('term_vocabulary', $instance['settings']) or !$instance['settings']['term_vocabulary'])) {
  219. tripal_report_error('tripal_fields', TRIPAL_WARNING,
  220. 'The field, !field, is missing a controlled vocabulary term. Please edit the field and set a term, otherwise this field may not work properly.',
  221. ['!field' => $field_name],
  222. ['drupal_set_message' => TRUE]);
  223. }
  224. // Warn users if any of the terms are not unique.
  225. if ($instance and array_key_exists('settings', $instance) and is_array($instance['settings']) and
  226. array_key_exists('term_vocabulary', $instance['settings'])) {
  227. $term = $instance['settings']['term_vocabulary'] . ':' . $instance['settings']['term_accession'];
  228. if (array_key_exists($term, $used_terms)) {
  229. $used_terms[$term][] = $field_name;
  230. tripal_report_error('tripal_fields', TRIPAL_WARNING,
  231. 'The term !term is in use by multiple fields: !fields. ' .
  232. 'This is not allowed. Every field must have a different controlled vocabulary term. ' .
  233. 'Please correct the term assignments.',
  234. ['!term' => $term, '!fields' => implode(', ', $used_terms[$term])],
  235. ['drupal_set_message' => TRUE]);
  236. }
  237. $used_terms[$term][] = $field_name;
  238. }
  239. // For rows in the tables that aren't fields, just add an empty value
  240. // for the storage column.
  241. if (!$field) {
  242. $form['fields'][$field_name][] = [
  243. '#markup' => '',
  244. ];
  245. $form['fields'][$field_name][] = [
  246. '#markup' => '',
  247. ];
  248. continue;
  249. }
  250. $term_info = '';
  251. if (array_key_exists('term_accession', $instance['settings']) and $instance['settings']['term_accession']) {
  252. $term = tripal_get_term_details($instance['settings']['term_vocabulary'], $instance['settings']['term_accession']);
  253. $term_info = $term['name'] . ' (' . $instance['settings']['term_vocabulary'] . ':' . $instance['settings']['term_accession'] . ')';
  254. }
  255. $form['fields'][$field_name][] = [
  256. '#markup' => $term_info,
  257. ];
  258. $storage_type = $field['storage']['type'];
  259. $storage_label = array_key_exists('label', $storage_info[$storage_type]) ? $storage_info[$storage_type]['label'] : '';
  260. if ($storage_type == 'field_sql_storage') {
  261. $storage_label = 'Drupal';
  262. }
  263. if (array_key_exists('logo_url', $storage_info[$storage_type])) {
  264. $logo_url = $storage_info[$storage_type]['logo_url'];
  265. $form['fields'][$field_name][] = [
  266. '#markup' => '<img class="form-field-ui-field-overview-storage-logo" src="' . $logo_url . '">',
  267. ];
  268. }
  269. else {
  270. $form['fields'][$field_name][] = [
  271. '#markup' => $storage_label,
  272. ];
  273. }
  274. }
  275. $form['note'] = [
  276. '#markup' => '* Fields attached to this content type can use various
  277. storage backends. Please be sure when you add new fields that the
  278. storage backend is appropriate. For example, if you use Chado, and you
  279. want all biological content to be stored in Chado, be sure that the
  280. respective fields are "supported by" Chado.',
  281. ];
  282. $form['#submit'] = array_merge(['tripal_form_field_ui_field_overview_form_submit'], $form['#submit']);
  283. }
  284. /**
  285. * A submit function for the field_ui_field_overview_form.
  286. *
  287. */
  288. function tripal_form_field_ui_field_overview_form_submit($form, &$form_state) {
  289. $form_values = $form_state['values']['fields'];
  290. $admin_path = _field_ui_bundle_admin_path('TripalEntity', $form['#bundle']);
  291. $destinations = [];
  292. // If the form Field UI form is adding a new field to the bundle we want
  293. // to preempt Drupal from creating the field in it's field_sql_storage
  294. // backend. We want to create it.
  295. if (!empty($form_values['_add_new_field']['field_name'])) {
  296. try {
  297. // Is the field type a TripalField? If so then we want
  298. // to pass of creation of the field to the module that manages that field.
  299. $type = $form_values['_add_new_field']['type'];
  300. if (tripal_load_include_field_class($type)) {
  301. $module = $type::$module;
  302. $function = $module . '_bundle_create_user_field';
  303. $bundle = tripal_load_bundle_entity(['name' => $form['#bundle']]);
  304. $field_name = $form_values['_add_new_field']['field_name'];
  305. // If the module implements the hook then we'll have it create the
  306. // field and instance.
  307. if (function_exists($function)) {
  308. $function($form_values['_add_new_field'], $bundle);
  309. }
  310. // Otherwise, we should make a good attempt on our own.
  311. // Especially in the case of fields added via the Library directory.
  312. else {
  313. $new_field = $form_values['_add_new_field'];
  314. // Create the field.
  315. $field = [
  316. 'field_name' => $new_field['field_name'],
  317. 'type' => $new_field['type'],
  318. 'cardinality' => FIELD_CARDINALITY_UNLIMITED, // @hard-coded
  319. 'locked' => FALSE,
  320. 'storage' => [
  321. 'type' => $type::$default_settings['storage'],
  322. ],
  323. ];
  324. field_create_field($field);
  325. // Then create the instance.
  326. $instance = [
  327. 'field_name' => $new_field['field_name'],
  328. 'entity_type' => $bundle->type,
  329. 'bundle' => $bundle->name,
  330. 'label' => $new_field['label'],
  331. 'description' => $type::$default_description,
  332. 'required' => FALSE,
  333. 'settings' => [
  334. 'auto_attach' => $type::$default_instance_settings['auto_attach'],
  335. ],
  336. 'widget' => [
  337. 'type' => $new_field['widget_type'],
  338. 'settings' => [],
  339. ],
  340. 'display' => [
  341. 'default' => [
  342. 'label' => 'hidden',
  343. 'type' => $type::$default_formatter,
  344. 'settings' => [],
  345. ],
  346. ],
  347. ];
  348. field_create_instance($instance);
  349. }
  350. $destinations[] = $admin_path . '/fields/' . $field_name . '/field-shcef';
  351. $destinations[] = $admin_path . '/fields/' . $field_name;
  352. // Store new field information for any additional submit handlers.
  353. $form_state['fields_added']['_add_new_field'] = $field_name;
  354. // Unset the the _add_new_field entry so Drupal doesn't try to
  355. // Create the field.
  356. unset($form_state['values']['fields']['_add_new_field']);
  357. drupal_set_message('Please set the controlled vocabulary that best describes the data of this field. See the "Controlled Vocabulary Term" section below.', 'warning');
  358. }
  359. } catch (Exception $e) {
  360. drupal_set_message(t('There was a problem creating field %label: !message', [
  361. '%label' => $instance['label'],
  362. '!message' => $e->getMessage(),
  363. ]), 'error');
  364. }
  365. if ($destinations) {
  366. $destination = drupal_get_destination();
  367. $destinations[] = $destination['destination'];
  368. unset($_GET['destination']);
  369. $form_state['redirect'] = field_ui_get_destinations($destinations);
  370. }
  371. else {
  372. drupal_set_message(t('Your settings have been saved.'));
  373. }
  374. }
  375. }
  376. /**
  377. * Implements hook_module_implements_alter()
  378. *
  379. * We want our edits to the field_ui_field_overview_form form to occur after
  380. * all modules have implemented their changes.
  381. */
  382. function tripal_module_implements_alter(&$implementations, $hook) {
  383. if ($hook == 'form_alter') {
  384. $group = $implementations['tripal'];
  385. unset($implementations['tripal']);
  386. $implementations['tripal'] = $group;
  387. }
  388. }
  389. /**
  390. * Implements hook_field_settings_form()
  391. */
  392. function tripal_field_settings_form($field, $instance, $has_data) {
  393. $field_class = $field['type'];
  394. if (tripal_load_include_field_class($field_class)) {
  395. $field = new $field_class($field, $instance);
  396. return $field->settingsForm($has_data);
  397. }
  398. }
  399. /**
  400. * Implements hook_instance_settings_form()
  401. */
  402. function tripal_field_instance_settings_form($field, $instance) {
  403. $field_class = $field['type'];
  404. if (tripal_load_include_field_class($field_class)) {
  405. $field = new $field_class($field, $instance);
  406. return $field->instanceSettingsForm();
  407. }
  408. }
  409. /**
  410. * Validates the TripalField instance settings form.
  411. *
  412. * This function is called because the TripalField::instanceSettingsForm()
  413. * adds it to the form element. By default, Drupal does not provide a
  414. * validate hook for the instance settings form.
  415. */
  416. function tripal_field_instance_settings_form_validate($element, &$form_state, $form) {
  417. $field = $element['#field'];
  418. $instance = $element['#instance'];
  419. $field_class = $field['type'];
  420. if (tripal_load_include_field_class($field_class)) {
  421. $field = new $field_class($field, $instance);
  422. return $field->instanceSettingsFormValidate($form, $form_state);
  423. }
  424. }
  425. /**
  426. * Allows for altering of a field's instance setting form.
  427. *
  428. * This appears to be a Drupal hook but is actually a custom function created
  429. * by this module. It is called by the tripal_form_alter() function of this
  430. * module.
  431. *
  432. * Here we put additional form elements for any field, regardless if it is
  433. * a tripalField or not.
  434. *
  435. * @param $form
  436. * The form array. Alterations to the form can be made within this array.
  437. * @param $form_state
  438. * The form state array.
  439. */
  440. function tripal_field_instance_settings_form_alter(&$form, $form_state) {
  441. global $language;
  442. // It's not possible to add AJAX to a form element in the hook_form_alter
  443. // function. To make it work we have to add a process function. Inisde
  444. // of that process function is where the form additions get added that use
  445. // Ajax.
  446. $form['field_term'][$language->language][0]['#process'] = ['tripal_field_instance_settings_form_alter_process'];
  447. $form['#submit'][] = 'tripal_field_instance_settings_form_submit';
  448. }
  449. /**
  450. * Implements a process function for the instance settings form.
  451. *
  452. * See the comment in the tripal_field_instance_settings_form_alter() for
  453. * more details.
  454. */
  455. function tripal_field_instance_settings_form_alter_process($element, &$form_state, $form) {
  456. $field = $form['#field'];
  457. $instance = $form['#instance'];
  458. // Get the term for this instance.
  459. $vocabulary = '';
  460. $accession = '';
  461. $term_name = '';
  462. $term = NULL;
  463. if (array_key_exists('settings', $instance) and
  464. array_key_exists('term_vocabulary', $instance['settings'])) {
  465. $vocabulary = $instance['settings']['term_vocabulary'];
  466. $accession = $instance['settings']['term_accession'];
  467. $term_name = $instance['settings']['term_name'];
  468. $term = tripal_get_term_details($vocabulary, $accession);
  469. }
  470. // Construct a table for the vocabulary information.
  471. $headers = [];
  472. $rows = [];
  473. $rows[] = [
  474. [
  475. 'data' => 'Vocabulary',
  476. 'header' => TRUE,
  477. 'width' => '20%',
  478. ],
  479. $term['vocabulary']['name'] . ' (' . $vocabulary . ') ' . $term['vocabulary']['description'],
  480. ];
  481. $rows[] = [
  482. [
  483. 'data' => 'Term',
  484. 'header' => TRUE,
  485. 'width' => '20%',
  486. ],
  487. $vocabulary . ':' . $accession,
  488. ];
  489. $rows[] = [
  490. [
  491. 'data' => 'Name',
  492. 'header' => TRUE,
  493. 'width' => '20%',
  494. ],
  495. $term['name'],
  496. ];
  497. $rows[] = [
  498. [
  499. 'data' => 'Definition',
  500. 'header' => TRUE,
  501. 'width' => '20%',
  502. ],
  503. $term['definition'],
  504. ];
  505. $table = [
  506. 'header' => $headers,
  507. 'rows' => $rows,
  508. 'attributes' => [],
  509. 'sticky' => FALSE,
  510. 'caption' => '',
  511. 'colgroups' => [],
  512. 'empty' => '',
  513. ];
  514. $description = t('All fields attached to a Tripal-based content type must
  515. be associated with a controlled vocabulary term. Please use caution
  516. when changing the term for this field as other sites may expect this term
  517. when querying web services.');
  518. if (array_key_exists('term_fixed', $instance['settings']) and $instance['settings']['term_fixed']) {
  519. $description = t('All fields attached to a Tripal-based content type must
  520. be associated with a controlled vocabulary term. This field mapping is
  521. required and cannot be changed');
  522. }
  523. $element['term_vocabulary'] = [
  524. '#type' => 'value',
  525. '#value' => $vocabulary,
  526. ];
  527. $element['term_name'] = [
  528. '#type' => 'value',
  529. '#value' => $term_name,
  530. ];
  531. $element['term_accession'] = [
  532. '#type' => 'value',
  533. '#value' => $accession,
  534. ];
  535. $element['field_term'] = [
  536. '#type' => 'fieldset',
  537. '#title' => 'Controlled Vocabulary Term',
  538. '#description' => $description,
  539. '#prefix' => '<div id = "tripal-field-term-fieldset">',
  540. '#suffix' => '</div>',
  541. ];
  542. $element['field_term']['details'] = [
  543. '#type' => 'item',
  544. '#title' => 'Current Term',
  545. '#markup' => theme_table($table),
  546. ];
  547. // If this field mapping is fixed then don't let the user change it.
  548. if (!array_key_exists('term_fixed', $instance['settings']) or $instance['settings']['term_fixed'] != TRUE) {
  549. $element['field_term']['new_name'] = [
  550. '#type' => 'textfield',
  551. '#title' => 'Change the term',
  552. // TODO: This autocomplete path should not use Chado.
  553. '#autocomplete_path' => "admin/tripal/storage/chado/auto_name/cvterm/",
  554. ];
  555. $element['field_term']['select_button'] = [
  556. '#type' => 'button',
  557. '#value' => t('Lookup Term'),
  558. '#name' => 'select_cvterm',
  559. '#ajax' => [
  560. 'callback' => "tripal_fields_select_term_form_ajax_callback",
  561. 'wrapper' => "tripal-field-term-fieldset",
  562. 'effect' => 'fade',
  563. 'method' => 'replace',
  564. ],
  565. ];
  566. }
  567. // If a new term name has been specified by the user then give some extra
  568. // fields to clarify the term.
  569. $term_name = '';
  570. if (array_key_exists('values', $form_state) and array_key_exists('new_name', $form_state['values'])) {
  571. $term_name = array_key_exists('values', $form_state) ? $form_state['values']['new_name'] : '';
  572. }
  573. if (array_key_exists('input', $form_state) and array_key_exists('new_name', $form_state['input'])) {
  574. $term_name = array_key_exists('input', $form_state) ? $form_state['input']['new_name'] : '';
  575. }
  576. if ($term_name) {
  577. $element['field_term']['instructions'] = [
  578. '#type' => 'item',
  579. '#title' => 'Matching terms',
  580. '#markup' => t('Please select the term the best matches the
  581. content type you want to associate with this field. If the same term exists in
  582. multiple vocabularies you will see more than one option below.'),
  583. ];
  584. $match = [
  585. 'name' => $term_name,
  586. ];
  587. $terms = chado_generate_var('cvterm', $match, ['return_array' => TRUE]);
  588. $terms = chado_expand_var($terms, 'field', 'cvterm.definition');
  589. $num_terms = 0;
  590. foreach ($terms as $term) {
  591. // Save the user a click, by setting the default value as 1 if there's
  592. // only one matching term.
  593. $default = FALSE;
  594. $attrs = [];
  595. if ($num_terms == 0 and count($terms) == 1) {
  596. $default = TRUE;
  597. $attrs = ['checked' => 'checked'];
  598. }
  599. $element['field_term']['term-' . $term->cvterm_id] = [
  600. '#type' => 'checkbox',
  601. '#title' => $term->name,
  602. '#default_value' => $default,
  603. '#attributes' => $attrs,
  604. '#description' => '<b>Vocabulary:</b> ' . $term->cv_id->name . ' (' . $term->dbxref_id->db_id->name . ') ' . $term->cv_id->definition .
  605. '<br><b>Term: </b> ' . $term->dbxref_id->db_id->name . ':' . $term->dbxref_id->accession . '. ' .
  606. '<br><b>Definition:</b> ' . $term->definition,
  607. ];
  608. $num_terms++;
  609. }
  610. if ($num_terms == 0) {
  611. $element['field_term']['none'] = [
  612. '#type' => 'item',
  613. '#markup' => '<i>' . t('There is no term that matches the entered text.') . '</i>',
  614. ];
  615. }
  616. }
  617. $element['#element_validate'][] = 'tripal_field_instance_settings_form_alter_validate';
  618. return $element;
  619. }
  620. /**
  621. * Implements an AJAX callback for the tripal_field_vocab_select_term_form.
  622. */
  623. function tripal_fields_select_term_form_ajax_callback($form, $form_state) {
  624. return $form['field_term'];
  625. }
  626. /**
  627. * Validate our custom instance settings form fields.
  628. */
  629. function tripal_field_instance_settings_form_alter_validate($form, &$form_state) {
  630. // If the user clicked the submit button then we want set the
  631. // instance settings values accordingly.
  632. if (array_key_exists('clicked_button', $form_state) and $form_state['clicked_button']['#executes_submit_callback'] == TRUE) {
  633. $has_default = FALSE;
  634. if ($form_state['values']['term_vocabulary']) {
  635. $form_state['values']['instance']['settings']['term_vocabulary'] = $form_state['values']['term_vocabulary'];
  636. $form_state['values']['instance']['settings']['term_accession'] = $form_state['values']['term_accession'];
  637. $form_state['values']['instance']['settings']['term_name'] = $form_state['values']['term_name'];
  638. $has_default = TRUE;
  639. }
  640. $started_new_term = FALSE;
  641. if (array_key_exists('new_name', $form_state['values']) and $form_state['values']['new_name']) {
  642. $started_new_term = TRUE;
  643. }
  644. $num_selected = 0;
  645. $selected_term = FALSE;
  646. foreach ($form_state['input'] as $key => $value) {
  647. $matches = [];
  648. if (preg_match("/^term-(\d+)$/", $key, $matches) and
  649. $form_state['input']['term-' . $matches[1]]) {
  650. $cvterm_id = $matches[1];
  651. // TODO: this should not call a Chado function, but the autocomplete
  652. // currently uses chado cvterm IDs.
  653. $term = chado_generate_var('cvterm', ['cvterm_id' => $cvterm_id]);
  654. if ($term) {
  655. $form_state['values']['instance']['settings']['term_vocabulary'] = $term->dbxref_id->db_id->name;
  656. $form_state['values']['instance']['settings']['term_accession'] = $term->dbxref_id->accession;
  657. $form_state['values']['instance']['settings']['term_name'] = $term->name;
  658. $selected_term = TRUE;
  659. $num_selected++;
  660. $has_default = TRUE;
  661. }
  662. }
  663. }
  664. // Make sure this term is not already used.
  665. $bundle_name = $form_state['values']['instance']['bundle'];
  666. $existing_instances = field_info_instances('TripalEntity', $bundle_name);
  667. $field_term_id = $form_state['values']['instance']['settings']['term_vocabulary'] . ':' . $form_state['values']['instance']['settings']['term_accession'];
  668. $field_name = $form_state['values']['instance']['field_name'];
  669. foreach ($existing_instances as $existing_name => $existing_instance) {
  670. $existing_term_id = $existing_instance['settings']['term_vocabulary'] . ':' . $existing_instance['settings']['term_accession'];
  671. if ($existing_term_id == $field_term_id and $field_name != $existing_name) {
  672. form_set_error('term-', t('The term, !term, is already in use on this content type. A term can only be used once per content type. Please choose a different term.',
  673. ['!term' => $field_term_id]));
  674. }
  675. }
  676. if ($num_selected > 1) {
  677. form_set_error('term-', 'Please select only one term
  678. from the "Controlled Vocabulary Term" section below.');
  679. }
  680. if ($started_new_term and !$selected_term) {
  681. form_set_error('term-', 'Please select a controlled vocabulary term for
  682. from the "Controlled Vocabulary Term" section below.');
  683. }
  684. if (!$has_default) {
  685. form_set_error('new_name', 'Fields attached to this content type must ' .
  686. 'be associated with a controlled vocabulary term. Please ' .
  687. 'provide one below.');
  688. }
  689. }
  690. }
  691. /**
  692. * Custom submit function for instance settings form.
  693. */
  694. function tripal_field_instance_settings_form_submit($form, &$form_state) {
  695. }
  696. /**
  697. *
  698. */
  699. function tripal_field_widget_form_validate($element, &$form_state, $form) {
  700. $field = $element['#field'];
  701. $instance = $element['#instance'];
  702. $langcode = (isset($element['#language'])) ? $element['#language'] : LANGUAGE_NONE;
  703. $delta = (isset($element['#delta'])) ? $element['#delta'] : 0;
  704. $widget_class = $instance['widget']['type'];
  705. tripal_load_include_field_class($widget_class);
  706. if (class_exists($widget_class)) {
  707. $widget = new $widget_class($field, $instance);
  708. // Set the validation function for this field widget depending on the
  709. // form displaying the widget.
  710. if ($form['#form_id'] == 'field_ui_field_edit_form') {
  711. $widget->validateDefaults($element, $form, $form_state, $langcode, $delta);
  712. }
  713. else {
  714. $widget->validate($element, $form, $form_state, $langcode, $delta);
  715. }
  716. }
  717. }
  718. /**
  719. * Implements hook_field_settings_form_validate().
  720. *
  721. * This function is called because the TripalField::settingsForm()
  722. * adds it to the form element. By default, Drupal does not provide a
  723. * validate hook for the settings form.
  724. */
  725. function tripal_field_settings_form_validate($element, &$form_state, $form) {
  726. $field = $element['#field'];
  727. $instance = $element['#instance'];
  728. $field_class = $field['type'];
  729. if (tripal_load_include_field_class($field_class)) {
  730. $field = new $field_class($field, $instance);
  731. $field->settingsFormValidate($form, $form_state);
  732. }
  733. }
  734. /**
  735. * Implements hook_field_formatter_settings_summary().
  736. */
  737. function tripal_field_formatter_settings_summary($field, $instance, $view_mode) {
  738. $formatter_class = $instance['display']['default']['type'];
  739. if (tripal_load_include_field_class($formatter_class)) {
  740. $formatter = new $formatter_class($field, $instance);
  741. return $formatter->settingsSummary($view_mode);
  742. }
  743. }
  744. /**
  745. * Implements hook_field_formatter_settings_form().
  746. */
  747. function tripal_field_formatter_settings_form($field, $instance,
  748. $view_mode, $form, &$form_state) {
  749. $formatter_class = $instance['display']['default']['type'];
  750. if (tripal_load_include_field_class($formatter_class)) {
  751. $formatter = new $formatter_class($field, $instance);
  752. $elements = $formatter->settingsForm($view_mode, $form, $form_state);
  753. }
  754. return $elements;
  755. }
  756. /**
  757. * Implements hook_field_widget_form().
  758. */
  759. function tripal_field_widget_form(&$form, &$form_state, $field,
  760. $instance, $langcode, $items, $delta, $element) {
  761. $widget_class = $instance['widget']['type'];
  762. tripal_load_include_field_class($widget_class);
  763. if (class_exists($widget_class)) {
  764. $widget = new $widget_class($field, $instance);
  765. $widget->form($element, $form, $form_state, $langcode, $items, $delta, $element);
  766. }
  767. return $element;
  768. }
  769. /**
  770. * Implements hook_field_widget_form_alter().
  771. */
  772. function tripal_field_widget_form_alter(&$element, &$form_state, $context) {
  773. if (array_key_exists('#field_name', $element)) {
  774. $field_name = $element['#field_name'];
  775. $matches = [];
  776. if (preg_match('/(.+?)__(.+?)$/', $field_name, $matches)) {
  777. $tablename = $matches[1];
  778. $colname = $matches[2];
  779. $schema = chado_get_schema($tablename);
  780. if (!$schema) {
  781. return;
  782. }
  783. // The timelastmodified field exists in many Chado tables. We want
  784. // the form element to update to the most recent time rather than the time
  785. // in the database.
  786. if ($colname == 'timelastmodified' and $schema['fields'][$colname]['type'] == 'datetime') {
  787. // We want the default value for the field to be the current time.
  788. $element['#default_value']['value'] = format_date(time(), 'custom', "Y-m-d H:i:s", 'UTC');
  789. $element['#date_items']['value'] = $element['#default_value']['value'];
  790. }
  791. // We want the date combo fieldset to be collaspible so we will
  792. // add our own theme_wrapper to replace the one added by the date
  793. // module.
  794. if (array_key_exists($colname, $schema['fields']) and $schema['fields'][$colname]['type'] == 'datetime') {
  795. $element['#theme_wrappers'] = ['tripal_chado_date_combo'];
  796. }
  797. }
  798. }
  799. }
  800. /**
  801. * Implements hook_field_validate()
  802. */
  803. function tripal_field_validate($entity_type, $entity, $field, $instance,
  804. $langcode, $items, &$errors) {
  805. $field_type = $field['type'];
  806. if (tripal_load_include_field_class($field_type)) {
  807. $tfield = new $field_type($field, $instance);
  808. $tfield->validate($entity_type, $entity, $langcode, $items, $errors);
  809. }
  810. }
  811. /**
  812. * Implements hook_form_FORM_ID_alter().
  813. *
  814. * The field_ui_display_overview_form is used for formatting the display
  815. * or layout of fields attached to an entity and shown on the entity view page.
  816. *
  817. * This function removes the cvterm class and property adder field as those are
  818. * really not meant for users to show or manage.
  819. */
  820. function tripal_form_field_ui_display_overview_form_alter(&$form, &$form_state, $form_id) {
  821. // Remove the kvproperty_addr field as it isn't ever displayed. It's just used
  822. // on the add/edit form of an entity for adding new property fields.
  823. $fields_names = element_children($form['fields']);
  824. foreach ($fields_names as $field_name) {
  825. $field_info = field_info_field($field_name);
  826. if ($field_info) {
  827. if ($field_info['type'] == 'kvproperty_adder') {
  828. unset($form['fields'][$field_name]);
  829. }
  830. if ($field_info['type'] == 'cvterm_class_adder') {
  831. unset($form['fields'][$field_name]);
  832. }
  833. }
  834. }
  835. }
  836. /**
  837. * Theme function for all TripalFieldWidget objects.
  838. *
  839. * @param $variables
  840. */
  841. function theme_tripal_field_default($variables) {
  842. $element = $variables['element'];
  843. $field = $element['#field'];
  844. $instance = $element['#instance'];
  845. $widget_class = $element['#field_name'] . '_widget';
  846. $langcode = $element['#language'];
  847. $delta = $element['#delta'];
  848. tripal_load_include_field_class($widget_class);
  849. if (class_exists($widget_class)) {
  850. $widget = new $widget_class($field, $instance);
  851. return $widget->theme($element);
  852. }
  853. }