chado_linker__prop_adder.inc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. <?php
  2. class chado_linker__prop_adder extends TripalField {
  3. // The default lable for this field.
  4. public static $default_label = 'Add a Property Type';
  5. // The default description for this field.
  6. public static $default_description = 'This record may have any number of properties. Use
  7. this field to first add the type.';
  8. // Add any default settings elements. If you override the globalSettingsForm()
  9. // or the instanceSettingsForm() functions then you need to be sure that
  10. // any settings you want those functions to manage are listed in this
  11. // array.
  12. public static $default_settings = array(
  13. 'chado_table' => '',
  14. 'chado_column' => '',
  15. 'base_table' => '',
  16. );
  17. // Set this to the name of the storage backend that by default will support
  18. // this field.
  19. public static $default_storage = 'field_chado_storage';
  20. /**
  21. * @see TripalField::formatterView()
  22. */
  23. public function formatterView(&$element, $entity_type, $entity, $langcode, $items, $display) {
  24. // This field should never be viewed. It's to help add new properties
  25. // when editing an entity. So return nothing.
  26. return '';
  27. }
  28. /**
  29. * @see TripalField::widgetForm()
  30. */
  31. public function widgetForm(&$widget, &$form, &$form_state, $langcode, $items, $delta, $element) {
  32. parent::widgetForm($widget, $form, $form_state, $langcode, $items, $delta, $element);
  33. $field_name = $widget['#field_name'];
  34. $widget['#type'] = 'fieldset';
  35. $widget['#title'] = $element['#title'];
  36. $widget['#description'] = $element['#description'];
  37. $widget['#group'] = 'entity_form_vtabs';
  38. $widget['kvproperty_instructions'] = array(
  39. '#type' => 'item',
  40. '#markup' => t('You may add additional properties to this form by
  41. providing a property name (from a vocabulary) in the field below
  42. and clicking the "Lookup Term" button. Terms that match the value
  43. entered will be displayed for selection. After selecting the
  44. appropriate term click the "Use this term" button and a
  45. new field will be added to the form above for the property you selected.
  46. In the future, this field will be present for all records
  47. of this type.'),
  48. );
  49. $term_name = isset($form_state['values'][$field_name]['und'][0]['wrapper']['term_name']) ? $form_state['values'][$field_name]['und'][0]['wrapper']['term_name'] : '';
  50. // Drupal's vertical feild set is a bit quirky in that we can't just
  51. // add a prefix and suffix to the weidget. If we do, then the
  52. // field doesn't show up on the page after an AJAX call. We have to add
  53. // an internal wrapper (below) for AJAX calls.
  54. $widget['wrapper'] = array(
  55. '#prefix' => "<span id='$field_name-lookup-form'>",
  56. '#suffix' => '</span>',
  57. );
  58. // If no term has been selected yet then provide the auto complete field.
  59. $widget['wrapper']['term_name'] = array(
  60. '#title' => t('Term'),
  61. '#type' => 'textfield',
  62. '#description' => t("The content type must be the name of a term in
  63. a controlled vocabulary and the controlled vocabulary should
  64. already be loaded into Tripal. For example, to create a content
  65. type for storing 'genes', use the 'gene' term from the
  66. Sequence Ontology (SO)."),
  67. '#default_value' => $term_name,
  68. '#autocomplete_path' => "admin/tripal/storage/chado/auto_name/cvterm/",
  69. );
  70. $widget['wrapper']['select_button'] = array(
  71. '#type' => 'button',
  72. '#value' => t('Lookup Term'),
  73. '#name' => 'select_cvterm',
  74. '#ajax' => array(
  75. 'callback' => "tripal_chado_prop_adder_form_ajax_callback",
  76. 'wrapper' => "$field_name-lookup-form",
  77. 'effect' => 'fade',
  78. 'method' => 'replace'
  79. ),
  80. );
  81. if ($term_name) {
  82. $widget['wrapper']['terms_list'] = array(
  83. '#type' => 'fieldset',
  84. '#title' => t('Matching Terms'),
  85. '#description' => t('Please select the term the best matches the
  86. content type you want to create. If the same term exists in
  87. multiple vocabularies you will see more than one option below.')
  88. );
  89. $match = array(
  90. 'name' => $term_name,
  91. );
  92. $terms = chado_generate_var('cvterm', $match, array('return_array' => TRUE));
  93. $terms = chado_expand_var($terms, 'field', 'cvterm.definition');
  94. $num_terms = 0;
  95. foreach ($terms as $term) {
  96. // Save the user a click by setting the default value as 1 if there's
  97. // only one matching term.
  98. $default = FALSE;
  99. $attrs = array();
  100. if ($num_terms == 0 and count($terms) == 1) {
  101. $default = TRUE;
  102. $attrs = array('checked' => 'checked');
  103. }
  104. $widget['wrapper']['terms_list']['term-' . $term->cvterm_id] = array(
  105. '#type' => 'checkbox',
  106. '#title' => $term->name,
  107. '#default_value' => $default,
  108. '#attributes' => $attrs,
  109. '#description' => '<b>Vocabulary:</b> ' . $term->cv_id->name . ' (' . $term->dbxref_id->db_id->name . ') ' . $term->cv_id->definition .
  110. '<br><b>Term: </b> ' . $term->dbxref_id->db_id->name . ':' . $term->dbxref_id->accession . '. ' .
  111. '<br><b>Definition:</b> ' . $term->definition,
  112. );
  113. $num_terms++;
  114. }
  115. if ($num_terms == 0) {
  116. $widget['wrapper']['terms_list']['none'] = array(
  117. '#type' => 'item',
  118. '#markup' => '<i>' . t('There is no term that matches the entered text.') . '</i>'
  119. );
  120. }
  121. else {
  122. $widget['wrapper']['cardinality'] = array(
  123. '#title' => t('Number of Values'),
  124. '#type' => 'textfield',
  125. '#description' => t("A number of 1 or more indicating the number of values allowed for this property. Enter -1 for unlimited values"),
  126. '#size' => 10,
  127. '#default_value' => 1,
  128. );
  129. // Add in the button for the cases of no terms or too many.
  130. $widget['wrapper']['submit_button'] = array(
  131. '#type' => 'submit',
  132. '#value' => t('Use this term'),
  133. '#name' => 'use_term_button',
  134. );
  135. }
  136. }
  137. }
  138. /**
  139. * @see TripalField::widgetFormValidate()
  140. */
  141. public function widgetFormValidate($form, &$form_state, $entity_type, $entity, $langcode, $delta) {
  142. $field = $this->field;
  143. $field_name = $field['field_name'];
  144. if ($form_state['triggering_element']['#name'] == 'use_term_button') {
  145. // If the user has clicked the 'user_term_button' then we need to makes
  146. // sure that the cardinality textbox contains a positive integer.
  147. $cardinality = $form_state['values'][$field_name][$langcode][$delta]['wrapper']['cardinality'];
  148. if (!preg_match('/^-*\d+$/', $cardinality) or $cardinality < -2 or $cardinality == 0) {
  149. form_set_error("$field_name][$langcode][$delta][wrapper][cardinality", "Please provide positive number for the number of values, or -1 for unlimited.");
  150. }
  151. // Get selected terms
  152. $terms_list = $form_state['values'][$field_name][$langcode][$delta]['wrapper']['terms_list'];
  153. $counter = 0;
  154. $selected_term_id = '';
  155. foreach ($terms_list AS $term => $selected) {
  156. if ($selected) {
  157. $selected_term_id = str_replace('term-', '', $term);
  158. $counter ++;
  159. }
  160. }
  161. // Make sure at least a term is selected
  162. if ($counter == 0) {
  163. form_set_error("$field_name][$langcode][$delta][wrapper][terms_list", "Please select a term.");
  164. }
  165. // Make sure only one term is selected
  166. if ($counter > 1) {
  167. form_set_error("$field_name][$langcode][$delta][wrapper][terms_list", "Please select only one term.");
  168. }
  169. // Make sure this property doesn't already have a field
  170. if (key_exists($field_name . '__' . $selected_term_id, $form_state['values'])) {
  171. form_set_error("$field_name][$langcode][$delta][wrapper][terms_name", "Property already exists. Please select another term.");
  172. }
  173. }
  174. }
  175. /**
  176. * @see TripalField::widgetFormValidate
  177. */
  178. public function validate($entity_type, $entity, $field, $items, &$errors) {
  179. // This form doesn't manage data in the database so we don't need to
  180. // validate anything here.
  181. }
  182. /**
  183. * @see TripalField::widgetFormSubmit()
  184. */
  185. public function widgetFormSubmit($form, &$form_state, $entity_type, $entity, $langcode, $delta) {
  186. // Add the new field to the entity but only if the property adder button
  187. // was clicked
  188. if (!array_key_exists('triggering_element', $form_state) or
  189. $form_state['triggering_element']['#name'] != 'use_term_button') {
  190. return;
  191. }
  192. else {
  193. // Because we're going to add a new property we want to rebuild the form
  194. // rather than have it fully submit.
  195. $form_state['rebuild'] = TRUE;
  196. }
  197. // Get the table and base table.
  198. $base_table = $this->instance['settings']['base_table'];
  199. // Get the term for the property
  200. $field = $this->field;
  201. $fname = $field['field_name'];
  202. $cardinality = $form_state['values'][$fname][$langcode][$delta]['wrapper']['cardinality'];
  203. // Get selected terms
  204. $terms_list = $form_state['values'][$fname][$langcode][$delta]['wrapper']['terms_list'];
  205. $selected_term_id = '';
  206. foreach ($terms_list AS $term => $selected) {
  207. if ($selected) {
  208. $selected_term_id = str_replace('term-', '', $term);
  209. }
  210. }
  211. $cvterm = chado_generate_var('cvterm', array('cvterm_id' => $selected_term_id));
  212. // Generate the name for the property table and the field name that we'll
  213. // be creating.
  214. $prop_table = $base_table . 'prop';
  215. $field_name = $prop_table . '__' . $cvterm->cvterm_id;
  216. // The field name is the table name in this case. We want to get the
  217. // primary key as this should be the field that maps th the value.
  218. $schema = chado_get_schema($prop_table);
  219. $pkey = $schema['primary key'][0];
  220. // Add the field if it doesn't already exists.
  221. $field = field_info_field($field_name);
  222. if (!$field) {
  223. $field = field_create_field(array(
  224. 'field_name' => $field_name,
  225. 'type' => 'chado_linker__prop',
  226. 'cardinality' => $cardinality,
  227. 'locked' => FALSE,
  228. 'storage' => array(
  229. 'type' => 'field_chado_storage',
  230. ),
  231. 'settings' => array(
  232. 'chado_table' => $prop_table,
  233. 'chado_column' => $pkey,
  234. 'base_table' => $base_table,
  235. 'semantic_web' => $cvterm->dbxref_id->db_id->name . ':' . $cvterm->dbxref_id->accession,
  236. ),
  237. ));
  238. }
  239. // Create an instance of the field.
  240. $instance = field_info_instance($entity_type, $field_name, $entity->bundle);
  241. if (!$instance) {
  242. $instance = field_create_instance(array(
  243. 'field_name' => $field_name,
  244. 'entity_type' => 'TripalEntity',
  245. 'bundle' => $entity->bundle,
  246. 'label' => ucfirst(preg_replace('/_/', ' ', $cvterm->name)),
  247. 'description' => $cvterm->definition ? $cvterm->definition : '',
  248. 'required' => FALSE,
  249. 'settings' => array(
  250. 'auto_attach' => FALSE,
  251. ),
  252. 'widget' => array(
  253. 'type' => 'chado_linker__prop_widget',
  254. 'settings' => array(
  255. 'display_label' => 1,
  256. ),
  257. ),
  258. 'display' => array(
  259. 'default' => array(
  260. 'label' => 'inline',
  261. 'type' => 'chado_linker__prop_formatter',
  262. 'settings' => array(),
  263. ),
  264. ),
  265. ));
  266. }
  267. }
  268. }
  269. /**
  270. *
  271. */
  272. function tripal_chado_prop_adder_form_ajax_callback($form, $form_state) {
  273. $field_name = $form_state['triggering_element']['#parents'][0];
  274. // Because this field is inside a vertical fieldset we can't just
  275. // return $form[$field_name]. We have set the AJAX call to replace
  276. // everything inside of the 'wrapper' element, so we must return that.
  277. return $form[$field_name]['und'][0]['wrapper'];
  278. }