فهرست منبع

initial cross site field implementation

Shawna 7 سال پیش
والد
کامیت
cdbf0732c2

+ 1 - 1
tripal_ws/includes/TripalFields/WebServicesField.inc

@@ -37,7 +37,7 @@ class WebServicesField extends TripalField {
   // Once instances exist for a field type then these settings cannot be
   // changed.
   public static $default_settings = array(
-    'storage' => 'tripal_no_storage',
+    'storage' => 'field_tripal_ws_storage',
     // It is expected that all fields set a 'value' in the load() function.
     // In many cases, the value may be an associative array of key/value pairs.
     // In order for Tripal to provide context for all data, the keys should

+ 1 - 123
tripal_ws/includes/TripalFields/WebServicesFieldWidget.inc

@@ -1,127 +1,5 @@
 <?php
-/**
- * @class
- * Purpose:
- *
- * Allowing edit?
- * Data:
- * Assumptions:
- */
-class WebServicesFieldWidget extends TripalFieldWidget {
-/*
-  // The default label for this field.
-  public static $default_label = '';
-
-  // The list of field types for which this formatter is appropriate.
-  //public static $field_types = array('no_widget');
-
-  /**
-   * Provides the form for editing of this field.
-   *
-   * This function corresponds to the hook_field_widget_form()
-   * function of the Drupal Field API.
-   *
-   * This form is diplayed when the user creates a new entity or edits an
-   * existing entity.  If the field is attached to the entity then the form
-   * provided by this function will be displayed.
-   *
-   * At a minimum, the form must have a 'value' element.  For Tripal, the
-   * 'value' element of a field always corresponds to the value that is
-   * presented to the end-user either directly on the page (with formatting)
-   * or via web services, or some other mechanism.  However, the 'value' is
-   * sometimes not enough for a field.  For example, the Tripal Chado module
-   * maps fields to table columns and sometimes those columns are foreign keys
-   * therefore, the Tripal Chado modules does not just use the 'value' but adds
-   * additional elements to help link records via FKs.  But even in this case
-   * the 'value' element must always be present in the return form and in such
-   * cases it's value should be set equal to that added in the 'load' function.
-   *
-   * @param $widget
-   * @param $form
-   *   The form structure where widgets are being attached to. This might be a
-   *   full form structure, or a sub-element of a larger form.
-   * @param $form_state
-   *   An associative array containing the current state of the form.
-   * @param $langcode
-   *   The language associated with $items.
-   * @param $items
-   *   Array of default values for this field.
-   * @param $delta
-   *   The order of this item in the array of subelements (0, 1, 2, etc).
-   * @param $element
-   * A form element array containing basic properties for the widget:
-   *  - #entity_type: The name of the entity the field is attached to.
-   *  - #bundle: The name of the field bundle the field is contained in.
-   *  - #field_name: The name of the field.
-   *  - #language: The language the field is being edited in.
-   *  - #field_parents: The 'parents' space for the field in the form. Most
-   *    widgets can simply overlook this property. This identifies the location
-   *    where the field values are placed within $form_state['values'], and is
-   *    used to access processing information for the field through the
-   *    field_form_get_state() and field_form_set_state() functions.
-   *  - #columns: A list of field storage columns of the field.
-   *  - #title: The sanitized element label for the field instance, ready for
-   *    output.
-   *  - #description: The sanitized element description for the field instance,
-   *    ready for output.
-   *  - #required: A Boolean indicating whether the element value is required;
-   *    for required multiple value fields, only the first widget's values are
-   *    required.
-   *  - #delta: The order of this item in the array of subelements; see
-   *    $delta above
-
-  public function form(&$widget, &$form, &$form_state, $langcode, $items, $delta, $element) {
-    parent::form($widget, $form, $form_state, $langcode, $items, $delta, $element);
-  }
 
-  /**
-   * Performs validation of the widgetForm.
-   *
-   * Use this validate to ensure that form values are entered correctly.
-   * The 'value' key of this field must be set in the $form_state['values']
-   * array anytime data is entered by the user.  It may be the case that there
-   * are other fields for helping select a value. In the end those helper
-   * fields must be used to set the 'value' field.
-
-  public function validate($element, $form, &$form_state, $langcode, $delta) {
-  }
-
-  /**
-   * Performs extra commands when the entity form is submitted.
-   *
-   * Drupal typically does not provide a submit hook for fields.  The
-   * TripalField provides one to allow for behind-the-scenes actions to
-   * occur.   This function should never be used for updates, deletes or
-   * inserts for the Chado table associated with the field.  Rather, the
-   * storage backend should be allowed to handle inserts, updates deletes.
-   * However, it is permissible to perform inserts, updates or deletions within
-   * Chado using this function.  Those operations can be performed if needed but
-   * on other tables not directly associated with the field.
-   *
-   * An example is the chado.feature_synonym table.  The chado_linker__synonym
-   * field allows the user to provide a brand new synonynm and it must add it
-   * to the chado.synonym table prior to the record in the
-   * chado.feature_synonym table.  This insert occurs in the widgetFormSubmit
-   * function.
-   *
-   *  @param $entity_type
-   *    The type of $entity.
-   *  @param $entity
-   *    The entity for the operation.
-   *  @param $field
-   *    The field structure for the operation.
-   *  @param $instance
-   *    The instance structure for $field on $entity's bundle.
-   *  @param $langcode
-   *    The language associated with $items.
-   *  @param $items
-   *    $entity->{$field['field_name']}[$langcode], or an empty array if unset.
-   *  @param $form
-   *    The submitted form array.
-   *  @param $form_state.
-   *    The form state array.
+class WebServicesFieldWidget extends TripalFieldWidget {
 
-  public function submit($form, &$form_state, $entity_type, $entity, $langcode, $delta) {
-  }
-*/
 }

+ 187 - 3
tripal_ws/includes/TripalFields/remote__data/remote__data.inc

@@ -22,9 +22,9 @@ class remote__data extends WebServicesField {
   public static $default_description = 'remote data';
 
   // The default widget for this field.
-  //public static $default_widget = '';
+  public static $default_widget = 'remote__data_widget';
   // The default formatter for this field.
-  //public static $default_formatter = '';
+  public static $default_formatter = 'remote__data_formatter';
   // The module that manages this field.
   public static $module = 'tripal_ws';
   // A list of global settings. These can be accessed within the
@@ -42,6 +42,7 @@ class remote__data extends WebServicesField {
     // listed here.
     'searchable_keys' => array(),
   );
+
   // Provide a list of instance specific settings. These can be access within
   // the instanceSettingsForm.  When the instanceSettingsForm is submitted
   // then Drupal with automatically change these settings for the instance.
@@ -55,7 +56,7 @@ class remote__data extends WebServicesField {
     // The name of the term.
     'term_name' => 'Thing',
     // The unique ID (i.e. accession) of the term.
-    'term_accession' => 'Thing',
+    'term_accession' => 'property',
     // Set to TRUE if the site admin is not allowed to change the term
     // type, otherwise the admin can change the term mapped to a field.
     'term_fixed' => FALSE,
@@ -65,6 +66,12 @@ class remote__data extends WebServicesField {
     // should have auto_attach set to FALSE so tha their values can be
     // attached asynchronously.
     'auto_attach' => FALSE,
+    // Settings to allow the site admin to set the remote data source info.
+			'data_info' => array(
+				'query' => '',
+				'remote_site' => '',
+				'description' => '',
+			),
   );
   // A boolean specifying that users should not be allowed to create
   // fields and instances of this field type through the UI. Such
@@ -81,5 +88,182 @@ class remote__data extends WebServicesField {
    * @see WebServicesField::load()
    */
   public function load($entity) {
+    $site_id_ws = $this->instance['settings']['data_info']['remote_site'];
+    $query = $this->instance['settings']['data_info']['query'];
+    $options = array();
+
+    // Check for tripal tokens and replace if present.
+    $bundle_entity = tripal_load_bundle_entity(array('name' => $entity->bundle));
+    $query = tripal_replace_entity_tokens($query, $entity, $bundle_entity);
+
+     // Get the site url from the tripal_sites table.
+     $site_url_ws = db_select('tripal_sites', 's')
+       ->fields('s',array('url'))
+       ->condition('s.id', $site_id_ws, '=')
+       ->execute()->fetchAll();
+
+     $full_url = $site_url_ws[0]->url . '/web-services/content/v0.1/';
+
+     //Remove trailing slash if one is included.$_COOKIE
+     $full_url = trim($full_url, '/');
+     $full_url = $full_url . '/' . $query;
+
+     //Make the call and pull the data down.
+     $data = drupal_http_request($full_url, $options);
+
+     if($data){
+       $data = drupal_json_decode($data->data);
+       //Check the returned data is not an error.
+       if(array_key_exists('error', $data)){
+          watchdog('Tripal WS', '<pre>Web Services data unavailable because site is returning error: '. print_r($data['error'], TRUE) .'</pre>');
+          $data = '';
+       }
+       //Check the returned data isn't empty.
+       if(count($data['members']) >= 1){
+        //If multiple records, if single follow @id and pull down data
+        if(array_key_exists('value', $data)){
+          $members = $data['value'][0]['members'];
+          if($members){
+            if(count($members) > 1){
+                $field_name = $this->field['field_name'];
+                $entity->{$field_name}['und'][0]['value'] = $data;
+            }
+            else {
+              $single_record_url = $data['members'][0]['@id'];
+              $data = drupal_http_request($single_record_url, $options);
+              $data = drupal_json_decode($data->data);
+              if(array_key_exists('error', $data)){
+                watchdog('Tripal WS', '<pre>Web Services data unavailable because site is returning error: '. print_r($data['error'], TRUE) .'</pre>');
+                $data = '';
+             }
+            }
+          }
+        }
+        else {
+            $single_record_url = $data['members'][0]['@id'];
+            $data = drupal_http_request($single_record_url, $options);
+            $data = drupal_json_decode($data->data);
+            if(array_key_exists('error', $data)){
+              watchdog('Tripal WS', '<pre>Web Services data unavailable because site is returning error: '. print_r($data['error'], TRUE) .'</pre>');
+              $data = '';
+           }
+        }
+        $field_name = $this->field['field_name'];
+        $entity->{$field_name}['und'][0]['value'] = $data;
+      }
+    }
+   }
+   /**
+	 *
+	 * @see TripalField::settingsForm()
+   */
+	public function instanceSettingsForm() {
+		$element = parent::instanceSettingsForm();
+    // Get the setting for the option for how this widget.
+    $instance = $this->instance;
+    $settings = '';
+    $site_list = '';
+
+    $tokens = array();
+    // Get the form info from the bundle about to be saved.
+    $bundle_info = tripal_load_bundle_entity(array($instance['bundle']));
+    // Retrieve all available tokens.
+    $tokens = tripal_get_entity_tokens($bundle_info);
+
+    $element['data_info'] = array(
+      '#type' => 'fieldset',
+      '#title' => 'Remote Data Settings',
+      '#description' => 'Provide the site name, query and description for the remote data source.',
+      '#collapsible' => TRUE,
+      '#collapsed' => FALSE,
+      '#prefix' => "<div id='set_titles-fieldset'>",
+      '#suffix' => '</div>',
+    );
+
+    // Get the site info from the tripal_sites table.
+    $sites = db_select('tripal_sites', 's')
+      ->fields('s')
+      ->execute()->fetchAll();
+
+    foreach ($sites as $site) {
+      $rows[$site->id] =$site->name;
+    }
+
+    $element['data_info']['remote_site'] = array(
+      '#type' => 'select',
+      '#title' => t('Site'),
+      '#options' => $rows,
+      '#default_value' => $this->instance['settings']['data_info']['remote_site'],
+    );
+
+    $element['data_info']['query'] = array(
+      '#type' => 'textarea',
+      '#title' => 'Query',
+      '#description' => 'Build the query string that should be appended after the url. The tokens
+      listed below may be used in your query build.',
+      '#default_value' => $this->instance['settings']['data_info']['query'],
+      '#rows' => 5
+    );
+
+    $element['data_info']['token_display']['tokens'] = array(
+      '#type' => 'hidden',
+      '#value' => serialize($tokens)
+    );
+
+    $element['data_info']['token_display'] = array(
+      '#type' => 'fieldset',
+      '#title' => 'Available Tokens',
+      '#description' => 'Copy the token and paste it into the "Query" text field above.',
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE
+    );
+
+    $element['data_info']['token_display']['content'] = array(
+      '#type' => 'item',
+      '#markup' => theme_token_list($tokens),
+    );
+
+    $element['data_info']['description'] = array(
+      '#type' => 'textarea',
+      '#title' => 'Description',
+      '#description' => 'Describe the data being pulled in.',
+      '#default_value' =>  $this->instance['settings']['data_info']['description'],
+      '#rows' => 1
+    );
+    /*$element['test_button']['data'] = array(
+      '#prefix' => '<div class="returned-data">',
+      '#suffix' => '</div>',
+      '#type' => 'container',
+        //…
+    );
+
+    $element['test_button'] = array(
+      '#type' => 'button',
+      '#value' => t('Test Query'),
+      '#ajax' => array(
+        'wrapper' => 'data-id',
+        'callback' => 'tripal_ws_url_query_test',
+      ),
+        //…
+    );*/
+
+			return $element;
+  }
+
+    /**
+     * Ajax callback.
+     */
+    function tripal_ws_url_query_test_ajax($form, $form_state) {
+      load();
+      return $element['test_button']['data'];
+    }
+ /**
+   *
+   * @param unknown $form
+   * @param unknown $form_state
+   */
+  public function instanceSettingsFormValidate($form, &$form_state) {
   }
+
+
 }

+ 71 - 0
tripal_ws/includes/TripalFields/remote__data/remote__data_formatter.inc

@@ -71,6 +71,77 @@ class remote__data_formatter extends WebServicesFieldFormatter {
   public function view(&$element, $entity_type, $entity, $langcode, $items, $display) {
     // Get the settings
     $settings = $display['settings'];
+    $field_name = $this->field['field_name'];
+
+    //Check that the load function returned content.
+    if (!empty($items[0]['value'])){
+      $list = array();
+      foreach ($items as $index => $item) {
+        $list[$index] = $item['value'];
+      }
+      // If more than one value has been found display all values in an unordered
+      // list.
+      if (count($list) > 1) {
+        $content = theme_item_list(array(
+          'items' => $list,
+          'title' => '',
+          'attributes' => array('class' => array($entity->bundle . '-remote-data-list', 'remote-data-field-list')),
+          'type' => 'ul'
+        ));
+      }
+      else {
+        $content = $list[0];
+      }
+
+      if(array_key_exists('members', $content)){
+        $header = array($items[0]['value']['label']);
+        $members = $content['members'];
+        $rows = array();
+        // Wrap each item in an array incase it is an array so that
+        // it works with the table_theme
+        foreach($members as $index => $member){
+          $rows[] = array('data' => array($index, $item));
+        }
+        //Remove the context information which is not needed.
+        unset($rows[0]);
+      }
+      else {
+        $header = array($items[0]['value']['label']);
+        $rows = array();
+        // Wrap each item in an array incase it is an array so that
+        // it works with the table_theme.
+        foreach($content as $index => $item){
+          $rows[] = array('data' => array($index, $item));
+        }
+        //Remove the context information which is not needed.
+        unset($rows[0]);
+      }
+
+
+      $table = array(
+        'header' => $header,
+        'rows' => $rows,
+        'attributes' => array(
+          'id' => 'tripal_table-remote-data-object',
+          'class' => 'tripal-data-table'
+        ),
+        'sticky' => FALSE,
+        'caption' => "",
+        'colgroups' => array(),
+        'empty' => 'There is no remote data available.',
+      );
+
+      $content = theme_table($table);
+
+      if (count($items) > 0) {
+        // once we have our table array structure defined, we call Drupal's theme_table()
+        // function to generate the table.
+        $element[0] = array(
+          '#type' => 'markup',
+          '#markup' => $content,
+        );
+      }
+    }
   }
   /**
    * Provides a summary of the formatter settings.

+ 91 - 37
tripal_ws/includes/TripalFields/remote__data/remote__data_widget.inc

@@ -63,6 +63,95 @@ class remote__data_widget extends WebServicesFieldWidget {
    */
   public function form(&$widget, &$form, &$form_state, $langcode, $items, $delta, $element) {
     parent::form($widget, $form, $form_state, $langcode, $items, $delta, $element);
+    // Get the field settings.
+   /* $field_name = $this->field['field_name'];
+    $field_type = $this->field['type'];
+
+    // Get the setting for the option for how this widget.
+    $instance = $this->instance;
+    $settings = '';
+    $site_list = '';
+
+    $tokens = array();
+    // Get the form info from the bundle about to be saved.
+    $bundle_info = tripal_load_bundle_entity(array('name' => $form_state['build_info']['args']['0']['bundle']));
+    // Retrieve all available tokens.
+    $tokens = tripal_get_entity_tokens($bundle_info);
+    // If the field already has a value then it will come through the $items
+    // array.  This happens when editing an existing record.
+    dpm($items);
+    // FORM PROPER
+    $widget['#prefix'] =  "<span id='$field_name-remote_data-$delta'>";
+    $widget['#suffix'] =  "</span>";
+
+    $widget['value'] = array(
+      '#type' => 'value',
+      '#value' => array_key_exists($delta, $items) ? $items[$delta]['value'] : '',
+    );
+
+    $widget['data_info'] = array(
+      '#type' => 'fieldset',
+      '#title' => 'Remote Data Settings',
+      '#description' => 'Provide the site name, query and description for the remote data source.',
+      '#collapsible' => TRUE,
+      '#collapsed' => FALSE,
+      '#prefix' => "<div id='set_titles-fieldset'>",
+      '#suffix' => '</div>',
+    );
+
+    // Get the site info from the tripal_sites table.
+      // Get the field groups associated with this bundle.
+    $sites = db_select('tripal_sites', 's')
+      ->fields('s')
+      ->execute()->fetchAll();
+
+    foreach ($sites as $site) {
+      $rows[] = $site->name;
+    }
+
+    $widget['data_info']['site'] = array(
+      '#type' => 'select',
+      '#title' => t('Site'),
+      '#options' => $rows,
+      '#default_value' => $site_list,
+    );
+
+    $widget['data_info']['query'] = array(
+      '#type' => 'textarea',
+      '#title' => 'Query',
+      '#description' => 'Build the query string that should be appended after the url. The tokens
+       listed below may be used in your query build.',
+      '#default_value' => $this->instance['settings']['data_info']['query'],
+      '#rows' => 5
+    );
+
+    $widget['set_titles']['token_display']['tokens'] = array(
+      '#type' => 'hidden',
+      '#value' => serialize($tokens)
+    );
+
+    $widget['data_info']['token_display'] = array(
+      '#type' => 'fieldset',
+      '#title' => 'Available Tokens',
+      '#description' => 'Copy the token and paste it into the "Query" text field above.',
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE
+    );
+
+    $widget['data_info']['token_display']['content'] = array(
+      '#type' => 'item',
+      '#markup' => theme_token_list($tokens),
+    );
+
+    $widget['data_info']['description'] = array(
+      '#type' => 'textarea',
+      '#title' => 'Description',
+      '#description' => 'Describe the data being pulled in.',
+      '#default_value' =>  $this->instance['settings']['data_info']['description'],
+      '#rows' => 1
+    );
+*/
+    //TODO Add test button to ensure query returns info.
   }
   /**
    * Performs validation of the widgetForm.
@@ -74,42 +163,7 @@ class remote__data_widget extends WebServicesFieldWidget {
    * fields must be used to set the 'value' field.
    */
   public function validate($element, $form, &$form_state, $langcode, $delta) {
-  }
-  /**
-   * Performs extra commands when the entity form is submitted.
-   *
-   * Drupal typically does not provide a submit hook for fields.  The
-   * TripalField provides one to allow for behind-the-scenes actions to
-   * occur.   This function should never be used for updates, deletes or
-   * inserts for the Chado table associated with the field.  Rather, the
-   * storage backend should be allowed to handle inserts, updates deletes.
-   * However, it is permissible to perform inserts, updates or deletions within
-   * Chado using this function.  Those operations can be performed if needed but
-   * on other tables not directly associated with the field.
-   *
-   * An example is the chado.feature_synonym table.  The chado_linker__synonym
-   * field allows the user to provide a brand new synonynm and it must add it
-   * to the chado.synonym table prior to the record in the
-   * chado.feature_synonym table.  This insert occurs in the widgetFormSubmit
-   * function.
-   *
-   *  @param $entity_type
-   *    The type of $entity.
-   *  @param $entity
-   *    The entity for the operation.
-   *  @param $field
-   *    The field structure for the operation.
-   *  @param $instance
-   *    The instance structure for $field on $entity's bundle.
-   *  @param $langcode
-   *    The language associated with $items.
-   *  @param $items
-   *    $entity->{$field['field_name']}[$langcode], or an empty array if unset.
-   *  @param $form
-   *    The submitted form array.
-   *  @param $form_state.
-   *    The form state array.
-   */
-  public function submit($form, &$form_state, $entity_type, $entity, $langcode, $delta) {
+    //TODO validate the tokens, site, and query. Test that query returns data.
+
   }
 }

+ 11 - 1
tripal_ws/includes/tripal_ws.field_storage.inc

@@ -41,12 +41,22 @@ function tripal_ws_field_storage_load($entity_type, $entities, $age,
       $field_type = $field['type'];
       $field_module = $field['module'];
 
+      // Get the instance for this field.  If no instance exists then skip
+      // loading of this field. This can happen when a field is deleted from
+      // a bundle using the user UI form.
+      // TODO: how to deal with deleted fields?
+      $instance = field_info_instance($entity_type, $field_name, $entity->bundle);
+
+	  if (!$instance) {
+        continue;
+      }
+
       // Set an empty value by default, and let the hook function update it.
       $entity->{$field_name}['und'][0]['value'] = '';
       tripal_load_include_field_class($field_type);
       if (class_exists($field_type) && method_exists($field_type, 'load')) {
         $tfield = new $field_type($field, $instance);
-        $tfield->load($entity, array('record' => $record));
+        $tfield->load($entity);
       }
 
     } // end: foreach ($fields as $field_id => $ids) {

+ 1 - 1
tripal_ws/tripal_ws.info

@@ -3,6 +3,6 @@ description = Exposes Tripal Entites as RESTful web services.
 core = 7.x
 project = tripal
 package = Tripal
-version = 7.x-3.0-rc2
+version = 7.x-3.0-rc1
 
 dependencies[] = tripal