Преглед на файлове

Merge pull request #664 from tripal/revert-663-revert-628-541-tv3-ajax_fields

Remove empty fields from view when loaded via ajax REDUX (revert revert)
Lacey-Anne Sanderson преди 6 години
родител
ревизия
3606ca5cf8
променени са 37 файла, в които са добавени 642 реда и са изтрити 272 реда
  1. 1 3
      docs/user_guide.rst
  2. BIN
      docs/user_guide/configuring_page_display.5.png
  3. 14 0
      docs/user_guide/content_types.rst
  4. 0 0
      docs/user_guide/content_types/configuring_page_display.1.png
  5. 0 0
      docs/user_guide/content_types/configuring_page_display.2.png
  6. 0 0
      docs/user_guide/content_types/configuring_page_display.3.rearrange.png
  7. 0 0
      docs/user_guide/content_types/configuring_page_display.4.png
  8. 4 14
      docs/user_guide/content_types/configuring_page_display.rst
  9. 0 0
      docs/user_guide/content_types/creating_content.create1.png
  10. 0 0
      docs/user_guide/content_types/creating_content.create2.png
  11. 0 0
      docs/user_guide/content_types/creating_content.create3.png
  12. 0 0
      docs/user_guide/content_types/creating_content.create4.png
  13. 0 0
      docs/user_guide/content_types/creating_content.create5.png
  14. 0 0
      docs/user_guide/content_types/creating_content.create6.png
  15. 0 0
      docs/user_guide/content_types/creating_content.create7.png
  16. 0 0
      docs/user_guide/content_types/creating_content.rst
  17. BIN
      docs/user_guide/content_types/field_loading.empty_ajax.png
  18. 26 0
      docs/user_guide/content_types/field_loading.rst
  19. 0 0
      docs/user_guide/content_types/setting_page_urls.1.png
  20. 0 0
      docs/user_guide/content_types/setting_page_urls.2.png
  21. 0 0
      docs/user_guide/content_types/setting_page_urls.rst
  22. 0 16
      tripal/api/tripal.entities.api.inc
  23. 53 0
      tripal/api/tripal.fields.api.inc
  24. 84 56
      tripal/includes/TripalBundleUIController.inc
  25. 6 5
      tripal/includes/TripalEntityController.inc
  26. 46 16
      tripal/includes/TripalEntityUIController.inc
  27. 100 36
      tripal/includes/tripal.entity.inc
  28. 0 26
      tripal/includes/tripal.fields.inc
  29. 202 21
      tripal/theme/js/tripal.js
  30. 22 0
      tripal/tripal.install
  31. 38 40
      tripal/tripal.module
  32. 0 6
      tripal_chado/includes/TripalFields/schema__publication/schema__publication.inc
  33. 0 6
      tripal_chado/includes/TripalFields/sio__annotation/sio__annotation.inc
  34. 4 0
      tripal_ds/theme/css/tripal_ds.css
  35. 1 0
      tripal_ds/theme/js/tripal_ds.js
  36. 35 21
      tripal_ds/tripal_ds.module
  37. 6 6
      tripal_ws/includes/TripalWebService/TripalContentService_v0_1.inc

+ 1 - 3
docs/user_guide.rst

@@ -12,9 +12,7 @@ User's Guide
    user_guide/drupal_overview
    user_guide/example_genomics
    user_guide/learn_chado
-   user_guide/creating_content
-   user_guide/setting_page_urls
-   user_guide/configuring_page_display
+   user_guide/content_types
    user_guide/mviews
    user_guide/searching
    user_guide/job_management

BIN
docs/user_guide/configuring_page_display.5.png


+ 14 - 0
docs/user_guide/content_types.rst

@@ -0,0 +1,14 @@
+Working with Content Types
+==========================
+
+New in Tripal v3 is the ability to create your own content types and manage their display!  In previous versions of Tripal a bit of PHP programming was necessary. This is no longer the case.  Tripal v3 comes pre-populated with a variety of content types and provides a web interface that allows you to create your own.  This section provides details for working with content types in Tripal.
+
+.. toctree::
+   :maxdepth: 1
+   :caption: Table of Contents
+   :glob:
+
+   ./content_types/creating_content
+   ./content_types/setting_page_urls
+   ./content_types/configuring_page_display
+   ./content_types/field_loading

+ 0 - 0
docs/user_guide/configuring_page_display.1.png → docs/user_guide/content_types/configuring_page_display.1.png


+ 0 - 0
docs/user_guide/configuring_page_display.2.png → docs/user_guide/content_types/configuring_page_display.2.png


+ 0 - 0
docs/user_guide/configuring_page_display.3.rearrange.png → docs/user_guide/content_types/configuring_page_display.3.rearrange.png


+ 0 - 0
docs/user_guide/configuring_page_display.4.png → docs/user_guide/content_types/configuring_page_display.4.png


+ 4 - 14
docs/user_guide/configuring_page_display.rst → docs/user_guide/content_types/configuring_page_display.rst

@@ -1,16 +1,12 @@
-
-Configuring Page Display
-=========================
-
-
-This is one of the many new exciting features of Tripal v3.x. In this version of Tripal we have taken integration with Drupal Fields to a whole new level representing each piece of content (in Chado or otherwise) as a Drupal Field. What this means for site builders is unprecendented control over content display and arrangement through the administrative user interface --No more editing PHP template files to change the order, grouping or wording of content!
+Configuring Page Layout
+=======================
+This is one of the many new exciting features of Tripal v3. Integration with Drupal Fields has gone to a whole new level. Site builders have unprecendented control over the display of each piece of data through the administrative user interface. Previously, site builders were required to edit PHP template files to change the order, grouping or wording of content.
 
 You can configure the display of a given Tripal Content Type by navigating to **Structure → Tripal Content Types** and then selecting the **Manage Display** link beside the content type you would like to configure.
 
 .. image:: ./configuring_page_display.1.png
 
 
-
 The Manage Display User Interface lists each Drupal Field in the order they will be displayed on the page. Fields are grouped into Tripal Panes by the Tripal DS module and the page is automatically divided into a right and left column. By default the left column contains the table of contents which lists the Tripal Panes available to the user in the order they are listed in this UI. The following screenshots are using the Analysis Content Type for demonstatration.
 
 .. image:: configuring_page_display.2.png
@@ -39,7 +35,7 @@ There may also be data you want to collect from your user but don't want to disp
 Changing Tripal Pane Names
 --------------------------
 
-The name of a Tripal Pane is displayed both in the header of the Pane itself and in the Table of Contents. To change this name, click the gear button to the far right of the Tripal Pane you would like to change. This will bring up a blue pane of settings. Changing the Field Group Label will change the display name of the pane. For example, the following screenshot shows how you would change the "Cross References" Tripal Pane to be labeled "External Resources" instead if that it what you prefer. Then just click the Update button to see your changes take effect.
+If you enabled the `tripal_ds` module during installation then you will have what is called **Panes** into which fields can be grouped. With the `tripal_ds` module all field come pre-organizd into panes.  The name of a pane is displayed both in the header of the pane itself and in the Table of Contents. To change this name, click the gear button to the far right of the Tripal Pane you would like to change. This will bring up a blue pane of settings. Changing the Field Group Label will change the display name of the pane. For example, the following screenshot shows how you would change the "Cross References" Tripal Pane to be labeled "External Resources" instead if that it what you prefer. Then just click the Update button to see your changes take effect.
 
 .. image:: ./configuring_page_display.4.png
 
@@ -49,9 +45,3 @@ Display/Hide Tripal Panes on Page Load
 
 You can also easily control which Tripal Panes you would like displayed to the user on initial page load. By default the Summary Pane is the only one configured to show by default. However, if you would prefer for all panes or even a specific subset of panes to show by default, you can simply click the gear button to the far right of each Tripal Pane you want displayed by default and uncheck the "Hide panel on page load" checkbox. This gives you complete control over which panes you want your user to see first. If more then one pane is displayed by default then they will be shown in the order they are listed on the Manage Display UI.
 
-Display/Hide Empty Fields
--------------------------
-
-By default Tripal v3 hides all empty fields from the user. However like most behaviour in Tripal, this can be configured. If you would prefer to show all fields to the user regardless of whether there is content for that particular page, then navigate to ``Structure → Tripal Content Types`` and then click on the edit link beside the Tripal Content Type you would like to show empty fields for. Near the bottom of this form is a **Field Display** drop-down. Just change this drop-down to "show empty fields" and then click **Save Content Type**. As an example, we have changed this setting for the organism content type and, as you can see below, now you can see all fields (including empty fields like cross references and relationships) available to the organism content type.
-
-.. image:: ./configuring_page_display.5.png

+ 0 - 0
docs/user_guide/creating_content.create1.png → docs/user_guide/content_types/creating_content.create1.png


+ 0 - 0
docs/user_guide/creating_content.create2.png → docs/user_guide/content_types/creating_content.create2.png


+ 0 - 0
docs/user_guide/creating_content.create3.png → docs/user_guide/content_types/creating_content.create3.png


+ 0 - 0
docs/user_guide/creating_content.create4.png → docs/user_guide/content_types/creating_content.create4.png


