chado_linker__prop_adder.inc 12 KB

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