+ 0 - 0
docs/user_guide/creating_content.create5.png → docs/user_guide/content_types/creating_content.create5.png


+ 0 - 0
docs/user_guide/creating_content.create6.png → docs/user_guide/content_types/creating_content.create6.png


+ 0 - 0
docs/user_guide/creating_content.create7.png → docs/user_guide/content_types/creating_content.create7.png


+ 0 - 0
docs/user_guide/creating_content.rst → docs/user_guide/content_types/creating_content.rst


BIN
docs/user_guide/content_types/field_loading.empty_ajax.png


+ 26 - 0
docs/user_guide/content_types/field_loading.rst

@@ -0,0 +1,26 @@
+Hide Empty Fields and AJAX loading
+==================================
+
+Tripal provides two additional controls for display of fields on a page:
+
+* Hiding fields with no data.
+* Loading fields using AJAX.
+
+You will find two check boxes when editing any content page that gives you these controls.  Navigate to ``Structure → Tripal Content Types`` and then click on any Tripal Content Type.  You will see options similar to the following:
+
+.. image:: ./field_loading.empty_ajax.png
+
+
+Hiding Empty Fields
+-------------------
+The previous sections of this guide instructed how to rearrange fields on a page, hide their titles, and organize them into panes.  However, while there are many fields many of them may not have any data.  All of these fields are present because the data store (e.g. Chado) has the capacity to house the type of data the fields represent, but if you did not load data appropriate for those fields then they will have no data.  
+
+By default Tripal v3 hides all empty fields from the user. However if you would prefer to show all fields to the user regardless of whether there is content for that particular page edit the content type and click the box labeled `Hide Empty Fields` and click the `Save` button at the bottom.  The next time anyone loads any page for the given content type all fields will be shown regardless if they have data.
+
+Using AJAX to Load Fields
+-------------------------
+Depending on the number of fields for your content type and the amount of data that those fields contain you may notice that page loads can take a few seconds to load. AJAX is a method to help decrease load times by allowing the page to load quickly with minimal data and allowing fields with larger amounts of data to load after the initial page load.  Tripal has this setting enabled by default. but you can disable this feature.  Similar to the check box for hiding fields, there is a check box on the content type edit page labeled `Load field using AJAX`. Remove the check for box to disable all AJAX loading of fields and save the content type settings.
+
+.. note::
+ 
+  You can control AJAX loading and hiding of empty fields differently for each conent type.

+ 0 - 0
docs/user_guide/setting_page_urls.1.png → docs/user_guide/content_types/setting_page_urls.1.png


+ 0 - 0
docs/user_guide/setting_page_urls.2.png → docs/user_guide/content_types/setting_page_urls.2.png


+ 0 - 0
docs/user_guide/setting_page_urls.rst → docs/user_guide/content_types/setting_page_urls.rst


+ 0 - 16
tripal/api/tripal.entities.api.inc

@@ -1006,22 +1006,6 @@ function tripal_get_bundle_variable($variable_name, $bundle_id, $default = FALSE
 function tripal_set_bundle_variable($variable_name, $bundle_id, $value) {
   $variable = tripal_get_variable($variable_name);
 
-  if (!$variable) {
-    if($variable_name === 'hide_empty_field'){
-      tripal_insert_variable(
-        'hide_empty_field',
-        'Structure->Tripal Content Type->edit checkbox to hide empty fields for that bundle.'
-      );
-      $variable = tripal_get_variable($variable_name);
-      if (!$variable) {
-        return FALSE;
-      }
-    }
-    else {
-      return FALSE;
-    }
-  }
-
   // And then we need to write the new format to the tripal_bundle_variables
   // table.
   $record = array(

+ 53 - 0
tripal/api/tripal.fields.api.inc

@@ -107,6 +107,59 @@ function hook_bundle_instances_info($entity_type, $bundle) {
 
 }
 
+/**
+ * Indicate if a field has an empty value.
+ *
+ * By default, all field values are attached to an entity in the form
+ * $entity->{field_name}[{language}][{delta}].   Tyipcally a field witll then
+ * have a 'value' element:  $entity->{field_name}[{language}][{delta}]['value']
+ * and if that value is empty then the field is considered empty by Tripal.
+ * By default the tripal_field-is_empty() function is used to check all
+ * fields to see if they are empty. However, this fhook can be implemented by
+ * any module to override that behavior.
+ *
+ * @param $field
+ *   A field array.
+ * @param $items
+ *   The array of items attached to entity.
+ * @param $delta
+ *   The specific value to check.  For fields with cardinality greater than
+ *   1 then each value can be checked. Defaults to 0 indicating it will check
+ *   the first value.
+ *
+ * @return
+ *   TRUE if the field value is empty for the given delta, and FALSE if not
+ *   empty.
+ *   
+ * @ingroup tripal_fields_api
+ */
+function tripal_field_is_empty($field, $items, $delta = 0) {
+  
+  // If the $items argument is empty then return TRUE.
+  if (!$items) {
+    return TRUE;
+  }
+  
+  // If the field is a tripal field storage API field and there
+  // is no value field then the field is empty.
+  if (array_key_exists('storage', $field) and 
+      array_key_exists('tripal_storage_api', $field['storage']['settings']) and 
+      !array_key_exists('value', $items[$delta])) {
+    return TRUE;
+  }
+  
+  // If there is a value field but there's nothing in it, the the field is
+  // empty.
+  if (array_key_exists($delta, $items) and 
+      array_key_exists('value', $items[$delta]) and 
+      empty($items[$delta]['value'])) {
+    return TRUE;
+  }
+  
+  // Otherwise, the field is not empty.
+  return FALSE;
+}
+
 /**
  * Retrieves a list of TripalField types.
  *

+ 84 - 56
tripal/includes/TripalBundleUIController.inc

@@ -56,7 +56,7 @@ class TripalBundleUIController extends EntityDefaultUIController {
 
     return $forms;
   }
-  
+
   /**
    * Renders the Bundle overview table
    */
@@ -64,17 +64,17 @@ class TripalBundleUIController extends EntityDefaultUIController {
     $entities = entity_load($this->entityType, FALSE, $conditions);
 
     // Sort the entities by label.
-    $sorted = [];    
+    $sorted = [];
     foreach ($entities as $entity) {
       $sorted[$entity->label] = $entity;
     }
     ksort($sorted, SORT_STRING|SORT_FLAG_CASE);
-    
+
     $rows = array();
     foreach ($sorted as $entity) {
       // Get the term for this content type
       $additional_cols = [$entity->term->name . ' (' . l($entity->accession, 'cv/lookup/' . $entity->term->vocab->vocabulary . '/' . $entity->term->accession) . ')'];
-      $rows[] = $this->overviewTableRow($conditions, 
+      $rows[] = $this->overviewTableRow($conditions,
         entity_id($this->entityType, $entity), $entity,
         $additional_cols);
     }
@@ -96,7 +96,7 @@ class TripalBundleUIController extends EntityDefaultUIController {
       'data' => t('Operations'),
       'colspan' => $colspan,
     );
-    
+
     $render = array(
       '#theme' => 'table',
       '#header' => $header,
@@ -220,16 +220,40 @@ function tripal_tripal_bundle_form($form, &$form_state, $entityDataType) {
     $form['description']['#default_value'] = tripal_get_bundle_variable('description', $bundle->id, '');
   }
 
-  $empty_fields = tripal_get_bundle_variable('hide_empty_field', $bundle->id, '');
+  $hide_empty_field_var = tripal_get_bundle_variable('hide_empty_field', $bundle->id);
+  if ($hide_empty_field_var != 0) {
+    $hide_empty_field_var = TRUE;
+  }
+
+  $ajax_field_var = tripal_get_bundle_variable('ajax_field', $bundle->id);
+  if ($ajax_field_var != 0) {
+    $ajax_field_var = TRUE;
+  }
+
   $form['hide_empty_field'] = array(
-    '#type' => 'select',
-    '#title' => t('Field Display'),
-    '#options' => array(
-      'hide' => t('Hide empty fields'),
-      'show' => t('Show empty fields'),
+    '#type' => 'checkbox',
+    '#title' => t('Hide empty fields'),
+    '#description' => t('Uncheck this box if you would like to show all empty fields.'),
+    '#default_value' => $hide_empty_field_var,
+    '#weight' => 9,
+  );
+
+  $form['ajax_field'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Load field using AJAX'),
+    '#description' => t('Uncheck this box if you do not want field data to load by ajax, this may signifiantly increase page load times.'),
+    '#default_value' => $ajax_field_var,
+    '#weight' => 9,
+  );
+
+  $form['ajax_field disclaimer'] = array(
+    '#type' => 'item',
+    '#markup' => t(
+      '<p>Please note, if both "Hide empty fields" and "Load field using AJAX" 
+      are checked empty fields will be hidden using javascript which may result
+       in some jumpiness of content as the page finishes loading.</p>'
     ),
-    '#description' => t('Choose either to show or hide all empty fields.  If "Show empty fields" is selected then fields will be loaded via AJAX to help speed page loads.'),
-    '#default_value' => !empty($empty_fields) ? array($empty_fields,) : array('hide',),
+    '#weight' => 10,
   );
 
   $form['additional_settings'] = array(
@@ -347,7 +371,7 @@ function tripal_tripal_bundle_form($form, &$form_state, $entityDataType) {
   $form['url']['token_display'] = array(
     '#type' => 'fieldset',
     '#title' => t('Available Tokens'),
-    '#description' => t('Copy the token and paste it into the "URL Alias Pattern" ' . 
+    '#description' => t('Copy the token and paste it into the "URL Alias Pattern" ' .
       'text field above. Please choose tokens that will guarantee a unique URL.'),
     '#collapsible' => TRUE,
     '#collapsed' => TRUE
@@ -393,7 +417,10 @@ function tripal_tripal_bundle_form_validate($form, $form_state) {
   // VALIDATE: That there is a value passed for the hide_empty_field option. If
   // no value passed default to hide field.
   if(empty($form_state['values']['hide_empty_field'])){
-    $form_state['values']['hide_empty_field'] = 'hide';
+    $form_state['values']['hide_empty_field'] = TRUE;
+  }
+  if (empty($form_state['values']['ajax_field'])) {
+    $form_state['values']['ajax_field'] = TRUE;
   }
   // VALIDATE: The only tokens used should be those we mentioned under "Available Tokens".
   // PART 1: Set Titles.
@@ -464,6 +491,7 @@ function tripal_tripal_bundle_form_submit($form, &$form_state) {
 
     // Save the hide_empty_field setting.
     tripal_set_bundle_variable('hide_empty_field', $bundle->id, $form_state['values']['hide_empty_field']);
+    tripal_set_bundle_variable('ajax_field', $bundle->id, $form_state['values']['ajax_field']);
 
     // Save the page title format.
     tripal_save_title_format(
@@ -489,7 +517,7 @@ function tripal_tripal_bundle_form_submit($form, &$form_state) {
       $includes = array(
         module_load_include('inc', 'tripal', 'includes/tripal.bulk_update'),
       );
-      tripal_add_job('Update all aliases for content type: ' . $bundle->label, 'tripal', 
+      tripal_add_job('Update all aliases for content type: ' . $bundle->label, 'tripal',
         'tripal_update_all_urls_and_titles', $args, $user->uid, 10, $includes);
     }
     elseif ($trigger == 'Bulk update all aliases'){
@@ -503,7 +531,7 @@ function tripal_tripal_bundle_form_submit($form, &$form_state) {
       $includes = array(
         module_load_include('inc', 'tripal', 'includes/tripal.bulk_update'),
       );
-      tripal_add_job('Update all aliases for content type: ' . $bundle->label, 'tripal', 
+      tripal_add_job('Update all aliases for content type: ' . $bundle->label, 'tripal',
         'tripal_update_all_urls_and_titles', $args, $user->uid, 10, $includes);
     }
 
@@ -546,12 +574,12 @@ function tripal_admin_add_type_form($form, &$form_state) {
           and return to create new Tripal content types.', TRIPAL_NOTICE);
     return;
   }
-  
+
   // Set the stage to step1 if it isn't already set.
   if (!isset($form_state['stage'])) $form_state['stage'] = 'step1';
   $stage = $form_state['stage'];
-  
-  
+
+
   // Get the selected term.
   if (array_key_exists('values', $form_state) and
       array_key_exists('term', $form_state['values'])) {
@@ -563,14 +591,14 @@ function tripal_admin_add_type_form($form, &$form_state) {
       $selected_term = $selected[0];
     }
   }
-    
+
   // Get the selected storage element.
   $default_store = 'term_chado_storage';
   if (array_key_exists('values', $form_state) and
       array_key_exists('store_select', $form_state['values'])) {
     $default_store = $form_state['values']['store_select'];
   }
-  
+
 
   // Handle the different stages:
   if ($stage == 'step1') {
@@ -585,10 +613,10 @@ function tripal_admin_add_type_form($form, &$form_state) {
     tripal_admin_add_type_form_step2_summary($form, $form_state, $stores, $selected_term, $default_store);
     tripal_admin_add_type_form_step3($form, $form_state, $stores, $selected_term, $default_store);
   }
-  
+
   $form['#prefix'] = '<div id = "tripal-add-type-form">';
   $form['#suffix'] = '</div>';
-  
+
   return $form;
 }
 
@@ -596,7 +624,7 @@ function tripal_admin_add_type_form($form, &$form_state) {
  * Builds step1 of the tripal_admin_add_type_form()
  */
 function tripal_admin_add_type_form_step1(&$form, &$form_state) {
-  
+
   // Get the term name from the form_state.
   $term_name = '';
   if (array_key_exists('values', $form_state) and array_key_exists('term_name0', $form_state['values'])) {
@@ -605,7 +633,7 @@ function tripal_admin_add_type_form_step1(&$form, &$form_state) {
   if (array_key_exists('input', $form_state) and array_key_exists('term_name0', $form_state['input'])) {
     $term_name = $form_state['input']['term_name0'];
   }
-  
+
   // Get the term lookup form.
   $description = t("The content type must be the name of a term in
         a controlled vocabulary and the controlled vocabulary should
@@ -615,7 +643,7 @@ function tripal_admin_add_type_form_step1(&$form, &$form_state) {
   tripal_get_term_lookup_form($form, $form_state, $term_name,
     'Step 1: Content Type', $description, TRUE, '', 0,
     'tripal_admin_add_type_form_ajax_callback');
-  
+
   if ($term_name) {
     $form['term_match']['step1-continue'] = array(
       '#type' => 'submit',
@@ -626,15 +654,15 @@ function tripal_admin_add_type_form_step1(&$form, &$form_state) {
 }
 
 /**
- * Provides a summary of values selected in Step 1. 
+ * Provides a summary of values selected in Step 1.
  */
 function tripal_admin_add_type_form_step1_summary(&$form, &$form_state, $selected_term) {
-     
+
   $form['term'] = [
     '#type' => 'value',
     '#value' => $selected_term,
   ];
-  
+
   $form['term_summary'] = [
     '#type' => 'fieldset',
     '#title' => t('Step 1: Content Type'),
@@ -646,7 +674,7 @@ function tripal_admin_add_type_form_step1_summary(&$form, &$form_state, $selecte
   $form['term_summary']['details'] = [
     '#type' => 'item',
     '#title' => t('Term'),
-    '#markup' => 'Name: ' . $selected_term->name . 
+    '#markup' => 'Name: ' . $selected_term->name .
       '<br>Vocabulary: ' . $selected_term->cv_id->name . ' (' . $selected_term->dbxref_id->db_id->name . ') ' .
       '<br>Term ID: ' . $selected_term->dbxref_id->db_id->name . ':' . $selected_term->dbxref_id->accession . '.  ' .
       '<br>Definition:  ' . $definition
@@ -655,14 +683,14 @@ function tripal_admin_add_type_form_step1_summary(&$form, &$form_state, $selecte
     '#type' => 'submit',
     '#value' => t('Pick a different term'),
     '#name' => 'step1-return',
-  );  
+  );
 }
-  
+
 /**
  * Builds step1 of the tripal_admin_add_type_form()
  */
 function tripal_admin_add_type_form_step2(&$form, &$form_state, $stores, $selected_term, $default_store) {
-  
+
   // Now let the user select where the data type will be stored.
   $form['storage'] = array(
     '#type' => 'fieldset',
@@ -671,7 +699,7 @@ function tripal_admin_add_type_form_step2(&$form, &$form_state, $stores, $select
           must be stored in a single storage backend. Please select the
           storage method and settings for this content type.')
   );
-    
+
   $store_options = array(0 => '-- Select --');
   foreach ($stores as $store_type => $store) {
     $store_options[$store_type] = $store['label'];
@@ -684,7 +712,7 @@ function tripal_admin_add_type_form_step2(&$form, &$form_state, $stores, $select
     '#default_value' => $default_store,
     '#description' => 'Select a storage background for this content type.'
   );
-  
+
   if ($default_store) {
     $form['term_match']['step2-continue'] = array(
       '#type' => 'submit',
@@ -700,12 +728,12 @@ function tripal_admin_add_type_form_step2(&$form, &$form_state, $stores, $select
 function tripal_admin_add_type_form_step2_summary(&$form, &$form_state, $stores, $selected_term, $default_store) {
   $default_store = $form_state['values']['store_select'];
   $selected_store_module = $stores[$default_store]['module'];
-  
+
   $form['store_select'] = [
     '#type' => 'value',
     '#value' => $default_store,
   ];
-  
+
   $form['store_summary'] = [
     '#type' => 'fieldset',
     '#title' => t('Step 2: Storage'),
@@ -722,7 +750,7 @@ function tripal_admin_add_type_form_step2_summary(&$form, &$form_state, $stores,
     '#type' => 'submit',
     '#value' => t('Pick a different term'),
     '#name' => 'step1-return',
-  );  
+  );
 }
 
 /**
@@ -730,9 +758,9 @@ function tripal_admin_add_type_form_step2_summary(&$form, &$form_state, $stores,
  */
 function tripal_admin_add_type_form_step3(&$form, &$form_state, $stores, $selected_term, $default_store) {
   $default_store = $form_state['values']['store_select'];
-  
+
   $selected_store_module = $stores[$default_store]['module'];
-  
+
   $form['store_settings'] = [
     '#type' => 'fieldset',
     '#title' => t('Step 3: Storage Settings'),
@@ -744,7 +772,7 @@ function tripal_admin_add_type_form_step3(&$form, &$form_state, $stores, $select
     $store_form = $function($form, $form_state, $selected_term, $submit_disabled);
     $form['store_settings'][$default_store] = $store_form;
   }
-  
+
   // Add in the button for the cases of no terms or too many.
   $form['submit_button'] = array(
     '#type' => 'submit',
@@ -768,7 +796,7 @@ function tripal_admin_add_type_form_validate($form, &$form_state) {
   $stores = module_invoke_all('vocab_storage_info');
   $store_select = (isset($form_state['values']['store_select'])) ? $form_state['values']['store_select'] : NULL;
   $clicked_button = $form_state['clicked_button']['#name'];
-  
+
   // Don't do validation on an ajax callback.
   if (array_key_exists('#ajax', $form_state['triggering_element'])) {
     return;
@@ -776,8 +804,8 @@ function tripal_admin_add_type_form_validate($form, &$form_state) {
 
   if ($clicked_button =='step1-continue') {
     $form_state['rebuild'] = TRUE;
-    $form_state['stage'] = 'step2';   
-    
+    $form_state['stage'] = 'step2';
+
     $selected = tripal_get_term_lookup_form_result($form, $form_state);
     if (count($selected) == 0) {
       form_set_error('term_match][term_name', 'Please select a vocabulary term.');
@@ -785,23 +813,23 @@ function tripal_admin_add_type_form_validate($form, &$form_state) {
     if (count($selected) > 1) {
       form_set_error('term_match][term_name', 'Please select only one vocabulary term.');
     }
-    
+
   }
   if ($clicked_button =='step1-return') {
     $form_state['rebuild'] = TRUE;
     $form_state['stage'] = 'step1';
   }
   if ($clicked_button =='step2-continue') {
-    
+
     if (!$store_select) {
       form_set_error('store_select', 'Please select only one vocabulary term.');
     }
-    
+
     $form_state['rebuild'] = TRUE;
     $form_state['stage'] = 'step3';
   }
-  
-  if ($clicked_button == 'create-content') {           
+
+  if ($clicked_button == 'create-content') {
     // Call the submit hook for this form for the storage method that
     // will be responsible for this cotent type.
     $stores = module_invoke_all('vocab_storage_info');
@@ -854,7 +882,7 @@ function tripal_admin_add_type_form_submit($form, &$form_state) {
       );
 
       global $user;
-      $job_id = tripal_add_job("Create content type: " . $term_name . ' ('. $vocabulary . ':' . $accession . ')', 
+      $job_id = tripal_add_job("Create content type: " . $term_name . ' ('. $vocabulary . ':' . $accession . ')',
         'tripal', 'tripal_create_bundle', [$args], $user->uid);
 
       if (!$job_id) {
@@ -888,14 +916,14 @@ function tripal_admin_access($entity) {
   $bundle = tripal_load_bundle_entity(array('name' => $bundle_name));
 
   if (!$bundle) {
-    tripal_report_error('tripal', TRIPAL_WARNING, 
+    tripal_report_error('tripal', TRIPAL_WARNING,
       'Unable to load bundle :name when creating permissions.', array(':name' => $bundle_name));
     return FALSE;
   }
 
   // Get the administrative user roles.
   $admin_role = NULL;
-  $admin_rid = variable_get('user_admin_role'); 
+  $admin_rid = variable_get('user_admin_role');
   if (!$admin_rid) {
     // If we couldn't identify a single role from the 'user_admin_role' variable
     // then let's get the role that is currently set to administer tripal. If
@@ -910,13 +938,13 @@ function tripal_admin_access($entity) {
       $admin_rid = 3;
     }
   }
-  
+
   // If we can't find a unique admin role then just don't add one and
   // the user will be forced to manually set permissions for the admin.
   if (!$admin_rid) {
     return FALSE;
   }
-  
+
   // Define the permissions.
   $permission_for_role = array(
     'create ' . $bundle->name => TRUE,
@@ -924,7 +952,7 @@ function tripal_admin_access($entity) {
     'edit ' . $bundle->name => TRUE,
     'delete ' . $bundle->name => TRUE,
   );
-  
+
   // Assign the permissions
   user_role_change_permissions($admin_rid, $permission_for_role);
 

+ 6 - 5
tripal/includes/TripalEntityController.inc

@@ -636,6 +636,7 @@ class TripalEntityController extends EntityAPIController {
         $info = entity_get_info($queried_entities[$id]->type);
         $field_cache = array_key_exists('field cache', $info) ? $info['field cache'] : FALSE;
         $bundle_name = $queried_entities[$id]->bundle;
+        $bundle = tripal_load_bundle_entity(array('name' => $bundle_name));
 
         // Iterate through the field instances and find those that are set to
         // 'auto_attach' and which are attached to this bundle. Add all
@@ -687,13 +688,13 @@ class TripalEntityController extends EntityAPIController {
           // attach then we will ignore it. It can only be set by providing
           // the id in the $field_id array handled previously.
           else {
-            // We only load via AJAX if empty fields are not hidden.
-            $bundle = tripal_load_bundle_entity(array('name' => $bundle_name));
-            $hide_variable = tripal_get_bundle_variable('hide_empty_field', $bundle->id, 'hide');
+            
+            // Do not load fields that are not auto attached.  Instead set
+            // their value to an empty string and set the #processed key to
+            // FALSE.
             if (array_key_exists('settings', $instance) and
                 array_key_exists('auto_attach', $instance['settings']) and
-                $instance['settings']['auto_attach'] == FALSE and
-                $hide_variable == 'show') {
+                $instance['settings']['auto_attach'] == FALSE) {
 
                // Add an empty value. This will allow the tripal_entity_view()
                // hook to add the necessary prefixes to the field for ajax

+ 46 - 16
tripal/includes/TripalEntityUIController.inc

@@ -91,6 +91,14 @@ class TripalEntityUIController extends EntityDefaultUIController {
       'type' => MENU_LOCAL_TASK,
       'weight' => -8,
     );
+    $items['bio_data/' . $wildcard . '/reload'] = array(
+      'title'  => 'Reload',
+      'page callback' => 'tripal_entity_reload',
+      'page arguments' => array(1),
+      'access arguments' => array('administer tripal'),
+      'type' => MENU_LOCAL_TASK,
+      'weight' => 10,
+    );
     // Menu item for deleting tripal data entities.
     $items['bio_data/' . $wildcard . '/delete'] = array(
       'title'  => 'Delete',
@@ -454,6 +462,28 @@ function tripal_view_entity($entity, $view_mode = 'full') {
    }
  }
 
+/**
+ * Clears the cache of a given entity to force a reload.
+ *
+ * @param $entity_id
+ *   The entity_id of the entity to reload.
+ */
+function tripal_entity_reload($entity) {
+  $entity_id = $entity->id;
+  $cid = 'field:TripalEntity:' . $entity_id . ':';
+  cache_clear_all($cid, 'cache_field', TRUE);
+
+  $sql = "SELECT count(*) FROM cache_field WHERE cid like :cid";
+  $count = db_query($sql, [':cid' => $cid . '%'])->fetchField();
+  if (!isset($count) or $count > 0) {
+    drupal_set_message('Failed to clear the cache for this entity.');
+  }
+  else {
+    drupal_set_message('Cache cleared, entity reloaded');
+  }
+  drupal_goto('bio_data/' . $entity_id);
+}
+
  /**
   *
   */
@@ -701,19 +731,19 @@ function tripal_entity_form_submit($form, &$form_state) {
  * TripalEntityUIController class.
  */
 function tripal_add_page() {
-  
+
   // The content array to be returned.
   $content = [];
-   
+
   $content['instructions'] = [
     '#type' => 'markup',
-    '#markup' => 'This page provides links for creating pages for Tripal ' . 
-      'supported content types. These content types are organized by categories. ' . 
-      'Please note, however, that the categorization is the most common use ' . 
+    '#markup' => 'This page provides links for creating pages for Tripal ' .
+      'supported content types. These content types are organized by categories. ' .
+      'Please note, however, that the categorization is the most common use ' .
       'for a given type. Some types may be useful in other "categories" as well.',
     '#weight' => -15,
   ];
-  
+
 
   // Get the list of categories.
   $select = "
@@ -723,8 +753,8 @@ function tripal_add_page() {
     WHERE TV.name = 'bundle_category'
   ";
   $categories = db_query($select);
-  
-  
+
+
   // Build the fieldsets for the categories.
   $fieldsets = [];
   $category_weight = 1;
@@ -744,7 +774,7 @@ function tripal_add_page() {
       '#weight' => $category == 'General' ? -10 : $category_weight++,
     ];
   }
-  
+
   // Create the "other" fieldset, and set it's weight to 100 so it goes to
   // the bottom.
   $fieldsets['Other_fieldset'] = [
@@ -759,14 +789,14 @@ function tripal_add_page() {
     '#attached' => array(
       'js' => array('misc/collapse.js', 'misc/form.js')
     ),
-  ]; 
-  
-  
+  ];
+
+
   // Get the list of bundles and iterate through them.
   $select = "SELECT id, name, label FROM tripal_bundle ORDER BY label";
   $bundles = db_query($select);
   while ($bundle = $bundles->fetchObject()) {
-    
+
     // Lookup the bundle category.
     $sql = "
       SELECT TBV.value as category
@@ -775,12 +805,12 @@ function tripal_add_page() {
         INNER JOIN tripal_variables TV on TV.variable_id = TBV.variable_id
       WHERE TV.name = 'bundle_category' and TB.id = :id;
     ";
-  
+
     $category = db_query($sql, [':id' => $bundle->id])->fetchField();
     if (!$category) {
       $category = 'Other';
     }
-    
+
     $machine_name = preg_replace('/[^\w]/', '_', $category);
     $bundle = tripal_load_bundle_entity(['id' => $bundle->id]);
     if (!$bundle) {
@@ -797,7 +827,7 @@ function tripal_add_page() {
       ];
     }
   }
-  
+
   // Now iterate through the fieldsets and set their weight
   return $content;
 }

+ 100 - 36
tripal/includes/tripal.entity.inc

@@ -496,50 +496,112 @@ function tripal_entity_access($op, $entity = NULL, $account = NULL, $entity_type
   return FALSE;
 }
 
+/**
+ * Implements hook_entity_prepare_view
+ * 
+ * This function is called before building the content array for an entity. It
+ * allows us to load any unattached fields if AJAX is turned off.
+ */
+function tripal_entity_prepare_view($entities, $type, $langcode) {
+  
+  // This is for only TripalEntity content types.
+  if ($type != 'TripalEntity') {
+    return;
+  }
+
+  // Iterate through the entities and instancesa and if AJAX loading is turned
+  // off then we need to load those fields that were not auto attached.
+  foreach ($entities as $entity_id => &$entity) {
+    $bundle = tripal_load_bundle_entity(['name' => $entity->bundle]);
+    $use_ajax = tripal_get_bundle_variable('ajax_field', $bundle->id);
+    $instances = field_info_instances('TripalEntity', $entity->bundle);
+    foreach ($instances as $instance) {
+      $field_name = $instance['field_name'];
+      $field = field_info_field($field_name);
+      
+      $auto_attach = array_key_exists('auto_attach', $instance['settings']) ? $instance['settings']['auto_attach'] : TRUE;
+      $processed = $entity->{$field_name}['#processed'];
+      
+      // If the field is not ajax loadable and the field is not auto attached
+      // then we need to add the value.
+      if (!$use_ajax and $auto_attach == FALSE and $processed == FALSE) {
+        $temp = tripal_load_entity('TripalEntity', [$entity_id], TRUE, [$field['id']]);
+        reset($temp);
+        $entity->{$field_name} = $temp[$entity_id]->{$field_name};
+        $items = field_get_items('TripalEntity', $entity, $field_name);        
+      }
+    }
+  }
+}
 
 /**
  * Implements hook_entity_view.
  *
  * Here we want to overwite unattached fields with a div box that will be
  * recognized by JavaScript that will then use AJAX to load the field.
- *
  * The tripal_ajax_attach_field() function is called by an AJAX call to
- * retrieve the field.
+ * retrieve the field.  We also remove empty fields that were auto attached.
  */
 function tripal_entity_view($entity, $type, $view_mode, $langcode) {
 
-  if ($type == 'TripalEntity') {
-
-    foreach (element_children($entity->content) as $child_name) {
-
-      // Initailize the prefix and suffix for this field.
-      if (!array_key_exists('#prefix', $entity->content[$child_name])) {
-        $entity->content[$child_name]['#prefix'] = '';
-      }
-      if (!array_key_exists('#suffix', $entity->content[$child_name])) {
-        $entity->content[$child_name]['#suffix'] = '';
-      }
-
-      // Surround the field with a <div> box for AJAX loading if this
-      // field is unattached.  this will allow JS code to automatically load
-      // the field.
-      $instance = field_info_instance('TripalEntity', $child_name, $entity->bundle);
-      if ($instance and array_key_exists('settings', $instance)) {
-        $class = '';
-        if (array_key_exists('auto_attach', $instance['settings']) and
-            $instance['settings']['auto_attach'] == FALSE and
-            $entity->{$child_name}['#processed'] == FALSE) {
-          // If the field is empty then try to use ajax to load it.
-          $items = field_get_items('TripalEntity', $entity, $child_name);
-          if (count($items) == 0 or empty($items[0]['value'])) {
-            $class = 'class="tripal-entity-unattached"';
-          }
-        }
-        $entity->content[$child_name]['#prefix'] .= '<div id="tripal-entity-' . $entity->id . '--' . $child_name . '" ' . $class . '>';
-        $entity->content[$child_name]['#suffix'] .= '</div>';
+  // This is for only TripalEntity content types.
+  if ($type != 'TripalEntity') {
+    return;
+  }
+    
+  $bundle = tripal_load_bundle_entity(['name' => $entity->bundle]);
+  $hide_empty = tripal_get_bundle_variable('hide_empty_field', $bundle->id);
+  $use_ajax = tripal_get_bundle_variable('ajax_field', $bundle->id);
+
+  // Iterate through the fields attached to this entity and add IDs to them
+  // as well as some classe for ajax loading.
+  foreach (element_children($entity->content) as $child_name) {
+
+    // Surround the field with a <div> box for AJAX loading if this
+    // field is unattached.  this will allow JS code to automatically load
+    // the field.
+    $instance = field_info_instance('TripalEntity', $child_name, $entity->bundle);
+    if (!$instance) {
+      continue;
+    }
+    $field = field_info_field($instance['field_name']);
+     
+    // Check if this is an AJAX loadable field, and if so set the class.
+    $class = '';
+    $auto_attach = array_key_exists('auto_attach', $instance['settings']) ? $instance['settings']['auto_attach'] : TRUE;
+    $processed = $entity->{$child_name}['#processed'];
+    if ($use_ajax and $auto_attach == FALSE and $processed == FALSE) {
+      $class = 'class="tripal-entity-unattached"';
+    }
+    
+    // Set the prefix and suffix.
+    if (!array_key_exists('#prefix', $entity->content[$child_name])) {
+      $entity->content[$child_name]['#prefix'] = '';
+    }
+    if (!array_key_exists('#suffix', $entity->content[$child_name])) {
+      $entity->content[$child_name]['#suffix'] = '';
+    }
+    $entity->content[$child_name]['#prefix'] .= '<div id="tripal-entity-' . $entity->id . '--' . $child_name . '" ' . $class . '>';
+    $entity->content[$child_name]['#suffix'] .= '</div>';
+    
+    // Remove any auto attached fields if they are empty.
+    if ($hide_empty and $processed) {
+      $items = field_get_items('TripalEntity', $entity, $child_name);
+      if (tripal_field_is_empty($field, $items)) {
+        unset($entity->content[$child_name]);
       }
     }
   }
+  
+  //dpm($entity->content);
+  // Add some settings for AJAX to deal with fields.
+  $settings = [
+    'tripal_display' => [
+      'hide_empty' => $hide_empty,
+      'use_ajax' => $use_ajax
+    ]
+  ];
+  drupal_add_js($settings, 'setting');
 }
 
 /**
@@ -551,12 +613,12 @@ function tripal_entity_view($entity, $type, $view_mode, $langcode) {
  */
 function tripal_ajax_attach_field($id) {
 
-  $matches = array();
+  $matches = [];
   if (preg_match('/^tripal-entity-(\d+)--(.+)$/', $id, $matches)) {
     $entity_id = $matches[1];
     $field_name = $matches[2];
     $field = field_info_field($field_name);
-    $result = tripal_load_entity('TripalEntity', array($entity_id), FALSE, array($field['id']));
+    $result = tripal_load_entity('TripalEntity', [$entity_id], FALSE, [$field['id']]);
     reset($result);
     $entity = $result[$entity_id];
 
@@ -566,13 +628,15 @@ function tripal_ajax_attach_field($id) {
     // instance default display settings. Not sure why it does this. It needs
     // more investigation.
     $instance = field_info_instance('TripalEntity', $field_name, $entity->bundle);
+    $items = field_get_items('TripalEntity', $entity, $field_name);
     $element = field_view_field('TripalEntity', $entity, $field_name, $instance['display']['default']);
     $element['#label_display'] = 'hidden';
 
     $content = drupal_render($element);
-    return drupal_json_output(array(
+    return drupal_json_output([
       'id' => $id,
-      'content' => $content
-    ));
+      'content' => $content,
+      'is_empty' => tripal_field_is_empty($field, $items),
+    ]);
   }
 }

+ 0 - 26
tripal/includes/tripal.fields.inc

@@ -913,32 +913,6 @@ function tripal_form_field_ui_display_overview_form_alter(&$form, &$form_state,
   }
 }
 
-/**
- * Implements hook_field_is_empty().
- */
-function tripal_field_is_empty($item, $field) {
-
-  // If the $item argument is empty then return TRUE.
-  if (!$item) {
-    return TRUE;
-  }
-
-  // If the field is a tripal field storage API field and there 
-  // is no value field then the field is empty.
-  if (array_key_exists('tripal_storage_api', $field['storage']['settings']) and !array_key_exists('value', $item)) {
-    return TRUE;
-  }
-
-  // If there is a value field but there's nothing in it, the the field is
-  // empty.
-  if (array_key_exists('value', $item) and empty($item['value'])) {
-    return TRUE;
-  }
-
-  // Otherwise, the field is not empty.
-  return FALSE;
-}
-
 /**
  * Theme function for all TripalFieldWidget objects.
  *

+ 202 - 21
tripal/theme/js/tripal.js

@@ -1,27 +1,208 @@
-// Using the closure to map jQuery to $. 
+// Using the closure to map jQuery to $.
 (function ($) {
   // Store our function as a property of Drupal.behaviors.
   Drupal.behaviors.tripal = {
     attach: function (context, settings) {
+    	
+      // If we don't have any settings, this is not a entity page so exit
+      if (!settings.tripal_display) {
+        return;
+      }
 
-      $(".tripal-entity-unattached .field-items").replaceWith('<div class="field-items">Loading... <img src="' + tripal_path + '/theme/images/ajax-loader.gif"></div>');
-      $(".tripal-entity-unattached").each(function() {
-        id = $(this).attr('id');
-        if (id) {
-          $.ajax({
-            url: baseurl + '/bio_data/ajax/field_attach/' + id,
-            dataType: 'json',
-            type: 'GET',
-            success: function(data){
-              var content = data['content'];
-              var id = data['id'];
-              $("#" + id + ' .field-items').replaceWith(content);
-            }
-          });
-        }
+      // If the site does not support AJAX loading of fields then we're done.
+      // Tripal will have already removed empty fields that wouldn't have 
+      // needed AJAX loading.
+      var use_ajax = settings.tripal_display.use_ajax;
+      if (!use_ajax) {
+        return;
+      }
+
+      // Attach all fields that require AJAX loading. 
+      $('.tripal-entity-unattached .field-items').replaceWith('<div class="field-items">Loading... <img src="' + tripal_path + '/theme/images/ajax-loader.gif"></div>');
+      $('.tripal-entity-unattached').each(function () {
+        var id = $(this).attr('id');
+        var hide_empty_field = settings.tripal_display.hide_empty;
+        var field = new TripalAjaxField(id, hide_empty_field);
+        field.attach();
       });
     }
+  /*    else {
+  if (pane_id) {
+    $('#' + pane_id).show(0);
+  }
+*/
+  };
+
+  /* ------------------------------------------------------------------------
+   *                        TripalPane Class
+   * ------------------------------------------------------------------------
+   */
+  
+  /**
+   * TripalPane constructor
+   */
+  function TripalPane(id, hidden) {
+    this.id = id;
+    this.hidden = hidden;
+  }
+  
+  /**
+   * Indicates if the pane has any fields as chidren.
+   */
+  TripalPane.prototype.hasChildren = function() {
+	var num_children = $('.tripal_pane-fieldset-' + this.id)
+	  .first()
+	  .children()
+	  .not('.tripal_pane-fieldset-buttons')
+	  .not('.field-group-format-title')
+	  .not('#' + this.id)
+	  .length > 0;
+	  
+	if (num_children > 0) {
+      return true;
+	}
+	return false;
+  }
+  
+  /**
+   * Removes the pane from the HTML of the page.
+   */
+  TripalPane.prototype.remove = function() {
+	// Remove the Pane's fieldset
+	var pane = $('.tripal_pane-fieldset-' + this.id);
+    pane.remove();
+    
+    // Remove the pane's title from the TOC.
+    $('#' + this.id).hide(0);
   }
+  
+  /**
+   * Removes a child from the pane.
+   */
+  TripalPane.prototype.removeChild = function(child_id) {
+    var child = $('#' + child_id);
+    
+    // If this child is within a table then remove the row.
+    var row = child.parents('tr');
+    if (row) {
+      row.remove();
+    }
+    
+    child.remove();
+  }
+  
+  /* ------------------------------------------------------------------------
+   *                        TripalAjaxField Class
+   * ------------------------------------------------------------------------
+   */
+  
+  /**
+   * TripalAjaxField Constructor.
+   *
+   * @param {Number} id
+   * @param {Boolean} hide_fields
+   * @constructor
+   */
+  function TripalAjaxField(id, hide_empty_field) {
+    this.id = id;
+    this.hide_empty_field = hide_empty_field;
+    
+    // Get the pane that this field beongs to (if one exists).
+    this.pane = this.getPane(); 
+  }
+
+  /**
+   * Load the field's content from the server.
+   */
+  TripalAjaxField.prototype.attach = function () {
+    $.ajax({
+      url     : baseurl + '/bio_data/ajax/field_attach/' + this.id,
+      dataType: 'json',
+      type    : 'GET',
+      success : this.setFieldContent.bind(this)
+    });
+  };
+
+  /**
+   * Add the content of the field to its pane.
+   *
+   * @param data
+   */
+  TripalAjaxField.prototype.setFieldContent = function (data) {
+	// Get the data items: the content, if this field is empty and the id 
+	// of this field.
+    var content = data['content'];
+    var empty = data['is_empty'];
+    var id = data['id'];
+    
+    // Get the field object.
+    var field = $('#' + id);
+    
+    // First step, set the content for this field.  This will be the
+    // field formatter content.
+    $('#' + id + ' .field-items').replaceWith(content);
+
+    // If the field is not empty then we're done.  Always show non-empty fields.
+    if (!empty) {
+      return;
+    }
+    
+    // If empty fields should not be hidden then return.
+    if (!this.hide_empty_field) {
+      return;
+    }
+	
+	// Second, if this field is part of a pane then we need to remove it
+	// from the pane. Otherwise, just remove it.
+	if (this.pane) {
+
+      // Remove this field from the pane.
+      this.pane.removeChild(id);
+  
+      // If the pane has no more children then remove it.
+      if (!this.pane.hasChildren()) {
+        this.pane.remove();
+      }
+	}
+	else {
+	  field.remove();
+	}
+  };
+
+  /**
+   * Extract the pane id from parent classes.
+   *
+   * @param classes
+   * @return {String|null}
+   */
+  TripalAjaxField.prototype.getPane = function () {
+	  
+	// Get the pane for this field.
+	var field = $('#' + this.id);
+	var pane = field.parents('.tripal_pane')
+	
+	// If the field is not in a pane then just return.
+	if (pane.length == 0) {
+	  return null;
+	}
+	
+	// Get further details about the pane.
+	var classes = pane.first().attr('class').split(' ');
+    var sub_length = 'tripal_pane-fieldset-'.length;
+    var pane_id = null;
+
+    classes.map(function (cls) {
+      if (cls.indexOf('tripal_pane-fieldset-') > -1) {
+        pane_id = cls.substring(sub_length, cls.length);
+      }
+    });
+    
+    if (pane_id) {
+      var pane = new TripalPane(pane_id, false);
+      return pane;
+    }
+    return null;
+  };
 
 })(jQuery);
 
@@ -34,11 +215,11 @@ function tripal_navigate_field_pager(id, page) {
   });
 
   jQuery.ajax({
-    type: "GET",
-    url: Drupal.settings["basePath"] + "bio_data/ajax/field_attach/" + id,
-    data: { 'page' : page },
-    success: function(response) {
-      jQuery("#" + id + ' .field-items').replaceWith(response['content']);
+    type   : 'GET',
+    url    : Drupal.settings['basePath'] + 'bio_data/ajax/field_attach/' + id,
+    data   : {'page': page},
+    success: function (response) {
+      jQuery('#' + id + ' .field-items').replaceWith(response['content']);
     }
   });
 }

+ 22 - 0
tripal/tripal.install

@@ -50,6 +50,10 @@ function tripal_add_variables() {
     'hide_empty_field',
     'Structure->Tripal Content Type->edit checkbox to hide empty fields for that bundle.'
   );
+  tripal_insert_variable(
+    'ajax_field',
+    'Structure->Tripal Content Type->edit checkbox for ajax fields for that bundle.'
+  );
 }
 
 /**
@@ -1209,3 +1213,21 @@ function tripal_update_7312() {
   }
 }
 
+
+/**
+ * Adds a tripal_storage_api setting to all field storage details
+ */
+function tripal_update_7313() {
+  $transaction = db_transaction();
+  try {
+    tripal_insert_variable(
+      'ajax_field',
+      'Structure->Tripal Content Type->edit checkbox ajax fields for that bundle.'
+    );
+  } catch (\PDOException $e) {
+    $transaction->rollback();
+    $error = $e->getMessage();
+    throw new DrupalUpdateException('Could not perform update: ' . $error);
+  }
+}
+

+ 38 - 40
tripal/tripal.module

@@ -333,15 +333,15 @@ function tripal_menu() {
       if (!$callback) {
         $callback = 'drupal_get_form';
         $page_args = ['tripal_get_importer_form', $class_name];
-      }      
+      }
       if (!$callback_path) {
         $callback_path = 'includes/tripal.importer.inc';
       }
       $file_path = drupal_get_path('module', 'tripal');
       if ($callback_path and $callback_module) {
         $file_path = drupal_get_path('module', $callback_module);
-      }      
-      
+      }
+
       $items[$menu_path] = array(
         'title' => $class_name::$name,
         'description' =>  $class_name::$description,
@@ -403,8 +403,8 @@ function tripal_menu() {
     'file' => 'includes/tripal.admin.inc',
     'file path' => drupal_get_path('module', 'tripal'),
   );
-  
-  
+
+
   //
   // USER FILE MANAGEMENT
   //
@@ -419,7 +419,7 @@ function tripal_menu() {
     'file path' => drupal_get_path('module', 'tripal'),
     'weight' => 30,
   ];
-  
+
   $items['admin/tripal/files/quota'] = [
     'title' => 'User Quotas',
     'description' => 'Set default quota, expiration date, and custom quotas',
@@ -431,7 +431,7 @@ function tripal_menu() {
     'file path' => drupal_get_path('module', 'tripal'),
     'weight' => 10,
   ];
-  
+
   // Admin remove user quota
   $items['admin/tripal/files/quota/remove/%'] = [
     'title' => 'Remove custom user quota',
@@ -443,7 +443,7 @@ function tripal_menu() {
     'file' => 'includes/tripal.admin_files.inc',
     'file path' => drupal_get_path('module', 'tripal'),
   ];
-  
+
   // Add user quota
   $items['admin/tripal/files/quota/add'] = [
     'title' => 'Add Custom Quota',
@@ -457,7 +457,7 @@ function tripal_menu() {
     'file' => 'includes/tripal.admin_files.inc',
     'file path' => drupal_get_path('module', 'tripal'),
   ];
-  
+
   // Autocomplete path for the users on the site
   $items['admin/tripal/files/quota/user/autocomplete'] = [
     'title' => 'Autocomplete for existing users',
@@ -468,7 +468,7 @@ function tripal_menu() {
     'file' => 'includes/tripal.admin_files.inc',
     'file path' => drupal_get_path('module', 'tripal'),
   ];
-  
+
   // Edit user quota
   $items['admin/tripal/files/quota/edit/%'] = [
     'title' => 'Edit Custom Quota',
@@ -480,7 +480,7 @@ function tripal_menu() {
     'file' => 'includes/tripal.admin_files.inc',
     'file path' => drupal_get_path('module', 'tripal'),
   ];
-  
+
   $items['admin/tripal/files/usage'] = [
     'title' => 'File Usage',
     'description' => 'Set default quota, expiration date, and custom quotas',
@@ -493,11 +493,11 @@ function tripal_menu() {
     'weight' => 15
   ];
 
-  
+
   //
   // USER FILES
   //
-  
+
   // User view quota (Tab)
   $items['user/%/files'] = [
     'title' => 'Files',
@@ -511,7 +511,7 @@ function tripal_menu() {
     'file path' => drupal_get_path('module', 'tripal'),
     'weight' => 10,
   ];
-  
+
   $items['user/%/files/%'] = [
     'title' => 'File Details',
     'description' => "View details about the file",
@@ -523,7 +523,7 @@ function tripal_menu() {
     'file' => 'includes/tripal.user.inc',
     'file path' => drupal_get_path('module', 'tripal'),
   ];
-  
+
   // User file renew.
   $items['user/%/files/%/renew'] = [
     'title' => 'Renew File',
@@ -567,7 +567,7 @@ function tripal_menu() {
  * Checks if the current user has permissions to perform an action on a file.
  *
  * @param $op
- *   The operation to perform.  These include 'view', 'download', 'renew' and 
+ *   The operation to perform.  These include 'view', 'download', 'renew' and
  *   'delete'
  * @param $uid
  *   The user ID of the user's account that owns the file.
@@ -576,23 +576,23 @@ function tripal_menu() {
  */
 function tripal_access_user_files($op, $uid, $fid = NULL) {
   global $user;
-  
+
   // The site admin can do anything.
   if (in_array('administrator', $user->roles)) {
     return TRUE;
   }
-  
+
   // Only the user that owns the files can see them.
   if ($uid != $user->uid) {
     return FALSE;
   }
-  
+
   // If no file ID is provided and the user wants to view then
   // this is the case where the user wants to see all the files.
   if (!$fid and $op == 'view') {
     return TRUE;
   }
-    
+
   $file = file_load($fid);
   switch ($op) {
     case 'view':
@@ -608,7 +608,7 @@ function tripal_access_user_files($op, $uid, $fid = NULL) {
 }
 /**
  * An access function for data collections.
- * 
+ *
  * @return boolean
  */
 function tripal_accesss_user_collections($uid) {
@@ -633,7 +633,7 @@ function tripal_users_autocomplete($string) {
     ->fields('u', ['name'])
     ->condition('name', '%' . db_like($string) . '%', 'LIKE')
     ->execute();
-  
+
   foreach ($result as $row) {
     $matches[$row->name] = check_plain($row->name);
   }
@@ -722,7 +722,7 @@ function tripal_permission() {
       'restrict access' => TRUE,
     );
   }
-  
+
   // Add permissions for each Importer
   $importers = tripal_get_importers();
   foreach ($importers as $class_name) {
@@ -1333,7 +1333,7 @@ function tripal_cron() {
   // Check for expired collections.
   tripal_add_job('Cron: Checking expired collections', 'tripal',
     'tripal_expire_collections', $args, 1, 1, $includes, TRUE);
-  
+
   tripal_add_job('Cron: Checking expired files', 'tripal',
     'tripal_expire_files', $args, 1, 1, $includes, TRUE);
 
@@ -1483,7 +1483,7 @@ function tripal_html5_file_validate($element, &$form_state) {
   $name = $element['#name'];
   $name = preg_replace('/[^\w]/', '_', $name);
   $fid = NULL;
-  if (is_array($element['#value']) and $array_key_exists($name, $element['#value'])) {
+  if (is_array($element['#value']) and array_key_exists($name, $element['#value'])) {
     $fid = $element['#value'][$name];
   }
 
@@ -1518,23 +1518,21 @@ function tripal_html5_file_value($element, $input = FALSE, &$form_state) {
  * @param $context
  */
 function tripal_field_display_TripalEntity_alter(&$display, $context){
+/*   $field = $context['field'];
   $field_name = $context['field']['field_name'];
   $bundle = $context['entity']->bundle;
   $bundle_info = tripal_load_bundle_entity(array('name' => $bundle));
 
   // Hide fields that are empty, but only if the hide_empty_field variable
-  // is set to 'hide' for this bundel.
-  $hide_variable = tripal_get_bundle_variable('hide_empty_field', $bundle_info->id, 'hide');
-  if($hide_variable == 'hide'){
-    $item = field_get_items('TripalEntity', $context['entity'], $field_name);
-    if($item) {
-      $field = field_info_field($field_name);
-      if(tripal_field_is_empty($item[0], $field)) {
-        // Stop the right rail element from rendering.
-        drupal_add_css('.' . $field_name.' {display: none;}', 'inline');
-      }
+  // is set to TRUE for this bundle and ajax load is turned off.
+  $hide_empty = tripal_get_bundle_variable('hide_empty_field', $bundle_info->id);
+  if ($hide_empty == TRUE) {
+    $items = field_get_items('TripalEntity', $context['entity'], $field_name);
+    if (tripal_field_is_empty($field, $items)) {
+      // Stop the right rail element from rendering.
+      drupal_add_css('.' . $field_name.' {display: none;}', 'inline');
     }
-  }
+  }  */
 }
 
 /**
@@ -1555,10 +1553,10 @@ function tripal_field_group_table_rows_alter(&$element, &$children) {
       $bundle = $element[$child]['#bundle'];
       $bundle_info = tripal_load_bundle_entity(array('name' => $bundle));
 
-      // If the hide empty variable is turned on then remove fields from
-      // the field group.
-      $hide_variable = tripal_get_bundle_variable('hide_empty_field', $bundle_info->id, 'hide');
-      if($hide_variable == 'hide'){
+      // If the hide empty variable is turned on and ajax load is turned off
+      // then remove fields from the field group.
+      $hide_variable = tripal_get_bundle_variable('hide_empty_field', $bundle_info->id);
+      if($hide_variable == TRUE){
         $items = $element[$child]['#items'];
         // Case #1: there are no items.
         if (count($items) == 0) {

+ 0 - 6
tripal_chado/includes/TripalFields/schema__publication/schema__publication.inc

@@ -144,12 +144,6 @@ class schema__publication extends ChadoField {
       }
     }
 
-    // Ensure we don't have a value if there are no publications.
-    // This is needed due to stubbing out the field above.
-    if (sizeof($pubs) == 0) {
-      unset($entity->{$field_name});
-    }
-
     $i = 0;
     foreach ($pubs as $pub_id => $pub) {
       $pub_details = chado_get_minimal_pub_info($pub);

+ 0 - 6
tripal_chado/includes/TripalFields/sio__annotation/sio__annotation.inc

@@ -328,12 +328,6 @@ class sio__annotation extends ChadoField {
         $entity->{$field_name}['und'][$i]['chado-' . $field_table . '__pub_id'] = $linker->pub_id;
       }
     }
-
-    // If there are no cvterms selected above then remove the stub.
-    // This is needed to ensure this field isn't displayed when there are no annotations.
-    if (sizeof($fcvterms) == 0) {
-      unset($entity->{$field_name});
-    }
   }
 }
 

+ 4 - 0
tripal_ds/theme/css/tripal_ds.css

@@ -74,4 +74,8 @@ span.download-icon {
   cursor: pointer;
   margin-top: 4px;
   color: #777777;
+}
+
+.ds-hide {
+  display: none;
 }

+ 1 - 0
tripal_ds/theme/js/tripal_ds.js

@@ -34,6 +34,7 @@
           }
         });
       });
+      
       // Move the tripal pane to the first position when its TOC item is clicked.
       $('.tripal_pane-toc-list-item-link').each(function (i) {
         var id = '.tripal_pane-fieldset-' + $(this).attr('id');

+ 35 - 21
tripal_ds/tripal_ds.module

@@ -467,20 +467,25 @@ function tripal_ds_field_display_alter(&$display, $context){
     $field_name = $context['field']['field_name'];
     $bundle = $context['entity']->bundle;
     $bundle_info = tripal_load_bundle_entity(array('name' => $bundle));
-    $hide_variable = tripal_get_bundle_variable('hide_empty_field', $bundle_info->id, 'hide');
+    $hide_variable = tripal_get_bundle_variable('hide_empty_field', $bundle_info->id);
+    $processed = isset($context['entity']->{$field_name}) ? $context['entity']->{$field_name}['#processed'] : false;
 
-    if ($field_name && ($hide_variable == 'hide')) {
+    if ($field_name && $hide_variable == TRUE && $processed) {
       $item = field_get_items('TripalEntity', $context['entity'], $field_name);
       $field = field_info_field($field_name);
       if ($item) {
-        if (tripal_field_is_empty($item[0], $field)) {
+        if (tripal_field_is_empty($field, $item)) {
           $parent_field_info = tripal_ds_find_field_group_parent($field_name, 'TripalEntity', $bundle, $context);
           if (!empty($parent_field_info)) {
-            foreach ($parent_field_info as $parent_key => $parent_field){
-               // We want to use JavaScript to remove the fields rather than
-               // CSS to hide them so that when users theme the table of
-               // contents using CSS they aren't theming empty rows.
-               drupal_add_js('jQuery(document).ready(function () { jQuery("#' . $parent_field_info[$parent_key] . '").parents(".views-row").remove() });', 'inline');
+            foreach ($parent_field_info as $parent_key => $parent_field) {
+              if (strpos($parent_field, 'single_field') !== false) {
+                $field_name = str_replace("_single_field", "", $parent_field);
+                drupal_add_js('jQuery(document).ready(function () { jQuery("#tripal-entity-' . $context['entity']->id . '--' . $field_name . '").addClass("ds-hide"); });', 'inline');
+              }
+              // We want to use JavaScript to remove the fields rather than
+              // CSS to hide them so that when users theme the table of
+              // contents using CSS they aren't theming empty rows.
+              drupal_add_js('jQuery(document).ready(function () { jQuery("#' . $parent_field_info[$parent_key] . '").parents(".views-row").remove() });', 'inline');
             }
           }
         }
@@ -505,6 +510,7 @@ function tripal_ds_find_field_group_parent($field_name, $entity_type, $bundle, $
   $field_groups_to_hide = array();
   $increment = 0;
 
+
   // Get the field groups associated with this bundle.
   $fg_for_bundle = db_select('field_group', 'fg')
     ->fields('fg')
@@ -520,24 +526,30 @@ function tripal_ds_find_field_group_parent($field_name, $entity_type, $bundle, $
       // Do nothing
     }
     elseif (!empty($field_group_data['children'][0])) {
+      $children_added = 0;
       $children = $field_group_data['children'];
       //If there is more than one child all need to be checked.
       if (count($children) > 1) {
-        foreach ($children as $kids => $child) {
-          // Now check if each child if empty.
+        foreach ($children as $order => $child) {
+          // Now check if each child is empty.
           $item = field_get_items('TripalEntity', $context['entity'], $child);
           $field = field_info_field($child);
-          if(!tripal_field_is_empty($item[0], $field)){
-            //If any of the fields are not empty do not add the parent.
-            break 2;
-          }
-          else {
+          if(tripal_field_is_empty($field, $item)){
+            //If any of the fields are not empty do not add the parent, add the specific field.
+            // break 2;
+            if (!empty($field)) {
+              $field_groups_to_hide[$increment] = $field['field_name'] . '_single_field';
+              $increment++;
+              $children_added++;
+            }
+          } else {
             continue;
           }
         }
-        $field_groups_to_hide[$increment] = $field_group->group_name;
-      }
-      elseif($children[0] == $field_name) {
+        if ($children_added === count($children)) {
+          $field_groups_to_hide[$increment] = $field_group->group_name;
+        }
+      } elseif ($children[0] == $field_name) {
         $field_groups_to_hide[$increment] = $field_group->group_name;
       }
     }
@@ -557,7 +569,8 @@ function tripal_ds_find_field_group_parent($field_name, $entity_type, $bundle, $
  * @param $bundle
  * @param array $fields
  */
-function tripal_ds_toc_order($bundle, $fields = array()){
+function tripal_ds_toc_order($bundle, $fields = array())
+{
   // Find all menu items associated with the bundle id.
   $menu_items = db_select('tripal_ds', 'ds')
     ->fields('ds')
@@ -571,7 +584,7 @@ function tripal_ds_toc_order($bundle, $fields = array()){
     if (array_key_exists($toc_field_name, $fields)) {
       $weight = $fields[$toc_field_name]['weight'];
       //If a weight is returned update the tripal_ds table.
-      if(!empty($weight)){
+      if (!empty($weight)) {
         db_update('tripal_ds')
           ->fields(array(
             'weight' => $weight,
@@ -589,6 +602,7 @@ function tripal_ds_toc_order($bundle, $fields = array()){
  *
  *
  */
-function tripal_ds_import_api() {
+function tripal_ds_import_api()
+{
   module_load_include('inc', 'tripal_ds', 'api/tripal_ds.pane.api');
 }

+ 6 - 6
tripal_ws/includes/TripalWebService/TripalContentService_v0_1.inc

@@ -248,7 +248,7 @@ class TripalContentService_v0_1 extends TripalWebService {
 
     // If the entity is set to hide fields that have no values then we
     // want to honor that in the web services too.
-    $hide_fields = tripal_get_bundle_variable('hide_empty_field', $bundle->id, 'hide');
+    $hide_fields = tripal_get_bundle_variable('hide_empty_field', $bundle->id);
 
     // Get information about the fields attached to this bundle and sort them
     // in the order they were set for the display.
@@ -320,7 +320,7 @@ class TripalContentService_v0_1 extends TripalWebService {
           $this->addResourceProperty($resource, $term, $service_path . '/' . $entity->id . '/' . urlencode($term['name']), array('lowercase', 'spacing'));
         }
         else {
-          if ($hide_fields == 'show') {
+          if ($hide_fields == FALSE) {
             $this->addResourceProperty($resource, $term, NULL, array('lowercase', 'spacing'));
           }
         }
@@ -340,7 +340,7 @@ class TripalContentService_v0_1 extends TripalWebService {
 
     // If the entity is set to hide fields that have no values then we
     // want to honor that in the web services too.
-    $hide_fields = tripal_get_bundle_variable('hide_empty_field', $bundle->id, 'hide');
+    $hide_fields = tripal_get_bundle_variable('hide_empty_field', $bundle->id);
 
     // Get the field  settings.
     $field_name = $field['field_name'];
@@ -371,7 +371,7 @@ class TripalContentService_v0_1 extends TripalWebService {
       }
     }
 
-    if ($hide_fields == 'hide' and empty($values[0])) {
+    if ($hide_fields == TRUE and empty($values[0])) {
       return;
     }
 
@@ -436,7 +436,7 @@ class TripalContentService_v0_1 extends TripalWebService {
 
     // If the entity is set to hide fields that have no values then we
     // want to honor that in the web services too.
-    $hide_fields = tripal_get_bundle_variable('hide_empty_field', $bundle->id, 'hide');
+    $hide_fields = tripal_get_bundle_variable('hide_empty_field', $bundle->id);
 
     $new_value = '';
     // If the value is an array rather than a scalar then map the sub elements
@@ -446,7 +446,7 @@ class TripalContentService_v0_1 extends TripalWebService {
       foreach ($value as $k => $v) {
 
         // exclude fields that have no values so we can hide them
-        if (!isset($v) and $hide_fields == 'hide') {
+        if (!isset($v) and $hide_fields == TRUE) {
           continue;
         }