Browse Source

Merge branch '7.x-3.x' into docker_compat

Stephen Ficklin 7 years ago
parent
commit
81f0d80c87
36 changed files with 2406 additions and 565 deletions
  1. 339 0
      LICENSE.txt
  2. 30 0
      legacy/tripal_analysis/tripal_analysis.module
  3. 0 45
      legacy/tripal_pub/includes/tripal_pub.admin.inc
  4. 2 2
      legacy/tripal_pub/theme/templates/tripal_pub_help.tpl.php
  5. 0 14
      legacy/tripal_pub/tripal_pub.module
  6. 16 0
      tripal/api/tripal.entities.api.inc
  7. 11 2
      tripal/includes/TripalEntityUIController.inc
  8. 1 1
      tripal/includes/TripalFieldDownloaders/TripalFieldDownloader.inc
  9. 4 2
      tripal/includes/TripalFields/TripalField.inc
  10. 26 17
      tripal/includes/TripalFields/TripalFieldFormatter.inc
  11. 25 2
      tripal/includes/tripal.entity.inc
  12. 27 25
      tripal/includes/tripal.fields.inc
  13. 0 31
      tripal_chado/api/modules/tripal_chado.analysis.api.inc
  14. 1 1
      tripal_chado/includes/TripalImporter/FASTAImporter.inc
  15. 0 1
      tripal_chado/includes/setup/tripal_chado.chado_v1_1.inc
  16. 0 1
      tripal_chado/includes/setup/tripal_chado.chado_vx_x.inc
  17. 1 1
      tripal_chado/includes/tripal_chado.entity.inc
  18. 214 165
      tripal_chado/includes/tripal_chado.pub_search.inc
  19. 35 2
      tripal_chado/tripal_chado.module
  20. 5 179
      tripal_chado_views/api/tripal_chado_views.api.inc
  21. BIN
      tripal_ws/.DS_Store
  22. 118 0
      tripal_ws/includes/TripalFields/WebServicesField.inc
  23. 118 0
      tripal_ws/includes/TripalFields/WebServicesFieldFormatter.inc
  24. 5 0
      tripal_ws/includes/TripalFields/WebServicesFieldWidget.inc
  25. 391 0
      tripal_ws/includes/TripalFields/remote__data/remote__data.inc
  26. 368 0
      tripal_ws/includes/TripalFields/remote__data/remote__data_formatter.inc
  27. 169 0
      tripal_ws/includes/TripalFields/remote__data/remote__data_widget.inc
  28. 142 0
      tripal_ws/includes/TripalWebService.inc
  29. 87 70
      tripal_ws/includes/TripalWebService/TripalEntityService_v0_1.inc
  30. 9 1
      tripal_ws/includes/TripalWebServiceResource.inc
  31. 78 0
      tripal_ws/includes/tripal_ws.field_storage.inc
  32. 95 0
      tripal_ws/includes/tripal_ws.fields.inc
  33. 37 0
      tripal_ws/theme/css/tripal_ws.css
  34. 4 2
      tripal_ws/tripal_ws.info
  35. 5 1
      tripal_ws/tripal_ws.install
  36. 43 0
      tripal_ws/tripal_ws.module

+ 339 - 0
LICENSE.txt

@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.

+ 30 - 0
legacy/tripal_analysis/tripal_analysis.module

@@ -249,5 +249,35 @@ function tripal_analysis_form_alter(&$form, &$form_state, $form_id) {
 }
 
 
+/**
+ * Register tripal_analysis_api sub-modules
+ *
+ * @param $modulename
+ *  The name of the module to be registered as a tripal analysis submodule
+ *
+ * @ingroup tripal_analysis_api
+ */
+function tripal_register_analysis_child($modulename) {
+  $sql = "SELECT * FROM {tripal_analysis} WHERE modulename = :modname";
+  if (!db_query($sql, array(':modname' => $modulename))->fetchField()) {
+    $sql = "INSERT INTO {tripal_analysis} (modulename) VALUES (:modname)";
+    db_query($sql, array(':modname' => $modulename));
+  }
+}
+
+/**
+ * Un-register a tripal analysis sub-module
+ *
+ * @param $modulename
+ *  The name of the module to un-register
+ *
+ * @ingroup tripal_analysis_api
+ */
+function tripal_unregister_analysis_child($modulename) {
+  if (db_table_exists('tripal_analysis')) {
+    $sql = "DELETE FROM {tripal_analysis} WHERE modulename = :modname";
+    db_query($sql, array(':modname' => $modulename));
+  }
+}
 
 

+ 0 - 45
legacy/tripal_pub/includes/tripal_pub.admin.inc

@@ -66,42 +66,6 @@ function tripal_pub_admin() {
   // This sub-form handles it's own validation & submit
   chado_add_admin_form_set_title($form, $form_state, $details);
 
-  // -----------------------------------------
-  // add in the fields for selecting which fields are used when search for pubs
-  $form['searching'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Searching Options'),
-    '#description' => t("The list of checkboxes below indicate which fields a user
-      can search with when using the publication search tool.  Check the fields that you want
-      to allow users to search with.  Click the 'Save configuration' button below to save changes."),
-  );
-
-  // get publication properties list
-  $properties = array();
-  $properties[] = 'Any Field';
-  $sql = "
-    SELECT DISTINCT CVTS.cvterm_id, CVTS.name, CVTS.definition
-    FROM {cvtermpath} CVTP
-      INNER JOIN {cvterm} CVTS ON CVTP.subject_id = CVTS.cvterm_id
-      INNER JOIN {cvterm} CVTO ON CVTP.object_id = CVTO.cvterm_id
-      INNER JOIN {cv} ON CVTO.cv_id = CV.cv_id
-    WHERE CV.name = 'tripal_pub' and
-      (CVTO.name = 'Publication Details' or CVTS.name = 'Publication Type') and
-      NOT CVTS.is_obsolete = 1
-    ORDER BY CVTS.name ASC
-  ";
-  $prop_types = chado_query($sql);
-  while ($prop = $prop_types->fetchObject()) {
-    $properties[$prop->cvterm_id] = $prop->name;
-  }
-  $form['searching']['allowed_search_fields'] = array(
-    '#type'    => 'checkboxes',
-    '#options' => $properties,
-    '#prefix'  => '<div style="scroll: auto; border:1px solid #CCCCCC;">',
-    '#suffix'  => '</div>',
-    '#default_value' => variable_get('tripal_pub_allowed_search_fields', array()),
-  );
-
   // -----------------------------------------
   // add the field set for syncing publications
   $form['import'] = array(
@@ -179,15 +143,6 @@ function tripal_pub_admin_validate($form, &$form_state) {
   global $user;  // we need access to the user info
   $job_args = array();
 
-  // set the allowed search fields
-  $allowed_fields = $form_state['values']['allowed_search_fields'];
-  foreach ($allowed_fields as $cvterm_id => $selected) {
-    if (!$selected) {
-      unset($allowed_fields[$cvterm_id]);
-    }
-  }
-  variable_set('tripal_pub_allowed_search_fields', $allowed_fields);
-
   $import_duplicate_check = $form_state['values']['import_duplicate_check'];
   variable_set('tripal_pub_import_duplicate_check', $import_duplicate_check);
 

+ 2 - 2
legacy/tripal_pub/theme/templates/tripal_pub_help.tpl.php

@@ -29,7 +29,7 @@ have been added to Chado database.</p>
   </li>
   <li>
   <p><b>Configure the Search Behavior</b>: Before allowing site visitors
-  to search for publications visit the <?php print l('configuration page', 'admin/tripal/tripal_pub/configuration') ?>
+  to search for publications visit the <?php print l('configuration page', 'admin/tripal/storage/chado/pub-search-config') ?>
   to disable or enable fields for searching. Tripal uses its own ontology
   for storing publication information in Chado, and all child terms of
   the "Publication Details" are made available for searching. However,
@@ -48,7 +48,7 @@ have been added to Chado database.</p>
     <li>Install the PHP YAZ extension: sudo pecl install yaz</li>
     <li>Add the text 'extension=yaz.so' to the appropriate php.ini file
     (e.g. /etc/php5/apache2filter/php.ini). On Ubuntu you may need to
-    add it to the php.ini file specfic for the Apache webserver and 
+    add it to the php.ini file specfic for the Apache webserver and
     also to the php.ini specific for the command-line.</li>
     <li>Restart the webserver</li>
   </ol>

+ 0 - 14
legacy/tripal_pub/tripal_pub.module

@@ -10,7 +10,6 @@ require_once 'theme/tripal_pub.theme.inc';
 
 require_once 'includes/tripal_pub.admin.inc';
 require_once 'includes/tripal_pub.chado_node.inc';
-require_once 'includes/tripal_pub.pub_search.inc';
 
 /**
  * @defgroup tripal_pub Publication Module
@@ -53,20 +52,7 @@ function tripal_pub_menu() {
 
   // for backwards compatibility the same chado/publications is also found
   // at find/publications
-  $items['find/publications' ]= array(
-    'title' => 'Publication Search',
-    'description' => ('Search for publications'),
-    'page callback' => 'tripal_pub_search_page',
-    'access arguments' => array('access chado_pub content'),
-    'type' => MENU_CALLBACK
-  );
 
-  $items['find/publications/criteria/%/%'] = array(
-    'page callback' => 'tripal_pub_search_page_update_criteria',
-    'page arguments' => array(5, 6),
-    'access arguments' => array('access chado_pub content'),
-    'type ' => MENU_CALLBACK,
-  );
 
   $items['admin/tripal/legacy/tripal_pub']= array(
     'title' => 'Publications',

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

@@ -1113,4 +1113,20 @@ function theme_token_list($tokens) {
   return theme('table', array('header' => $header, 'rows' => $rows));
 }
 
+/**
+ * Define the entity label callback.  This will return the title.
+ *
+ * @param $entity
+ *
+ * @return mixed
+ */
+
+function tripal_entity_label($entity) {
+  if (property_exists($entity, 'title')) {
+    return $entity->title;
+  }
+  return NULL;
+}
+
+
 

+ 11 - 2
tripal/includes/TripalEntityUIController.inc

@@ -308,6 +308,14 @@ function tripal_view_entity($entity, $view_mode = 'full') {
    if ($etype) {
      $query->entityCondition('bundle', $etype);
    }
+   if ($status) {
+     if ($status == 'status-1') {
+       $query->propertyCondition('status', 1);
+     }
+     if ($status == 'status-0') {
+       $query->propertyCondition('status', 0);
+     }
+   }
    //$query->propertyOrderBy('created', 'DESC');
 
    // Find out the total number of records and determine what page we're on, and
@@ -324,8 +332,9 @@ function tripal_view_entity($entity, $view_mode = 'full') {
 
 
    // For each entity retrieved add a row to the data listing.
-   //while ($entity = $entities->fetchObject()) {
-   if (!isset($results['TripalEntity'])) $results['TripalEntity'] = array();
+   if (!isset($results['TripalEntity'])) {
+     $results['TripalEntity'] = array();
+   }
    foreach ($results['TripalEntity'] as $entity_id => $stub) {
      $vocabulary = '';
      $term_name = '';

+ 1 - 1
tripal/includes/TripalFieldDownloaders/TripalFieldDownloader.inc

@@ -153,7 +153,7 @@ abstract class TripalFieldDownloader {
   }
 
   /**
-   * Formats the entity and the specified fields for utput.
+   * Formats the entity and the specified fields for output.
    *
    * This function should be implemented by a child class. It should iterate
    * over the fields for the entity and return the appropriate format. It may

+ 4 - 2
tripal/includes/TripalFields/TripalField.inc

@@ -109,7 +109,9 @@ class TripalField {
    *   An array containing the instance data as returned by field_instance_info().
    */
   public function __construct($field, $instance) {
-    $vocabulary = $term = NULL;
+    $term = NULL;
+    $vocabulary = NULL;
+    $accession = NULL;
     $this->field = $field;
     $this->instance = $instance;
 
@@ -495,7 +497,7 @@ class TripalField {
   }
 
   /**
-   * Provides the list of elements returned by the 'value' of the field. \
+   * Provides the list of elements returned by the 'value' of the field.
    *
    * The elements provided by this function are used to integrate with
    * Drupal Views and Web services.  The return value is an associative array

+ 26 - 17
tripal/includes/TripalFields/TripalFieldFormatter.inc

@@ -53,6 +53,7 @@ class TripalFieldFormatter {
       'label' => $class::$default_label,
       'field types' => $class::$field_types,
       'settings' => $class::$default_settings,
+      'TripalFieldFormatter' => TRUE,
     );
   }
 
@@ -98,23 +99,31 @@ class TripalFieldFormatter {
    * This function corresponds to the hook_field_formatter_view()
    * function of the Drupal Field API.
    *
-   *  This function provides the display for a field when it is viewed on
-   *  the web page.  The content returned by the formatter should only include
-   *  what is present in the $items[$delta]['values] array. This way, the
-   *  contents that are displayed on the page, via webservices and downloaded
-   *  into a CSV file will always be identical.  The view need not show all
-   *  of the data in the 'values' array.
-   *
-   *  @param $element
-   *  @param $entity_type
-   *  @param $entity
-   *  @param $langcode
-   *  @param $items
-   *  @param $display
-   *
-   *  @return
-   *    An element array compatible with that returned by the
-   *    hook_field_formatter_view() function.
+   * This function provides the display for a field when it is viewed on
+   * as a full page, teaser, indexing for searching, etc.  The content
+   * returned by the formatter should only include what is present in the
+   * $items[$delta]['values] array. This way, the contents that are displayed
+   * on the page, via web services and downloaded into a CSV file will
+   * always be identical.  The view need not show all of the data in the
+   * 'values' array.
+   *
+   * @param $element
+   *   A renderable array for the $items, as an array of child elements keyed
+   *   by numeric indexes starting from 0.  When implemented as a child
+   *   class, this argument is set for the display.
+   * @param $entity_type
+   *   The type of $entity.
+   * @param $entity
+   *   The entity object.
+   * @param $langcode
+   *   The language associated with $items.
+   * @param $items
+   *   Array of values for this field.
+   * @param $display
+   *   The display settings to use, as found in the 'display' entry of instance
+   *   definitions. The array notably contains the following keys and values;
+   *     - type: The name of the formatter to use.
+   *     - settings: The array of formatter settings.
    */
   public function view(&$element, $entity_type, $entity, $langcode, $items, $display) {
 

+ 25 - 2
tripal/includes/tripal.entity.inc

@@ -139,7 +139,7 @@ function tripal_entity_info() {
     'base table' => 'tripal_entity',
 
     // Returns the uri elements of an entity.
-    'uri callback' => 'tripal_vocbulary_term_uri',
+    'uri callback' => 'tripal_entity_uri',
 
     // IF fieldable == FALSE, we can't attach fields.
     'fieldable' => TRUE,
@@ -191,7 +191,20 @@ function tripal_entity_info() {
     )
   );
 
-  //
+  // Search integration
+  if (module_exists('search')) {
+    $entities['TripalEntity']['view modes'] += array(
+      'search_index' => array(
+        'label' => t('Search index'),
+        'custom settings' => FALSE,
+      ),
+      'search_result' => array(
+        'label' => t('Search result highlighting input'),
+        'custom settings' => FALSE,
+      ),
+    );
+  }
+
   // The TripalBundle entity is used manage the bundle types.  The 'bundle of'
   // attribute links this to the TripalEntity and allows the UI provided
   // by the entity module to work for each TripalEntity bundle.
@@ -224,6 +237,16 @@ function tripal_entity_info() {
   return $entities;
 }
 
+/**
+ * Implements the Entity URI callback function.
+ */
+function tripal_entity_uri($entity) {
+  return array(
+    'path' => 'bio-data/' . $entity->id,
+    'options' => array(),
+  );
+}
+
 /**
  * Implements hook_entities_info_alter().
  *

+ 27 - 25
tripal/includes/tripal.fields.inc

@@ -220,11 +220,6 @@ function tripal_field_formatter_view($entity_type, $entity, $field,
     return;
   }
 
-  // TODO: (Shawna) 1) Check the entity type (bundle) settings to see if empty fieleds
-  // should be ignored by default. 2)  Check the field itself to see if it
-  // overides the bundle default.  The idea being do not show fields when
-  // people don't want them shown.
-
   $element = array();
   $formatter_class = $display['type'];
   $is_loaded = tripal_load_include_field_class($formatter_class);
@@ -430,7 +425,7 @@ function tripal_field_instance_settings_form_validate($element, &$form_state, $f
   $field_class = $field['type'];
   if (tripal_load_include_field_class($field_class)) {
     $field = new $field_class($field, $instance);
-    return $field->instanceSettingsForm();
+    return $field->instanceSettingsFormValidate($form, $form_state);
   }
 }
 
@@ -475,7 +470,8 @@ function tripal_field_instance_settings_form_alter_process($element, &$form_stat
   $accession = '';
   $term_name = '';
   $term = NULL;
-  if (array_key_exists('settings', $instance) and array_key_exists('term_vocabulary', $instance['settings'])) {
+  if (array_key_exists('settings', $instance) and
+      array_key_exists('term_vocabulary', $instance['settings'])) {
     $vocabulary = $instance['settings']['term_vocabulary'];
     $accession = $instance['settings']['term_accession'];
     $term_name = $instance['settings']['term_name'];
@@ -536,25 +532,25 @@ function tripal_field_instance_settings_form_alter_process($element, &$form_stat
         be associated with a controlled vocabulary term. This field mapping is
         required and cannot be changed');
   }
-  $element['field_term'] = array(
-    '#type' => 'fieldset',
-    '#title' => 'Controlled Vocabulary Term',
-    '#description' => $description,
-    '#prefix' => '<div id = "tripal-field-term-fieldset">',
-    '#suffix' => '</div>',
-  );
-  $element['field_term']['term_vocabulary'] = array(
+  $element['term_vocabulary'] = array(
     '#type' => 'value',
     '#value' => $vocabulary,
   );
-  $element['field_term']['term_name'] = array(
+  $element['term_name'] = array(
     '#type' => 'value',
     '#value' => $term_name,
   );
-  $element['field_term']['term_accession'] = array(
+  $element['term_accession'] = array(
     '#type' => 'value',
     '#value' => $accession,
   );
+  $element['field_term'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'Controlled Vocabulary Term',
+    '#description' => $description,
+    '#prefix' => '<div id = "tripal-field-term-fieldset">',
+    '#suffix' => '</div>',
+  );
   $element['field_term']['details'] = array(
     '#type' => 'item',
     '#title' => 'Current Term',
@@ -606,7 +602,7 @@ function tripal_field_instance_settings_form_alter_process($element, &$form_stat
     $terms = chado_expand_var($terms, 'field', 'cvterm.definition');
     $num_terms = 0;
     foreach ($terms as $term) {
-      // Save the user a click by setting the default value as 1 if there's
+      // Save the user a click, by setting the default value as 1 if there's
       // only one matching term.
       $default = FALSE;
       $attrs = array();
@@ -653,6 +649,9 @@ function tripal_field_instance_settings_form_alter_validate($form, &$form_state)
   if (array_key_exists('clicked_button', $form_state) and $form_state['clicked_button']['#executes_submit_callback'] == TRUE) {
     $has_default = FALSE;
     if ($form_state['values']['term_vocabulary']) {
+      $form_state['values']['instance']['settings']['term_vocabulary'] = $form_state['values']['term_vocabulary'];
+      $form_state['values']['instance']['settings']['term_accession'] = $form_state['values']['term_accession'];
+      $form_state['values']['instance']['settings']['term_name'] = $form_state['values']['term_name'];
       $has_default = TRUE;
     }
 
@@ -670,14 +669,17 @@ function tripal_field_instance_settings_form_alter_validate($form, &$form_state)
       if (preg_match("/^term-(\d+)$/", $key, $matches) and
           $form_state['input']['term-' . $matches[1]]) {
         $cvterm_id = $matches[1];
-        // TODO: this should not call a Chado function.
+        // TODO: this should not call a Chado function, but the autocomplete
+        // currently uses chado cvterm IDs.
         $term = chado_generate_var('cvterm', array('cvterm_id' => $cvterm_id));
-        $form_state['values']['instance']['settings']['term_vocabulary'] = $term->dbxref_id->db_id->name;
-        $form_state['values']['instance']['settings']['term_accession'] = $term->dbxref_id->accession;
-        $form_state['values']['instance']['settings']['term_name'] = $term->name;
-        $selected_term = TRUE;
-        $num_selected++;
-        $has_default =  TRUE;
+        if ($term) {
+          $form_state['values']['instance']['settings']['term_vocabulary'] = $term->dbxref_id->db_id->name;
+          $form_state['values']['instance']['settings']['term_accession'] = $term->dbxref_id->accession;
+          $form_state['values']['instance']['settings']['term_name'] = $term->name;
+          $selected_term = TRUE;
+          $num_selected++;
+          $has_default =  TRUE;
+        }
       }
     }
 

+ 0 - 31
tripal_chado/api/modules/tripal_chado.analysis.api.inc

@@ -14,36 +14,6 @@
  * @}
  */
 
-/**
- * Register tripal_analysis_api sub-modules
- *
- * @param $modulename
- *  The name of the module to be registered as a tripal analysis submodule
- *
- * @ingroup tripal_analysis_api
- */
-function tripal_register_analysis_child($modulename) {
-  $sql = "SELECT * FROM {tripal_analysis} WHERE modulename = :modname";
-  if (!db_query($sql, array(':modname' => $modulename))->fetchField()) {
-    $sql = "INSERT INTO {tripal_analysis} (modulename) VALUES (:modname)";
-    db_query($sql, array(':modname' => $modulename));
-  }
-}
-
-/**
- * Un-register a tripal analysis sub-module
- *
- * @param $modulename
- *  The name of the module to un-register
- *
- * @ingroup tripal_analysis_api
- */
-function tripal_unregister_analysis_child($modulename) {
-  if (db_table_exists('tripal_analysis')) {
-      $sql = "DELETE FROM {tripal_analysis} WHERE modulename = :modname";
-      db_query($sql, array(':modname' => $modulename));
-  }
-}
 
 /**
  * Retrieves an chado analysis variable
@@ -165,7 +135,6 @@ function tripal_get_analysis($identifier, $options) {
  *   An array of analyses sync'd with Drupal where each value is the analysis scientific
  *   name and the keys are analysis_id's
  *
- * @ingroup tripal_analysis_api
  */
 function tripal_get_analysis_select_options($syncd_only = TRUE) {
   $analysis_list = array();

+ 1 - 1
tripal_chado/includes/TripalImporter/FASTAImporter.inc

@@ -21,7 +21,7 @@ class FASTAImporter extends TripalImporter {
   /**
    * An array containing the extensions of allowed file types.
    */
-  public static $file_types = array('fasta', 'fa');
+  public static $file_types = array('fasta', 'txt', 'fa', 'aa', 'pep', 'nuc', 'faa', 'fna');
 
 
   /**

+ 0 - 1
tripal_chado/includes/setup/tripal_chado.chado_v1_1.inc

@@ -3,7 +3,6 @@
  * Create a legacy custom chado table (analysisfeatureprop) to store properties of
  * analysisfeature links.
  *
- * @ingroup tripal_analysis
  */
 function tripal_chado_add_analysisfeatureprop_table() {
 

+ 0 - 1
tripal_chado/includes/setup/tripal_chado.chado_vx_x.inc

@@ -352,7 +352,6 @@ function tripal_chado_add_organism_feature_count_mview() {
 /**
  * Creates a view showing the link between an organism & it's analysis through associated features.
  *
- * @ingroup tripal_analysis
  */
 function tripal_chado_add_analysis_organism_mview() {
   $view_name = 'analysis_organism';

+ 1 - 1
tripal_chado/includes/tripal_chado.entity.inc

@@ -152,7 +152,7 @@ function tripal_chado_tripal_default_title_format($bundle, $available_tokens) {
     }
     else {
       $format[] = array(
-        'format' => '[taxrank__genus] [taxrank__species] [taxrank__infraspecies]',
+        'format' => '[taxrank__genus] [taxrank__species] [taxrank__infraspecific_taxon]',
         'weight' => -5
       );
     }

+ 214 - 165
legacy/tripal_pub/includes/tripal_pub.pub_search.inc → tripal_chado/includes/tripal_chado.pub_search.inc

@@ -7,12 +7,68 @@
  * in Chado.
  */
 
+function tripal_chado_pub_search_admin_form($form, &$form_state) {
+  // -----------------------------------------
+  // add in the fields for selecting which fields are used when search for pubs
+  $form['searching'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Searching Options'),
+    '#description' => t("The list of checkboxes below indicate which fields a user
+      can search with when using the publication search tool.  Check the fields that you want
+      to allow users to search with.  Click the 'Save configuration' button below to save changes."),
+  );
+
+  // get publication properties list
+  $properties = array();
+  $properties[] = 'Any Field';
+  $sql = "
+    SELECT DISTINCT CVTS.cvterm_id, CVTS.name, CVTS.definition
+    FROM {cvtermpath} CVTP
+      INNER JOIN {cvterm} CVTS ON CVTP.subject_id = CVTS.cvterm_id
+      INNER JOIN {cvterm} CVTO ON CVTP.object_id = CVTO.cvterm_id
+      INNER JOIN {cv} ON CVTO.cv_id = CV.cv_id
+    WHERE CV.name = 'tripal_pub' and
+      (CVTO.name = 'Publication Details' or CVTS.name = 'Publication Type') and
+      NOT CVTS.is_obsolete = 1
+    ORDER BY CVTS.name ASC
+  ";
+  $prop_types = chado_query($sql);
+  while ($prop = $prop_types->fetchObject()) {
+    $properties[$prop->cvterm_id] = $prop->name;
+  }
+  $form['allowed_search_fields'] = array(
+    '#type'    => 'checkboxes',
+    '#options' => $properties,
+    '#default_value' => variable_get('tripal_pub_allowed_search_fields', array()),
+  );
+
+  $form['button'] = array(
+    '#type' => 'submit',
+    '#value' => 'Save configuration',
+    '#name' => 'sumbit',
+  );
+  return $form;
+}
+/**
+ *
+ */
+function tripal_chado_pub_search_admin_form_submit($form, &$form_state) {
+  // set the allowed search fields
+  $allowed_fields = $form_state['values']['allowed_search_fields'];
+  foreach ($allowed_fields as $cvterm_id => $selected) {
+    if (!$selected) {
+      unset($allowed_fields[$cvterm_id]);
+    }
+  }
+  variable_set('tripal_pub_allowed_search_fields', $allowed_fields);
+  drupal_set_message('Changes saved.');
+}
 /**
  * The page that contains the publication search form and the results for the search
  *
  * @ingroup tripal_pub
  */
-function tripal_pub_search_page() {
+function tripal_chado_pub_search_page() {
 
   // This line may not be required, but on some sites the $_SESSION
   // variable wasn't being set for anonymous users. This line solves that
@@ -22,117 +78,111 @@ function tripal_pub_search_page() {
   $limit = 25;
 
   // generate the search form
-  $form = drupal_get_form('tripal_pub_search_form');
+  $form = drupal_get_form('tripal_chado_pub_search_form');
   $output = drupal_render($form);
 
   // retrieve any results
-  if (array_key_exists('tripal_pub_search_form', $_SESSION) and
-      $_SESSION['tripal_pub_search_form']['perform_search']) {
-    $num_criteria = $_SESSION['tripal_pub_search_form']['num_criteria'];
-    $from_year    = $_SESSION['tripal_pub_search_form']['from_year'];
-    $to_year      = $_SESSION['tripal_pub_search_form']['to_year'];
-
-    $search_array = array();
-    $search_array['num_criteria'] = $num_criteria;
-    $search_array['from_year']    = $from_year;
-    $search_array['to_year']      = $to_year;
-    for ($i = 0; $i <= $num_criteria; $i++) {
-      $search_array['criteria'][$i]['search_terms'] = $_SESSION['tripal_pub_search_form']['criteria'][$i]['search_terms'];
-      $search_array['criteria'][$i]['scope']        = $_SESSION['tripal_pub_search_form']['criteria'][$i]['scope'];
-      $search_array['criteria'][$i]['mode']         = $_SESSION['tripal_pub_search_form']['criteria'][$i]['mode'];
-      $search_array['criteria'][$i]['operation']    = $_SESSION['tripal_pub_search_form']['criteria'][$i]['operation'];
-    }
+  if (array_key_exists('tripal_chado_pub_search_form', $_SESSION) and
+      $_SESSION['tripal_chado_pub_search_form']['perform_search']) {
+        $num_criteria = $_SESSION['tripal_chado_pub_search_form']['num_criteria'];
+        $from_year    = $_SESSION['tripal_chado_pub_search_form']['from_year'];
+        $to_year      = $_SESSION['tripal_chado_pub_search_form']['to_year'];
+
+        $search_array = array();
+        $search_array['num_criteria'] = $num_criteria;
+        $search_array['from_year']    = $from_year;
+        $search_array['to_year']      = $to_year;
+        for ($i = 0; $i <= $num_criteria; $i++) {
+          $search_array['criteria'][$i]['search_terms'] = $_SESSION['tripal_chado_pub_search_form']['criteria'][$i]['search_terms'];
+          $search_array['criteria'][$i]['scope']        = $_SESSION['tripal_chado_pub_search_form']['criteria'][$i]['scope'];
+          $search_array['criteria'][$i]['mode']         = $_SESSION['tripal_chado_pub_search_form']['criteria'][$i]['mode'];
+          $search_array['criteria'][$i]['operation']    = $_SESSION['tripal_chado_pub_search_form']['criteria'][$i]['operation'];
+        }
+
+        // get the list of publications from the remote database using the search criteria.
+        $page = isset($_GET['page']) ? $_GET['page'] : '0';
+        $offset = $page * $limit;
+        $total_records = 0;
+        $pubs = tripal_search_publications($search_array, $offset, $limit, $total_records);
+        pager_default_initialize($total_records, $limit, 0);
+
+        // iterate through the results and construct the table displaying the publications
+        $rows = array();
+        $i = $page * $limit + 1;
+        foreach ($pubs as $pub) {
+          // get the citation for this publication
+          $values = array(
+            'pub_id' => $pub->pub_id,
+            'type_id' => array(
+              'name' => 'Citation',
+            ),
+          );
+          $citation_rec = chado_generate_var('pubprop', $values);
+          $citation_rec = chado_expand_var($citation_rec, 'field', 'pubprop.value');
+
+          // if we have the citation then use it, otherwise, just use the title
+          $title = htmlspecialchars($pub->title);
+          $result = $title;
+          $link = NULL;
+
+          // tripal v2 link (node)
+          if (module_exists('tripal_pub')) {
+            $nid = chado_get_nid_from_id ('pub', $pub->pub_id);
+            if ($nid) {
+              $link = "/node/$nid";
+            }
+          }
+          // try tripal v3 link (entity), if it exists, update the link to entity
+          $entity_id = tripal_get_chado_entity_id ('pub', $pub->pub_id);
+          if ($entity_id) {
+            $link = "/bio_data/$entity_id";
+          }
+
+          if ($link) {
+            $result = l($title , $link, array('attributes' => array('target' => '_blank')));
+          }
+          if ($citation_rec->value) {
+            $citation = htmlspecialchars($citation_rec->value);
+            $result .= '<br>' . $citation;
+          }
+          $rows[] = array(
+            number_format($i) . ".",
+            $pub->pyear,
+            $result
+          );
+          $i++;
+        }
+
+        $headers = array('', 'Year', 'Reference');
+        $table = array(
+          'header' => $headers,
+          'rows' => $rows,
+          'attributes' => array(
+            'id' => 'tripal-pub-search-results-table',
+            'border' => '0',
+            'class' => array('tripal-data-table')
+          ),
+          'sticky' => TRUE,
+          'caption' => '',
+          'colgroups' => array(),
+          'empty' => 'No publications found',
+        );
+        $results = theme_table($table);
+
+        // generate the pager
+        $pager = array(
+          'tags' => array(),
+          'element' => 0,
+          'parameters' => array(),
+          'quantity' => $limit,
+        );
+        $pager = theme_pager($pager);
 
-    // get the list of publications from the remote database using the search criteria.
-    $page = isset($_GET['page']) ? $_GET['page'] : '0';
-    $offset = $page * $limit;
-    $total_records = 0;
-    $pubs = tripal_search_publications($search_array, $offset, $limit, $total_records);
-    pager_default_initialize($total_records, $limit, 0);
-
-    // iterate through the results and construct the table displaying the publications
-    $rows = array();
-    $i = $page * $limit + 1;
-    foreach ($pubs as $pub) {
-      // get the citation for this publication
-      $values = array(
-        'pub_id' => $pub->pub_id,
-        'type_id' => array(
-          'name' => 'Citation',
-        ),
-      );
-      $citation_rec = chado_generate_var('pubprop', $values);
-      $citation_rec = chado_expand_var($citation_rec, 'field', 'pubprop.value');
-
-      // if we have the citation then use it, otherwise, just use the title
-      $title = htmlspecialchars($pub->title);
-      $result = $title;
-      $link = NULL;
-      // tripal v2 link (node)
-      $nid = chado_get_nid_from_id ('pub', $pub->pub_id);
-      if ($nid) {
-        $link = "/node/$nid";
-      }
-      // try tripal v3 link (entity), if it exists, update the link to entity
-      $entity_id = tripal_get_chado_entity_id ('pub', $pub->pub_id);
-      if ($entity_id) {
-        $link = "/bio_data/$entity_id";
+        // join all to form the results
+        $output .= "<p><b>Found " . number_format($total_records) .
+        " Results</b></br>" . $results . $pager;
       }
-      
-      if ($link) {
-        $result = l($title , $link, array('attributes' => array('target' => '_blank')));
-      }
-      if ($citation_rec->value) {
-        $citation = htmlspecialchars($citation_rec->value);
-        $result .= '<br>' . $citation;
-      }
-      $rows[] = array(
-      number_format($i) . ".",
-      $pub->pyear,
-      $result
-      );
-      $i++;
-    }
-
-    if (count($rows) == 0) {
-      $rows[] = array(
-        array(
-          'data' => 'No results found',
-          'colspan' => 3
-        )
-      );
-    }
-
-    $headers = array('', 'Year', 'Publication');
-    $table = array(
-      'header' => $headers,
-      'rows' => $rows,
-      'attributes' => array(
-        'id' => 'tripal-pub-search-results-table',
-        'border' => '0',
-        'class' => array('tripal-data-table')
-      ),
-      'sticky' => TRUE,
-      'caption' => '',
-      'colgroups' => array(),
-      'empty' => '',
-    );
-    $results = theme_table($table);
-
-    // generate the pager
-    $pager = array(
-      'tags' => array(),
-      'element' => 0,
-      'parameters' => array(),
-      'quantity' => $limit,
-    );
-    $pager = theme_pager($pager);
-
-    // join all to form the results
-    $output .= "<p><b>Found " . number_format($total_records) .
-      " Results</b></br>" . $results . $pager;
-  }
-  return $output;
+      return $output;
 }
 
 /**
@@ -140,7 +190,7 @@ function tripal_pub_search_page() {
  *
  * @ingroup tripal_pub
  */
-function tripal_pub_search_form($form, &$form_state) {
+function tripal_chado_pub_search_form($form, &$form_state) {
   // Default values can come in the following ways:
   //
   // 1) as elements of the $pub_importer object.  This occurs when editing an existing importer
@@ -162,10 +212,10 @@ function tripal_pub_search_form($form, &$form_state) {
   if (array_key_exists('storage', $form_state)) {
     $num_criteria = $form_state['storage']['num_criteria'];
   }
-  if (array_key_exists('tripal_pub_search_form', $_SESSION)) {
-    $num_criteria = $_SESSION['tripal_pub_search_form']['num_criteria'] ? $_SESSION['tripal_pub_search_form']['num_criteria'] : $num_criteria;
-    $from_year    = $_SESSION['tripal_pub_search_form']['from_year']    ? $_SESSION['tripal_pub_search_form']['from_year']    : '';
-    $to_year      = $_SESSION['tripal_pub_search_form']['to_year']      ? $_SESSION['tripal_pub_search_form']['to_year']      : '';
+  if (array_key_exists('tripal_chado_pub_search_form', $_SESSION)) {
+    $num_criteria = $_SESSION['tripal_chado_pub_search_form']['num_criteria'] ? $_SESSION['tripal_chado_pub_search_form']['num_criteria'] : $num_criteria;
+    $from_year    = $_SESSION['tripal_chado_pub_search_form']['from_year']    ? $_SESSION['tripal_chado_pub_search_form']['from_year']    : '';
+    $to_year      = $_SESSION['tripal_chado_pub_search_form']['to_year']      ? $_SESSION['tripal_chado_pub_search_form']['to_year']      : '';
   }
   if (array_key_exists('values', $form_state)) {
     $from_year    = $form_state['values']['from_year']    ? $form_state['values']['from_year']    : $from_year;
@@ -178,27 +228,27 @@ function tripal_pub_search_form($form, &$form_state) {
 
   if (array_key_exists('triggering_element', $form_state) and
       $form_state['triggering_element']['#name'] == 'add') {
-    $num_criteria++;
+        $num_criteria++;
   }
   if (array_key_exists('triggering_element', $form_state) and
       $form_state['triggering_element']['#name'] == 'remove') {
-    $num_criteria--;
+        $num_criteria--;
   }
 
   $form_state['storage']['num_criteria'] = $num_criteria;
 
   $form['admin-instructions'] = array(
     '#markup'  =>  tripal_set_message(
-      t('Administrators, you can select the fields with which a user can use to search, by checking the desired fields on the ' .
-        l('Publication Module Settings Page', 'admin/tripal/legacy/tripal_pub/configuration', array('attributes' => array('target' => '_blank'))) . '
-        in the section titled "Search Options".  The selected fields will appear in the dropdowns below.'),
+        t('Administrators, you can select the fields with which a user can use to search, by checking the desired fields on the ' .
+        l('Publication Search Settings Page', 'admin/tripal/storage/chado/pub-search-config', array('attributes' => array('target' => '_blank'))) . '
+      in the section titled "Search Options".  The selected fields will appear in the dropdowns below.'),
       TRIPAL_INFO,
       array('return_html' => 1)),
   );
   $form['instructions'] = array(
     '#markup'  =>  t('To search for publications enter keywords in the text boxes below.
-        You can limit your search by selecting the field in the dropdown box. Click the
-        add and remove buttons to add additional fields for searching. '),
+    You can limit your search by selecting the field in the dropdown box. Click the
+    add and remove buttons to add additional fields for searching. '),
   );
 
   // get publication properties list
@@ -230,11 +280,11 @@ function tripal_pub_search_form($form, &$form_state) {
     $mode = '';
 
     // first populate defaults using any values in the SESSION variable
-    if (array_key_exists('tripal_pub_search_form', $_SESSION)) {
-      $search_terms = $_SESSION['tripal_pub_search_form']['criteria'][$i]['search_terms'] ? $_SESSION['tripal_pub_search_form']['criteria'][$i]['search_terms'] : $search_terms;
-      $scope        = $_SESSION['tripal_pub_search_form']['criteria'][$i]['scope']        ? $_SESSION['tripal_pub_search_form']['criteria'][$i]['scope']        : $scope;
-      $mode         = $_SESSION['tripal_pub_search_form']['criteria'][$i]['mode']         ? $_SESSION['tripal_pub_search_form']['criteria'][$i]['mode']         : $mode;
-      $operation    = $_SESSION['tripal_pub_search_form']['criteria'][$i]['operation']    ? $_SESSION['tripal_pub_search_form']['criteria'][$i]['operation']    : $operation;
+    if (array_key_exists('tripal_chado_pub_search_form', $_SESSION)) {
+      $search_terms = $_SESSION['tripal_chado_pub_search_form']['criteria'][$i]['search_terms'] ? $_SESSION['tripal_chado_pub_search_form']['criteria'][$i]['search_terms'] : $search_terms;
+      $scope        = $_SESSION['tripal_chado_pub_search_form']['criteria'][$i]['scope']        ? $_SESSION['tripal_chado_pub_search_form']['criteria'][$i]['scope']        : $scope;
+      $mode         = $_SESSION['tripal_chado_pub_search_form']['criteria'][$i]['mode']         ? $_SESSION['tripal_chado_pub_search_form']['criteria'][$i]['mode']         : $mode;
+      $operation    = $_SESSION['tripal_chado_pub_search_form']['criteria'][$i]['operation']    ? $_SESSION['tripal_chado_pub_search_form']['criteria'][$i]['operation']    : $operation;
     }
     if (array_key_exists('values', $form_state)) {
       $search_terms = array_key_exists("search_terms-$i", $form_state['values']) ? $form_state['values']["search_terms-$i"] : $search_terms;
@@ -306,8 +356,8 @@ function tripal_pub_search_form($form, &$form_state) {
           // but we need it or Drupal will run the default validate anyway.
           // we also set #limit_validation_errors to empty so fields that
           // are required that don't have values won't generate warnings.
-          '#submit'   => array('tripal_pub_search_form_ajax_button_submit'),
-          '#validate' => array('tripal_pub_search_form_ajax_button_validate'),
+          '#submit'   => array('tripal_chado_pub_search_form_ajax_button_submit'),
+          '#validate' => array('tripal_chado_pub_search_form_ajax_button_validate'),
           '#limit_validation_errors' => array(),
         );
       }
@@ -329,16 +379,16 @@ function tripal_pub_search_form($form, &$form_state) {
         // but we need it or Drupal will run the default validate anyway.
         // we also set #limit_validation_errors to empty so fields that
         // are required that don't have values won't generate warnings.
-        '#submit'   => array('tripal_pub_search_form_ajax_button_submit'),
-        '#validate' => array('tripal_pub_search_form_ajax_button_validate'),
+        '#submit'   => array('tripal_chado_pub_search_form_ajax_button_submit'),
+        '#validate' => array('tripal_chado_pub_search_form_ajax_button_validate'),
         '#limit_validation_errors' => array(),
       );
     }
   }
   $form['criteria']["date"] = array(
-      '#type'          => 'select',
-      '#options'       => array('Years' => 'Years'),
-      '#attributes'    => array('class' => array('tripal-pub-search-form-scope-select')),
+    '#type'          => 'select',
+    '#options'       => array('Years' => 'Years'),
+    '#attributes'    => array('class' => array('tripal-pub-search-form-scope-select')),
   );
   $form['criteria']["from_year"] = array(
     '#type'          => 'textfield',
@@ -366,7 +416,7 @@ function tripal_pub_search_form($form, &$form_state) {
     '#value'        => t('Reset'),
   );
 
-  $form['criteria']['#theme'] = 'tripal_pub_search_setup_form_elements';
+  $form['criteria']['#theme'] = 'tripal_chado_pub_search_setup_form_elements';
 
   return $form;
 }
@@ -379,7 +429,7 @@ function tripal_pub_search_form($form, &$form_state) {
  *
  * @ingroup tripal_pub
  */
-function tripal_pub_search_form_ajax_button_submit() {
+function tripal_chado_pub_search_form_ajax_button_submit() {
   $form_state['rebuild'] = TRUE;
 }
 
@@ -388,16 +438,16 @@ function tripal_pub_search_form_ajax_button_submit() {
  *
  * @ingroup tripal_pub
  */
-function tripal_pub_search_form_ajax_button_validate() {
+function tripal_chado_pub_search_form_ajax_button_validate() {
   // do nothing
 }
 
 /**
- * Validate the tripal_pub_search_form form
+ * Validate the tripal_chado_pub_search_form form
  *
  * @ingroup tripal_pub
  */
-function tripal_pub_search_form_validate($form, &$form_state) {
+function tripal_chado_pub_search_form_validate($form, &$form_state) {
   $num_criteria = $form_state['storage']['num_criteria'];
   $from_year    = $form_state['values']['from_year'];
   $to_year      = $form_state['values']['to_year'];
@@ -423,11 +473,11 @@ function tripal_pub_search_form_validate($form, &$form_state) {
 }
 
 /**
- * Submit the tripal_pub_search_form form
+ * Submit the tripal_chado_pub_search_form form
  *
  * @ingroup tripal_pub
  */
-function tripal_pub_search_form_submit($form, &$form_state) {
+function tripal_chado_pub_search_form_submit($form, &$form_state) {
   $num_criteria = $form_state['storage']['num_criteria'];
   $from_year    = $form_state['values']['from_year'];
   $to_year      = $form_state['values']['to_year'];
@@ -435,8 +485,8 @@ function tripal_pub_search_form_submit($form, &$form_state) {
 
   // set the session variables
   if($op == 'Search') {
-    $_SESSION['tripal_pub_search_form']['num_criteria'] = $num_criteria;
-    unset($_SESSION['tripal_pub_search_form']['criteria']);
+    $_SESSION['tripal_chado_pub_search_form']['num_criteria'] = $num_criteria;
+    unset($_SESSION['tripal_chado_pub_search_form']['criteria']);
     for ($i = 0; $i <= $num_criteria; $i++) {
       $search_terms = '';
       $scope = '';
@@ -453,7 +503,7 @@ function tripal_pub_search_form_submit($form, &$form_state) {
       }
       //$mode =  $form_state['values']["mode-$i"];
 
-      $_SESSION['tripal_pub_search_form']['criteria'][$i] = array(
+      $_SESSION['tripal_chado_pub_search_form']['criteria'][$i] = array(
         'search_terms' => $search_terms,
         'scope' => $scope,
         'mode' => $mode,
@@ -461,12 +511,12 @@ function tripal_pub_search_form_submit($form, &$form_state) {
       );
 
     }
-    $_SESSION['tripal_pub_search_form']['from_year'] = $from_year;
-    $_SESSION['tripal_pub_search_form']['to_year'] = $to_year;
-    $_SESSION['tripal_pub_search_form']['perform_search'] = 1;
+    $_SESSION['tripal_chado_pub_search_form']['from_year'] = $from_year;
+    $_SESSION['tripal_chado_pub_search_form']['to_year'] = $to_year;
+    $_SESSION['tripal_chado_pub_search_form']['perform_search'] = 1;
   }
   if($op == 'Reset') {
-    unset($_SESSION['tripal_pub_search_form']);
+    unset($_SESSION['tripal_chado_pub_search_form']);
   }
 }
 
@@ -486,11 +536,11 @@ function tripal_pubs_search_form_ajax_update($form, $form_state) {
 }
 
 /**
- * Theme the tripal_pub_search_setup_form form
+ * Theme the tripal_chado_pub_search_setup_form form
  *
  * @ingroup tripal_pub
  */
-function theme_tripal_pub_search_setup_form_elements($variables) {
+function theme_tripal_chado_pub_search_setup_form_elements($variables) {
   $form = $variables['form'];
 
   $rows = array();
@@ -516,13 +566,13 @@ function theme_tripal_pub_search_setup_form_elements($variables) {
     drupal_render($form['date']),
     array(
       'data' =>
-        "<div id=\"pub-search-form-dates-row\">
-           <div id=\"pub-search-form-dates\"> ".
-             drupal_render($form['from_year']) .
-             drupal_render($form['to_year']) . "
-           </div>
+      "<div id=\"pub-search-form-dates-row\">
+         <div id=\"pub-search-form-dates\"> ".
+           drupal_render($form['from_year']) .
+           drupal_render($form['to_year']) . "
          </div>
-        ",
+       </div>
+      ",
     ),
     ''
   );
@@ -551,19 +601,21 @@ function theme_tripal_pub_search_setup_form_elements($variables) {
 /**
  * Builds the SQL statement need to search Chado for the publications
  * that match the user supplied criteria.  Tpyically, this function is
- * called by the search form generated by the tripal_pub_search_form() function
- * but this function is included in the API for calling by anyone.
+ * called by the search form generated by the tripal_chado_pub_search_form()
+ * function but this function is included in the API for calling by anyone.
  *
  * @param $search_array
  *   An array of search criteria provided by the user. The search array is
  *   an associative array with the following keys:
- *     'num_criteria': an integer indicating the number of search criteria supplied
+ *     'num_criteria': an integer indicating the number of search criteria
+ *        supplied
  *     'from_year':    filters records by a start year
  *     'to_year':      filters records by an end year
  *     'criteria':     an array of criteria. Each criteria is an associative
  *                     array with the following keys:
  *                     'search_terms':   The text used for searching
- *                     'scope':          The cvterm_id of the property used for filtering
+ *                     'scope':          The cvterm_id of the property used for
+ *                        filtering
  *                     'mode':           The operation (e.g. AND, OR or NOT)
  * @param $offset
  *   The offset for paging records.  The first record returned will be
@@ -584,15 +636,12 @@ function theme_tripal_pub_search_setup_form_elements($variables) {
  */
 function tripal_search_publications($search_array, $offset, $limit, &$total_records) {
 
-  // build the SQL based on the criteria provided by the user
-  $select = "SELECT DISTINCT P.*, CP.nid ";
-  $from   = "FROM {pub} P
-               LEFT JOIN [chado_pub] CP on P.pub_id = CP.pub_id
-               INNER JOIN {cvterm} CVT on CVT.cvterm_id = P.type_id
-            ";
-  $where  = "WHERE (NOT P.title = 'null') "; // always exclude the dummy pub
+  // Build the SQL based on the criteria provided by the user
+  $select = "SELECT DISTINCT P.* ";
+  $from   = "FROM {pub} P INNER JOIN {cvterm} CVT on CVT.cvterm_id = P.type_id ";
+  $where  = "WHERE (NOT P.title = 'null') ";
   $order  = "ORDER BY P.pyear DESC, P.title ASC";
-  $args = array();  // arguments for where clause
+  $args = array();
   $join = 0;
 
   $num_criteria = $search_array['num_criteria'];

+ 35 - 2
tripal_chado/tripal_chado.module

@@ -718,6 +718,39 @@ function tripal_chado_menu() {
     'type' => MENU_CALLBACK,
   );
 
+  //////////////////////////////////////////////////////////////////////////////
+  //                           Publications
+  //////////////////////////////////////////////////////////////////////////////
+  $items['find/publications' ]= array(
+    'title' => 'Publication Search',
+    'description' => ('Search for publications'),
+    'page callback' => 'tripal_chado_pub_search_page',
+    'access arguments' => array('access chado_pub content'),
+    'file' =>  'includes/tripal_chado.pub_search.inc',
+    'file path' => drupal_get_path('module', 'tripal_chado'),
+    'type' => MENU_CALLBACK
+  );
+
+  $items['find/publications/criteria/%/%'] = array(
+    'page callback' => 'tripal_chado_pub_search_page_update_criteria',
+    'page arguments' => array(5, 6),
+    'access arguments' => array('access chado_pub content'),
+    'file' =>  'includes/tripal_chado.pub_search.inc',
+    'file path' => drupal_get_path('module', 'tripal_chado'),
+    'type ' => MENU_CALLBACK,
+  );
+
+  $items['admin/tripal/storage/chado/pub-search-config'] = array(
+    'title' => 'Publication Search Settings',
+    'description' => 'Configure the settings for the publication search.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('tripal_chado_pub_search_admin_form'),
+    'file' =>  'includes/tripal_chado.pub_search.inc',
+    'file path' => drupal_get_path('module', 'tripal_chado'),
+    'type' => MENU_NORMAL_ITEM,
+    'access arguments' => array('administer tripal'),
+  );
+
   return $items;
 }
 
@@ -770,9 +803,9 @@ function tripal_chado_theme($existing, $type, $theme, $path) {
       'render element' => 'form',
       'file' => 'includes/loaders/tripal_chado.pub_importers.inc',
     ),
-    'tripal_pub_search_setup_form_elements' => array(
+    'tripal_chado_pub_search_setup_form_elements' => array(
       'render element' => 'form',
-      'file' => 'includes/loaders/tripal_chado.pub_importers.inc',
+      'file' => 'includes/tripal_chado.pub_search.inc',
     ),
   );
 

+ 5 - 179
tripal_chado_views/api/tripal_chado_views.api.inc

@@ -4,181 +4,6 @@
  * API functions for Chado Views Integration
  */
 
- /**
- * @defgroup tripal_chado_views_api Tripal Views Module API
- * @ingroup tripal_api
- * @{
- * Provides functions to help extension modules add their own tripal views integrations
- * as well as functions for managing default views.
- *
- * Managing Default Views:
- *
- * When you create administrative default views (or really any default view) you really
- * have no way to ensure that the site administrator keeps these views enabled. In some
- * cases this is a good thing and provides the site administrator the ability to disable
- * views that don't apply to their particular installation or to replace your default view
- * with a custom view of their own. But in other cases, particularily for administration
- * views, you want to gaurd against accidental disabling of your views.
- *
- * One way to do this is to add a landing page using a heook_menu() definition to a custom
- * callback returning HTML. You can use this page to render the view if it is enabled but
- * provide logic to display a message if the view is disabled. Furthermore, it's often
- * helpful to provide a link allowing site administrators to enable the view from your
- * page rather than expecting them to go to the views administration UI. This is done for
- * all the administration views provided by Tripal. The following is an example of how to
- * achomplish this functionality for your own module:
- * @code
-function mymodule_menu() {
-  $items = array();
-
-  // Create the landing page
-  $items['admin/tripal/<PATH-TO-YOUR-LANDING-PAGE>'] = array(
-    'title' => 'MyModule Administration',
-    'description' => 'Administration of my module.',
-    'page callback' => 'mymodule_admin_landing_page',
-    'access arguments' => array('<YOUR-PERMISSION-KEY>'),
-    'type' => MENU_NORMAL_ITEM,
-  );
-
-  // Create one of these for each of your default views
-  $items['admin/tripal/<PATH-TO-YOUR-ADMIN-SECTION>/views/<VIEW-MACHINE-NAME>/enable'] = array(
-    'title' => 'Enable <VIEW-HUMAN-READABLE-NAME>',
-    'page callback' => 'tripal_enable_view',
-    'page arguments' => array('<VIEW-MACHINE-NAME>', '<PATH-TO-REDIRECT-TO-AFTERWARDS>'),
-    'access arguments' => array('<YOUR-PERMISSION-KEY>'),
-    'type' => MENU_CALLBACK,
-  );
-
-  return $items;
-}
-
-function mymodule_admin_landing_page() {
-
-  // Get the View Embed Code
-  // This function will return FALSE if your view is not enabled
-  $view_code = views_embed_view('<VIEW-MACHINE-NAME>', '<DISPLAY-MACHINE-NAME');
-  // If your view is enabled then embed it in this page by returning the embed code
-  if (isset($view_code)) {
-    $output .= $view_code;
-  }
-  else {
-    // Provide the landing page with links to the menu item created in hook_menu to
-    // to enable your view
-    $output .= '<p>The My Module module uses primarily views to provide an '
-      . 'administrative interface. Currently one or more views needed for this '
-      . 'administrative interface are disabled. <strong>Click each of the following links to '
-      . 'enable the pertinent views</strong>:</p>';
-    $output .= '<ul>';
-    // NOTE: <URL-FROM-MENU-TO-ENABLE-VIEW> is
-    // admin/tripal/<PATH-TO-YOUR-ADMIN-SECTION>/views/<VIEW-MACHINE-NAME>/enable
-    // from above hook_menu().
-    $output .= '<li>' . l('<VIEW-HUMAN-RADABLE-NAME>', '<URL-FROM-MENU-TO-ENABLE-VIEW>') . '</li>';
-    $output .= '</ul>';
-  }
-
-  return $output;
-}
-
- * @endcode
- *
- * Adding your own Chado Views Integrations:
- *
- * One of the main ways the Tripal View API is likely to be used is if your module needs
- * to create it's own tripal views integration. You might need to do this for any number of
- * reasons but the following examples are thought to be the most common:
- *
- *   1) Your module wants to add handlers with better functionality to fields it has more
- *       knowledge of than the general integration for all tables
- * @code
-   mymodule_views_data() {
-
-    // First check that your integration has not already been added
-    // When selecting a priority, do not choose -10 or -9 and make sure it is below 0
-    // so that individual sites still have the ability to override it, if needed
-    $table_name = 'my_chado_table';
-    $priority = -5;
-    if (!tripal_is_table_integrated($table_name, $priority)) {
-
-      // If you really only need to tweak an existing integration you can clone it
-      // If you want to clone the integration that is currently taking priority
-      // then use tripal_get_lightest_views_integration_priority() as below
-      $lightest_priority = tripal_get_lightest_views_integration_priority($table_name);
-      $setup_id = tripal_clone_views_integration($table_name, $priority, $lightest_priority);
-
-      // And then make a few changes
-      // First get the definition array created via the clone above
-      $defn_array = tripal_export_views_integration($setup_id);
-
-      // Then make some changes to the array here
-
-      // And finally save the changes to the integration
-      tripal_update_views_integration($setup_id, $defn_array);
-
-    }
-
-  }
- * @endcode
- *   2) Your module creates a chado table that is not already integrated.
- * @code
-  mymodule_views_data() {
-
-    // First check that your integration has not already been added
-    // When selecting a priority, do not choose 10 or 9 and make sure it is below 0
-    // so that individual sites still have the ability to override it, if needed
-    $table_name = 'my_chado_table';
-    $priority = 5;
-    if (!tripal_is_table_integrated($table_name, $priority)) {
-
-      // Describe your table using a large array as specified by tripal_add_views_integration().
-      $defn_array = array(
-        'table' => $table_name, //tablename or materialized view name
-        'name' => 'My Chado Table', // Human readable name
-        'type' => 'chado', //either chado or mview depending on tablename
-        'description' => 'Create a listing from my chado table.', //description seen when creating a view of this type
-        'priority' => $priority, //For Base tripal modules: 10; custom modules: 9 to 0;
-        'base_table' => TRUE //either TRUE or FALSE depending on whether the current table should show up in the add view list
-        'fields' => array(
-          'feature_id' => array(
-            'name' => 'feature_id', //field name in database
-            'title' => 'Feature ID', //human-readable name -seen in Views UI
-            'description' => 'This is the unique identifier for features', //help/description seen in Views UI
-            'type' => 'int', // the type of field
-            'handlers' => array(  //possible keys are field, filter, sort, argument, relationship
-              'field' => array(
-                'name' => 'chado_views_handler_numeric' //name of handler
-              ),
-              'filter' => array( ... ),
-              ...
-            ),
-            // Describe any joins involving this field.
-            // Note: you can include both foreign keys (feature.type_id => cvterm.cvterm_id)
-            // and referring tables (ie: feature.feature_id <= feature_relationship.subject_id)
-            'joins' => array(
-              'feature_relationship' => array( //table to join to.
-                'subject_id' => array( //field in above table (feature_relationship)
-                  'table' => 'featureprop', //table to join to
-                  'field' => 'feature_id', //field in above table (feature_relationship)
-                  'handler' => 'views_join', //handler to use for joining
-                  'relationship_handler' => 'views_handler_relationship', //handler to use when a relationship is added.
-                  'relationship_only' => FALSE, //whether to join automatically (FALSE) or not (TRUE)
-                ),
-                ...
-              ),
-              ...
-            ),
-          )
-        ),
-      );
-      // Actually create the entry
-      tripal_add_views_integration($defn_array);
-
-    }
-
-  }
- * @endcode
- * @}
- */
-
 /**
  * Programatically enable view
  *
@@ -839,10 +664,11 @@ function tripal_add_views_integration($defn_array, $setup_id = FALSE) {
 /**
  * Export Views integration records.
  *
- * This is a great way to create your own integration since it returns an already defined
- * integration in array form that you can modify. After modifications simply set the
- * priority to something lighter (but still below 0) than any existing integrations
- * and use tripal_add_views_integration() to add it to the list of integrations.
+ * This is a great way to create your own integration since it returns an
+ * already defined integration in array form that you can modify. After
+ * modifications simply set the priority to something lighter (but still
+ * below 0) than any existing integrations and use
+ * tripal_add_views_integration() to add it to the list of integrations.
  *
  * @param $setup_id
  *   The unique setup id of the tripal views integration

BIN
tripal_ws/.DS_Store


+ 118 - 0
tripal_ws/includes/TripalFields/WebServicesField.inc

@@ -0,0 +1,118 @@
+<?php
+/**
+ * @class
+ * Purpose:
+ *
+ * Data:
+ * Assumptions:
+ */
+class WebServicesField extends TripalField {
+
+  // --------------------------------------------------------------------------
+  //                     EDITABLE STATIC CONSTANTS
+  //
+  // The following constants SHOULD be set for each descendant class.  They are
+  // used by the static functions to provide information to Drupal about
+  // the field and it's default widget and formatter.
+  // --------------------------------------------------------------------------
+
+  // The default label for this field.
+  public static $default_label = 'Remote Data';
+
+  // The default description for this field.
+  public static $default_description = '';
+/*
+  // The default widget for this field.
+  public static $default_widget = '';
+
+  // The default formatter for this field.
+  public static $default_formatter = '';
+*/
+  // The module that manages this field.
+  public static $module = 'tripal_ws';
+
+  // A list of global settings. These can be accessed within the
+  // globalSettingsForm.  When the globalSettingsForm is submitted then
+  // Drupal will automatically change these settings for all fields.
+  // Once instances exist for a field type then these settings cannot be
+  // changed.
+  public static $default_settings = array(
+    '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
+    // be a controlled vocabulary term (e.g. rdfs:type). Keys in the load()
+    // function that are supported by the query() function should be
+    // 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.
+  // It is recommended to put settings at the instance level whenever possible.
+  // If you override this variable in a child class be sure to replicate the
+  // term_name, term_vocab, term_accession and term_fixed keys as these are
+  // required for all TripalFields.
+  public static $default_instance_settings = array(
+    // The short name for the vocabulary (e.g. schema, SO, GO, PATO, etc.).
+    'term_vocabulary' => 'schema',
+    // The name of the term.
+    'term_name' => 'Thing',
+    // The unique ID (i.e. accession) of the term.
+    'term_accession' => 'Thing',
+    // 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,
+    // Indicates if this field should be automatically attached to display
+    // or web services or if this field should be loaded separately. This
+    // is convenient for speed.  Fields that are slow should for loading
+    // should have auto_attach set to FALSE so tha their values can be
+    // attached asynchronously.
+    'auto_attach' => FALSE,
+  );
+
+  // A boolean specifying that users should not be allowed to create
+  // fields and instances of this field type through the UI. Such
+  // fields can only be created programmatically with field_create_field()
+  // and field_create_instance().
+ // public static $no_ui = FALSE;
+
+  // A boolean specifying that the field will not contain any data. This
+  // should exclude the field from web services or downloads.  An example
+  // could be a quick search field that appears on the page that redirects
+  // the user but otherwise provides no data.
+  //public static $no_data = FALSE;
+
+
+ /**
+   * Loads the field values from the underlying data store.
+   *
+   * @param $entity
+   *
+   * @return
+   *   An array of the following format:
+   *     $entity->{$field_name}['und'][0]['value'] = $value;
+   *   where:
+   *     - $entity is the entity object to which this field is attached.
+   *     - $field_name is the name of this field
+   *     - 'und' is the language code (in this case 'und' == undefined)
+   *     - 0 is the cardinality.  Increment by 1 when more than one item is
+   *       available.
+   *     - 'value' is the key indicating the value of this field. It should
+   *       always be set.  The value of the 'value' key will be the contents
+   *       used for web services and for downloadable content.  The value
+   *       should be of the follow format types: 1) A single value (text,
+   *       numeric, etc.) 2) An array of key value pair. 3) If multiple entries
+   *       then cardinality should incremented and format types 1 and 2 should
+   *       be used for each item.
+   *   The array may contain as many other keys at the same level as 'value'
+   *   but those keys are for internal field use and are not considered the
+   *   value of the field.
+   *
+   *
+   */
+  public function load($entity) {
+  }
+
+}

+ 118 - 0
tripal_ws/includes/TripalFields/WebServicesFieldFormatter.inc

@@ -0,0 +1,118 @@
+<?php
+/**
+ * @class
+ * Purpose:
+ *
+ * Display:
+ * Configuration:
+ */
+class WebServicesFieldFormatter extends TripalFieldFormatter {
+/*
+  // 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();
+
+  // The list of default settings for this formatter.
+  public static $default_settings = array(
+    'key' => 'default_value',
+  );
+
+  /**
+   * Provides the field's setting form.
+   *
+   * This function corresponds to the hook_field_formatter_settings_form()
+   * function of the Drupal Field API.
+   *
+   * The settings form appears on the 'Manage Display' page of the content
+   * type administration page. This function provides the form that will
+   * appear on that page.
+   *
+   * To add a validate function, please create a static function in the
+   * implementing class, and indicate that this function should be used
+   * in the form array that is returned by this function.
+   *
+   * This form will not be displayed if the formatter_settings_summary()
+   * function does not return anything.
+   *
+   * param $field
+   *   The field structure being configured.
+   * param $instance
+   *   The instance structure being configured.
+   * param $view_mode
+   *   The view mode being configured.
+   * param $form
+   *   The (entire) configuration form array, which will usually have no use
+   *   here.  Typically for reference only.
+   * param $form_state
+   *   The form state of the (entire) configuration form.
+   *
+   * @return
+   *   A Drupal Form array containing the settings form for this field.
+
+  public function settingsForm($view_mode, $form, &$form_state) {
+  }
+
+  /**
+   *  Provides the display for a field
+   *
+   * This function corresponds to the hook_field_formatter_view()
+   * function of the Drupal Field API.
+   *
+   *  This function provides the display for a field when it is viewed on
+   *  the web page.  The content returned by the formatter should only include
+   *  what is present in the $items[$delta]['values] array. This way, the
+   *  contents that are displayed on the page, via webservices and downloaded
+   *  into a CSV file will always be identical.  The view need not show all
+   *  of the data in the 'values' array.
+   *
+   *  @param $element
+   *  @param $entity_type
+   *  @param $entity
+   *  @param $langcode
+   *  @param $items
+   *  @param $display
+   *
+   *  @return
+   *    An element array compatible with that returned by the
+   *    hook_field_formatter_view() function.
+
+  public function view(&$element, $entity_type, $entity, $langcode, $items, $display) {
+
+    // Get the settings
+    $settings = $display['settings'];
+
+  }
+
+  /**
+   * Provides a summary of the formatter settings.
+   *
+   * This function corresponds to the hook_field_formatter_settings_summary()
+   * function of the Drupal Field API.
+   *
+   * On the 'Manage Display' page of the content type administration page,
+   * fields are allowed to provide a settings form.  This settings form can
+   * be used to allow the site admin to define how the field should be
+   * formatted.  The settings are then available for the formatter()
+   * function of this class.  This function provides a text-based description
+   * of the settings for the site developer to see.  It appears on the manage
+   * display page inline with the field.  A field must always return a
+   * value in this function if the settings form gear button is to appear.
+   *
+   * See the hook_field_formatter_settings_summary() function for more
+   * information.
+   *
+   * @param $field
+   * @param $instance
+   * @param $view_mode
+   *
+   * @return string
+   *   A string that provides a very brief summary of the field settings
+   *   to the user.
+   *
+
+  public function settingsSummary($view_mode) {
+  }
+*/
+}

+ 5 - 0
tripal_ws/includes/TripalFields/WebServicesFieldWidget.inc

@@ -0,0 +1,5 @@
+<?php
+
+class WebServicesFieldWidget extends TripalFieldWidget {
+
+}

+ 391 - 0
tripal_ws/includes/TripalFields/remote__data/remote__data.inc

@@ -0,0 +1,391 @@
+<?php
+/**
+ * @class
+ * Purpose:
+ *
+ * Data:
+ * Assumptions:
+ */
+class remote__data extends WebServicesField {
+
+  // --------------------------------------------------------------------------
+  //                     EDITABLE STATIC CONSTANTS
+  //
+  // The following constants SHOULD be set for each descendant class.  They are
+  // used by the static functions to provide information to Drupal about
+  // the field and it's default widget and formatter.
+  // --------------------------------------------------------------------------
+  // The default label for this field.
+  public static $default_label = 'Remote Tripal Site';
+
+  // The default description for this field.
+  public static $default_description = 'Allows for inclusion of remote data from another Tripal site.';
+
+  // The default widget for this field.
+  public static $default_widget = 'remote__data_widget';
+
+  // The default formatter for this field.
+  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
+  // globalSettingsForm.  When the globalSettingsForm is submitted then
+  // Drupal will automatically change these settings for all fields.
+  // Once instances exist for a field type then these settings cannot be
+  // changed.
+  public static $default_settings = array(
+    '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
+    // be a controlled vocabulary term (e.g. rdfs:type). Keys in the load()
+    // function that are supported by the query() function should be
+    // 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.
+  // It is recommended to put settings at the instance level whenever possible.
+  // If you override this variable in a child class be sure to replicate the
+  // term_name, term_vocab, term_accession and term_fixed keys as these are
+  // required for all TripalFields.
+  public static $default_instance_settings = array(
+    // The short name for the vocabulary (e.g. schema, SO, GO, PATO, etc.).
+    'term_vocabulary' => 'schema',
+    // The name of the term.
+    'term_name' => 'Thing',
+    // The unique ID (i.e. accession) of the term.
+    '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,
+    // Indicates if this field should be automatically attached to display
+    // or web services or if this field should be loaded separately. This
+    // is convenient for speed.  Fields that are slow should for loading
+    // 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' => '',
+      'rd_field_name' => '',
+      'site_logo' => '',
+    ),
+  );
+
+  // A boolean specifying that users should not be allowed to create
+  // fields and instances of this field type through the UI. Such
+  // fields can only be created programmatically with field_create_field()
+  // and field_create_instance().
+  public static $no_ui = FALSE;
+
+  // A boolean specifying that the field will not contain any data. This
+  // should exclude the field from web services or downloads.  An example
+  // could be a quick search field that appears on the page that redirects
+  // the user but otherwise provides no data.
+  public static $no_data = FALSE;
+
+  // Holds an object describing the remote site that tihs field connects to.
+  private $remote_site = NULL;
+
+  // Set to TRUE if this field is being loaded via web services. WE don't
+  // want remote fields loaded when a web-service call is made.
+  private $loaded_via_ws = FALSE;
+
+  public function __construct($field, $instance) {
+    parent::__construct($field, $instance);
+
+    // This field should not do anything if it is loaded via web-services.
+    // We don't want remote content to be available in web services.  There
+    // is an if statement to not show this field in the web services but the
+    // entity_load function doesn't know this field shouldn't be loaded so
+    // we need to short-circuit that.
+    $_SERVER['REQUEST_URI'];
+    if (preg_match('/^web-services/', $_SERVER['REQUEST_URI'])) {
+      $this->loaded_via_ws = TRUE;
+      return;
+    }
+
+    // Get the site url from the tripal_sites table.
+    if (array_key_exists('data_info', $instance['settings'])) {
+      $site_id_ws = $instance['settings']['data_info']['remote_site'];
+      if ($site_id_ws) {
+        $this->remote_site = db_select('tripal_sites', 'ts')
+          ->fields('ts')
+          ->condition('ts.id', $site_id_ws)
+          ->execute()
+          ->fetchObject();
+      }
+    }
+  }
+  /**
+   * @see WebServicesField::load()
+   */
+  public function load($entity) {
+
+    $field_name = $this->field['field_name'];
+    $field_type = $this->field['type'];
+
+    // Set some defaults for the empty record.
+    $entity->{$field_name}['und'][0] = array(
+      'value' => array(),
+      'remote_entity' => array(),
+    );
+
+    // If this field is being loaded via web services then just return.
+    if ($this->loaded_via_ws == TRUE) {
+      return;
+    }
+
+    // Get the query set by the admin for this field and replace any tokesn
+    $query_str = $this->instance['settings']['data_info']['query'];
+    $bundle = tripal_load_bundle_entity(array('name' => $entity->bundle));
+    $query_str = tripal_replace_entity_tokens($query_str, $entity, $bundle);
+
+    // Make the request.
+    $data = $this->makeRemoteRequest($query_str);
+    if(!$data){
+      return;
+    }
+
+    $total_items = $data['totalItems'];
+
+    // Iterate through the members returned and save those for the field.
+    for ($i = 0; $i < count($data['members']); $i++) {
+      $member = $data['members'][$i];
+
+      // Get the cotent type and remote entity id
+      $content_type = $member['@type'];
+      $remote_entity_id = $member['@id'];
+      $remote_entity_id = preg_replace('/^.*\/(\d+)/', '$1', $remote_entity_id);
+
+      // Save the member information for use later.
+      $entity->{$field_name}['und'][$i]['remote_entity'] = $member;
+
+      // Separate the query_field if it has subfields.
+      $rd_field_name = $this->instance['settings']['data_info']['rd_field_name'];
+      $subfields = explode(',', $rd_field_name);
+      $query_field = $subfields[0];
+
+      // Next get the the details about this member.
+      $query_field_url =  $content_type . '/' . $remote_entity_id . '/' . $query_field;
+      $field_data = $this->makeRemoteRequest($query_field_url);
+      if(!$field_data){
+        // If we encounter any type of error, we'll reset the field and return.
+        $entity->{$field_name}['und'] = array();
+        $entity->{$field_name}['und'][0] = array(
+          'value' => array(),
+          'remote_entity' => array(),
+        );
+        return;
+      }
+
+      // Set the field data as the value.
+      $field_data_type = $field_data['@type'];
+      $entity->{$field_name}['und'][$i]['value'] = $field_data;
+    }
+   }
+   /**
+    * Makes a request to a remote Tripal web services site.
+    *
+    * @param $query
+    *   The query string. This string is added to the URL for the remote
+    *   website.
+    */
+   private function makeRemoteRequest($query) {
+     $ctype = $query;
+     $qdata = '';
+     if (preg_match('/\?/', $query)) {
+       list($ctype, $qdata) = explode('?', $query);
+     }
+
+     // Build the URL to the remote web services.
+     $ws_version = $this->remote_site->version;
+     $ws_url = $this->remote_site->url;
+     $ws_url = trim($ws_url, '/');
+     $ws_url .= '/web-services/content/' . $ws_version . '/' . $ctype;
+
+     // Build the Query and make and substitions needed.
+     //dpm($ws_url . '?' . $query);
+     $options = array(
+       'data' => $qdata,
+     );
+     $data = drupal_http_request($ws_url, $options);
+
+     if (!$data) {
+       tripal_report_error('tripal_ws', TRIPAL_ERROR,
+           t('Could not connect to the remote web service.'));
+       return FALSE;
+     }
+
+     // If the data object has an error then this is some sort of
+     // connection error (not a Tripal web servcies error).
+     if (property_exists($data, 'error')) {
+       tripal_report_error('tripal_ws', TRIPAL_ERROR,
+           'Web Services error on remote site: %error.',
+           array('%error' => $data->error));
+       return FALSE;
+     }
+
+     // We got a response, so convert it to a PHP array.
+     $data = drupal_json_decode($data->data);
+
+     // Check if there was a Tripal Web Services error.
+     if (array_key_exists('error', $data)) {
+       $error = '</pre>' . print_r($data['error'], TRUE) . '</pre>';
+       tripal_report_error('tripal_ws', TRIPAL_ERROR,
+           'Web Services error on remote site: %error.',
+           array('%error' => $error));
+       return FALSE;
+     }
+
+     return $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 = tripal_load_bundle_entity(array('name' => $instance['bundle']));
+    // Retrieve all available tokens.
+    $tokens = tripal_get_entity_tokens($bundle);
+
+    $element['data_info'] = array(
+      '#type' => 'fieldset',
+      '#title' => 'Remote Data Settings',
+      '#description' => 'These settings allow you to provide a Tripal web
+        services query to identify content on another Tripal site and display
+        that here within this field.  You must specify the query to execute and
+        the field to display.',
+      '#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('Remote Tripal Site'),
+      '#options' => $rows,
+      '#default_value' => $this->instance['settings']['data_info']['remote_site'],
+    );
+
+    $element['data_info']['query'] = array(
+      '#type' => 'textarea',
+      '#title' => 'Query to Execute',
+      '#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,
+      '#required' => TRUE
+    );
+    $element['data_info']['rd_field_name'] = array(
+      '#type' => 'textfield',
+      '#title' => 'Field to Display',
+      '#description' => 'The results returned by the query should match
+        entities (or records) from the selected remote site.  That entity
+        will have multiple fields. Only one remote field can be shown by
+        this field. Please enter the name of the field you would like
+        to display.  Some fields have "subfields".  You can display a subfield
+        rather than the entire field by entering a comma-separated sequence
+        of subfields.  For example, for relationships, you may only want to
+        show the "clause", therefore, the entry here would be: realtionship,clause.',
+      '#default_value' => $this->instance['settings']['data_info']['rd_field_name'],
+      '#required' => TRUE
+    );
+    $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
+    );
+
+    $fid = $this->instance['settings']['data_info']['site_logo'] ? $this->instance['settings']['data_info']['site_logo'] : NULL;
+    $file = NULL;
+    if ($fid) {
+      $file = file_load($fid);
+    }
+    $element['data_info']['site_logo'] = array(
+      '#title' => 'Remote Site Logo',
+      '#description' => t('When data is taken from a remote site and shown to the user,
+         the site from which the data was retrieved is indicated.  If you would like to
+         include the logo for the remote site, please upload an image here.'),
+      '#type' => 'managed_file',
+      '#default_value' => $file ? $file->fid : NULL,
+      '#theme' => 'image_widget',
+      '#attached' => array(
+        'css' => array(
+          'image-preview' => drupal_get_path('module', 'image') . '/image.css',
+        ),
+      ),
+      'preview' => array(
+        '#markup' => theme('image_style', array('style_name' => 'thumbnail', 'path' => $file ? $file->uri : '')),
+      ),
+    );
+
+    return $element;
+  }
+
+ /**
+   *
+   * @param unknown $form
+   * @param unknown $form_state
+   */
+  public function instanceSettingsFormValidate($form, &$form_state) {
+    $site_logo = $form_state['values']['instance']['settings']['data_info']['site_logo'];
+
+    // If we have a site logo then add usage information.
+    if ($site_logo) {
+      $file = file_load($site_logo);
+      $file_usage  = file_usage_list($file);
+      if (!array_key_exists('tripal_ws', $file_usage)) {
+        file_usage_add($file, 'tripal_ws', 'site-logo', 1);
+      }
+    }
+  }
+
+
+}

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

@@ -0,0 +1,368 @@
+<?php
+
+class remote__data_formatter extends WebServicesFieldFormatter {
+
+  // The default label for this field.
+  public static $default_label = 'Remote Data';
+  // The list of field types for which this formatter is appropriate.
+  public static $field_types = array('remote__data');
+  // The list of default settings for this formatter.
+  public static $default_settings = array(
+    'setting1' => 'default_value',
+  );
+  /**
+   * Provides the field's setting form.
+   *
+   * This function corresponds to the hook_field_formatter_settings_form()
+   * function of the Drupal Field API.
+   *
+   * The settings form appears on the 'Manage Display' page of the content
+   * type administration page. This function provides the form that will
+   * appear on that page.
+   *
+   * To add a validate function, please create a static function in the
+   * implementing class, and indicate that this function should be used
+   * in the form array that is returned by this function.
+   *
+   * This form will not be displayed if the formatter_settings_summary()
+   * function does not return anything.
+   *
+   * param $field
+   *   The field structure being configured.
+   * param $instance
+   *   The instance structure being configured.
+   * param $view_mode
+   *   The view mode being configured.
+   * param $form
+   *   The (entire) configuration form array, which will usually have no use
+   *   here.  Typically for reference only.
+   * param $form_state
+   *   The form state of the (entire) configuration form.
+   *
+   * @return
+   *   A Drupal Form array containing the settings form for this field.
+   */
+  public function settingsForm($view_mode, $form, &$form_state) {
+  }
+  /**
+   *  Provides the display for a field
+   *
+   * This function corresponds to the hook_field_formatter_view()
+   * function of the Drupal Field API.
+   *
+   *  This function provides the display for a field when it is viewed on
+   *  the web page.  The content returned by the formatter should only include
+   *  what is present in the $items[$delta]['values] array. This way, the
+   *  contents that are displayed on the page, via webservices and downloaded
+   *  into a CSV file will always be identical.  The view need not show all
+   *  of the data in the 'values' array.
+   *
+   *  @param $element
+   *  @param $entity_type
+   *  @param $entity
+   *  @param $langcode
+   *  @param $items
+   *  @param $display
+   *
+   *  @return
+   *    An element array compatible with that returned by the
+   *    hook_field_formatter_view() function.
+   */
+  public function view(&$element, $entity_type, $entity, $langcode, $items, $display) {
+    // Get the settings
+    $settings = $display['settings'];
+    $field_name = $this->field['field_name'];
+
+    // Get any subfields and the header label.  Shift the array because the
+    // results should already be the value of the fisrt entry.
+    $rd_field_name = $this->instance['settings']['data_info']['rd_field_name'];
+    $subfields = explode(',', $rd_field_name);
+    $header_label = $this->getHeaderLabel($subfields);
+    $flabel = array_shift($subfields);
+
+    // Get the site logo if one is provided
+    $site_logo = $this->instance['settings']['data_info']['site_logo'];
+    if ($site_logo) {
+      $site_logo = file_load($site_logo);
+    }
+
+    // Get the site name where the data came from.
+    $site_id_ws = $this->instance['settings']['data_info']['remote_site'];
+    $site = db_select('tripal_sites', 'ts')
+      ->fields('ts', array('name', 'url'))
+      ->condition('ts.id', $site_id_ws)
+      ->execute()
+      ->fetchObject();
+
+      $content = '<p>';
+    if (is_object($site_logo)) {
+      $content .= '<img class="tripal-remote--data-field-logo" src="' . file_create_url($site_logo->uri) . '"><br/>';
+    }
+    $content .=  t('This content provided by !site.',
+        array('!site' => l($site->name, $site->url, array('attributes' => array("target" => '_blank')))));
+    $content .= '</p>';
+    $rows = array();
+    foreach ($items as $index => $item) {
+      $remote_entity_label = $item['remote_entity']['label'];
+      $remote_entity_page = $item['remote_entity']['ItemPage'];
+      $link = t('View !data on %site',
+          array('!data' => l('this content', $remote_entity_page, array('attributes' => array('target' => '_blank'))),
+            '%site' => $site->name));
+      $value = $item['value'];
+      if (!$value) {
+        continue;
+      }
+      $headers = array('');
+
+      // If this is a collection then handle it as a list of members.
+      if (array_key_exists('members', $value)) {
+        foreach ($value['members'] as $subvalue) {
+          $subvalue = $this->refineSubValue($subvalue, $subfields, $header_label);
+          $rows[] = array($subvalue);
+        }
+      }
+      else {
+        if (count($subfields) > 0) {
+          $subvalue = $this->refineSubValue($value, $subfields, $header_label);
+          $rows[] = array($subvalue);
+        }
+        else {
+          if (array_key_exists($flabel, $value)) {
+            $rows[] = array(l($value[$flabel], $remote_entity_page, array('attributes' => array('target' => '_blank'))));
+          }
+          else {
+            $value['Link'] = l('View content on ' . $site->name, $remote_entity_page, array('attributes' => array('target' => '_blank')));
+            $rows[] = array($value);
+          }
+        }
+      }
+
+    }
+
+    $has_sub_tables = FALSE;
+    for ($i = 0; $i < count($rows); $i++) {
+      if (is_array($rows[$i][0])) {
+        $rows[$i][0] = $this->createTable($rows[$i]);
+        $has_sub_tables = TRUE;
+      }
+    }
+
+    // If we don't have  tables for each row then we'll put everything into
+    // a table.
+    if (!$has_sub_tables) {
+      $headers = array($header_label . '(s)');
+      $content .= theme_table(array(
+        'header' => $headers,
+        'rows' => $rows,
+        'attributes' => array(
+          'class' => 'tripal-remote--data-field-table',
+        ),
+        'sticky' => FALSE,
+        'caption' => "",
+        'colgroups' => array(),
+        'empty' => 'There are no results.',
+      ));
+    }
+    else {
+      for ($i = 0; $i < count($rows); $i++) {
+        if (count($rows) > 1) {
+          $content .= '<span class="tripal-remote--data-field-table-label">' . $header_label . ' ' . ($i + 1) . '</span>';
+        }
+        $content .= $rows[$i][0];
+      }
+    }
+    // Return the content for this field.
+    $element[0] = array(
+      '#type' => 'markup',
+      '#markup' => '<div class="tripal-remote--data-field">' . $content . '</div>',
+    );
+  }
+
+
+  /**
+   * Retrieves the header label given the subfields criteria.
+   *
+   * @param $subfields
+   *   An array of the sequence of subfields.
+   */
+  private function getHeaderLabel($subfields) {
+     $subfield = array_shift($subfields);
+     $header_label = ucwords(preg_replace('/_/', ' ', $subfield));
+     if (count($subfields) > 0) {
+       $header_label .= ' ' . $this->getHeaderLabel($subfields);
+     }
+     return $header_label;
+  }
+  /**
+   * Adjusts the items array to contain only the section/subsection desired.
+   *
+   * The field settings can indicate a field with sub fields that should
+   * be displayed (e.g. organism,genus or relationship,clause).  We want
+   * to adjust the item to only include what the user requested.
+   *
+   * @param $values
+   * @param $subfields
+   */
+  private function refineSubValue($values, $subfields) {
+
+    // Remove unwanted elements.
+    unset($values['@id']);
+    unset($values['@context']);
+    unset($values['@type']);
+    unset($values['remote_entity']);
+
+    $subfield = array_shift($subfields);
+    if (array_key_exists($subfield, $values)) {
+      if (is_array($values[$subfield]) and count($subfields) > 0) {
+        return  $this->refineSubvalue($values[$subfield], $subfields);
+      }
+      else {
+        return $values[$subfield];
+      }
+    }
+    else {
+      return $values;
+    }
+  }
+  /**
+   * A recursive function for displaying an item in a table.
+   *
+   * @param $item
+   *   An item from the $items array passed to the view() function.
+   * @return
+   *   An HTML formatted Table.
+   */
+  private function createTable($item, &$pkey = '', &$rows = array(), $depth = 0) {
+    foreach ($item as $key => $value) {
+      // Skip JSON-LD keys.
+      if (preg_match('/^\@/', $key)) {
+        continue;
+      }
+      $key = preg_replace('/_/', ' ', $key);
+      $key = ucwords($key);
+      if ($pkey) {
+        $key = $pkey . ' ' . $key;
+      }
+      if (is_array($value)) {
+        $this->createTable($value, $key, $rows, $depth + 1);
+      }
+      else {
+        $rows[] = array(
+          'data'=> array(
+            $key,
+            $value
+          ),
+          'no_striping' => TRUE,
+        );
+      }
+    }
+    if ($depth == 0) {
+      $headers = array('Data Type', 'Value');
+      return theme_table(array(
+        'header' => $headers,
+        'rows' => $rows,
+        'attributes' => array(
+          'class' => 'tripal-remote--data-field-table',
+        ),
+        'sticky' => FALSE,
+        'caption' => "",
+        'colgroups' => array(),
+        'empty' => 'There are no results.',
+      ));
+    }
+  }
+
+  /**
+   * A recursive function for creating an HTML dictionary list from
+   * the results for the item provided.
+   *
+   * @param $item
+   *   An item from the $items array passed to the view() function.
+   * @return
+   *   An HTML formatted DL.
+   */
+  private function createDL($item, &$pkey = '', &$content= '', $depth = 0) {
+    if ($depth == 0) {
+      $content = '<dl class="tripal-remote--data-field-dl">';
+    }
+    foreach ($item as $key => $value) {
+      // Skip JSON-LD keys.
+      if (preg_match('/^\@/', $key)) {
+        continue;
+      }
+
+      $key = preg_replace('/_/', ' ', $key);
+      $key = ucwords($key);
+      if ($pkey) {
+        $key = $pkey . ' ' . $key;
+      }
+      if (is_array($value)) {
+        $this->createDL($value, $key, $content, $depth + 1);
+      }
+      else {
+        $content .= '<dt>' . $key . '&nbsp;:&nbsp;</dt><dd>' . $value . '</dd>';
+      }
+    }
+    if ($depth == 0) {
+      $content .= '</dl>';
+      return $content;
+    }
+  }
+
+  /**
+   * A recursive function for creating an HTML dictionary list from
+   * the results for the item provided.
+   *
+   * @param $item
+   *   An item from the $items array passed to the view() function.
+   * @return
+   *   An HTML formatted DL.
+   */
+  private function createNestedDL($item) {
+    $content = '<dl>';
+    foreach ($item as $key => $value) {
+      // Skip JSON-LD keys.
+      if (preg_match('/^\@/', $key)) {
+        continue;
+      }
+
+      $key = preg_replace('/_/', ' ', $key);
+      $key = ucwords($key);
+      if (is_array($value)) {
+        $value = $this->createDL($value);
+      }
+      $content .= '<dt>' . $key . '</dt><dd>' . $value . '</dd>';
+    }
+    $content .= '</dl>';
+    return $content;
+  }
+  /**
+   * Provides a summary of the formatter settings.
+   *
+   * This function corresponds to the hook_field_formatter_settings_summary()
+   * function of the Drupal Field API.
+   *
+   * On the 'Manage Display' page of the content type administration page,
+   * fields are allowed to provide a settings form.  This settings form can
+   * be used to allow the site admin to define how the field should be
+   * formatted.  The settings are then available for the formatter()
+   * function of this class.  This function provides a text-based description
+   * of the settings for the site developer to see.  It appears on the manage
+   * display page inline with the field.  A field must always return a
+   * value in this function if the settings form gear button is to appear.
+   *
+   * See the hook_field_formatter_settings_summary() function for more
+   * information.
+   *
+   * @param $field
+   * @param $instance
+   * @param $view_mode
+   *
+   * @return string
+   *   A string that provides a very brief summary of the field settings
+   *   to the user.
+   *
+   */
+  public function settingsSummary($view_mode) {
+  }
+}

+ 169 - 0
tripal_ws/includes/TripalFields/remote__data/remote__data_widget.inc

@@ -0,0 +1,169 @@
+<?php
+
+class remote__data_widget extends WebServicesFieldWidget {
+
+  // The default label for this field.
+  public static $default_label = 'Remote Data';
+  // The list of field types for which this formatter is appropriate.
+  public static $field_types = array('remote__data');
+  /**
+   * 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);
+    // 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.
+   *
+   * 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) {
+    //TODO validate the tokens, site, and query. Test that query returns data.
+
+  }
+}

+ 142 - 0
tripal_ws/includes/TripalWebService.inc

@@ -249,6 +249,7 @@ class TripalWebService {
     $this->resource = new TripalWebServiceResource($this->base_path);
     $this->resource->setID('error');
     $this->resource->addContextItem('error', 'rdfs:error');
+    $this->resource->setType('error');
     $this->resource->addProperty('error', $message);
   }
 
@@ -266,4 +267,145 @@ class TripalWebService {
      $supported_classes = array();
      return $supported_classes;
   }
+
+  /**
+   * Adds a term to the '@context' section for a given resource.
+   *
+   * @param $resource
+   *   A TripalWebServiceResource instance.
+   * @param $term
+   *   The term array.
+   * @param $santize
+   *   An array of keywords indicating how to santize the key.  By default,
+   *   no sanitizing occurs.  The two valid types are 'lowercase', and 'spacing'
+   *   where 'lowercase' converts the term name to all lowercase and
+   *   'spacing' replaces any spaces with underscores.
+   * @return $key
+   *   The key (the term name but with spaces replaced with underscores).
+   */
+  public function addContextTerm($resource, $term, $sanitize = array()) {
+    if (!is_a($resource, 'TripalWebServiceResource')) {
+      throw new Exception('addContextTerm: Please provide a $resource of type TripalWebServiceResource.');
+    }
+
+    if (!$term) {
+      $backtrace = debug_backtrace();
+      throw new Exception('addContextTerm: Please provide a non NUll or non empty $term.');
+
+    }
+    if (!$term['name']) {
+      throw new Exception('addContextTerm: The provided term does not have a name: ' . print_r($term, TRUE));
+    }
+
+    // Make sure the vocab is present
+    $vocab = $term['vocabulary'];
+    $this->addContextVocab($resource, $vocab);
+
+    // Sanitize the term key
+    $key = $term['name'];
+    $key_adj = $key;
+    if (in_array('spacing', $sanitize)) {
+      $key_adj = preg_replace('/ /', '_', $key_adj);
+    }
+    if (in_array('lowercase', $sanitize)) {
+      $key_adj = strtolower($key_adj);
+    }
+
+    // Create the compact IRI
+    $compact_iri = $vocab['short_name'] . ':' . $term['accession'];
+
+    // If the full naming is indicated in the service parameters then
+    // set the full name of the keys to include the vocab short name.
+    if (array_key_exists('full_keys', $this->params)) {
+      //$key_adj = $vocab['short_name'] . ':' . $key_adj;
+    }
+
+    $iri = $term['url'];
+
+    $resource->addContextItem($key_adj, $compact_iri);
+    $resource->addContextItem($compact_iri, $iri);
+
+    if ($key_adj == 'clause subject') {
+      //print_r(debug_backtrace()[2]['function']);
+    }
+    return $key_adj;
+  }
+
+  /**
+   * Adds a vocabulary to the '@context' section for a given resource.
+   *
+   * @param $resource
+   *   A TripalWebServiceResource instance.
+   * @param $vocab
+   *   The vocabulary array.
+   */
+  public function addContextVocab($resource, $vocab) {
+
+    if (!is_a($resource, 'TripalWebServiceResource')) {
+      throw new Exception('addContextVocab: Please provide a $resource of type TripalWebServiceResource.');
+    }
+
+    $key = $vocab['short_name'];
+    // The URL here should be the URL prefix not the database home
+    // page.  But unfortunately, the URL prefix can't be guaranteed to be
+    // a true prefix. Not all databases place by the rules.  For this reason,
+    // we can never use JSON-LD compact IRIs. :-(
+    $iri = $vocab['url'];
+    $resource->addContextItem($key, $iri);
+  }
+
+  /**
+   * Adds a key/value property to the given resource.
+   *
+   * @param $resource
+   *   A TripalWebServiceResource instance.
+   * @param $term
+   *   The term array for the key.
+   * @param $value
+   *   The value to add.
+   * @param $santize
+   *   An array of keywords indicating how to santize the key.  By default,
+   *   no sanitizing occurs.  The two valid types are 'lowercase', and 'spacing'
+   *   where 'lowercase' converts the term name to all lowercase and
+   *   'spacing' replaces any spaces with underscores.
+   * @return $key
+   *   The key (the term name but with spaces replaced with underscores).
+   */
+  public function addResourceProperty($resource, $term, $value, $sanitize = array()) {
+    if (!is_a($resource, 'TripalWebServiceResource')) {
+      throw new Exception('addProperty: Please provide a $resource of type TripalWebServiceResource.');
+    }
+
+    // First add the term
+    $key = $this->addContextTerm($resource, $term, $sanitize);
+
+    // Then add the property.
+    $resource->addProperty($key, $value);
+  }
+  /**
+   * Sets the type for the given resource.
+   *
+   * The type exist in the context of the web service.
+   *
+   * @param $resource
+   *   A TripalWebServiceResource instance.
+   * @param $type
+   *   The type
+   * @param $santize
+   *   An array of keywords indicating how to santize the key.  By default,
+   *   no sanitizing occurs.  The two valid types are 'lowercase', and 'spacing'
+   *   where 'lowercase' converts the term name to all lowercase and
+   *   'spacing' replaces any spaces with underscores.
+   */
+  public function setResourceType($resource, $term, $sanitize = array('spacing')) {
+
+    if (!is_a($resource, 'TripalWebServiceResource')) {
+      throw new Exception('addProperty: Please provide a $resource of type TripalWebServiceResource.');
+    }
+    // First add the term
+    $key = $this->addContextTerm($resource, $term, $sanitize);
+
+    // Then set the type
+    $resource->setType($key);
+  }
 }

+ 87 - 70
tripal_ws/includes/TripalWebService/TripalEntityService_v0_1.inc

@@ -40,6 +40,14 @@ class TripalEntityService_v0_1 extends TripalWebService {
     $entity_id = (count($this->path) > 1) ? $this->path[1] : '';
     $expfield  = (count($this->path) > 2) ? $this->path[2] : '';
 
+    // is this a valid content type?
+    if ($ctype) {
+      $bundle = tripal_load_bundle_entity(array('label' => $ctype));
+      if (!$bundle) {
+        throw new Exception('Invalid content type: ' . $ctype);
+      }
+    }
+
     // If we have a content type then list all of the entities that belong
     // to it.
     if ($ctype and !$entity_id and !$expfield) {
@@ -67,37 +75,39 @@ class TripalEntityService_v0_1 extends TripalWebService {
 
     // Get the TripalBundle, TripalTerm and TripalVocab for this type.
     $bundle = tripal_load_bundle_entity(array('label' => $ctype));
-    $term = entity_load('TripalTerm', array('id' => $bundle->term_id));
-    $term = reset($term);
-    $vocab = $term->vocab;
+
+    // Find the field that matches the field name provided by the user.
+    list($field, $instance, $term) = $this->findField($bundle, $expfield);
+
+    if (!$field) {
+      throw new Exception("Could not find a matching field for the name: $expfield");
+    }
 
     // Get the TripalEntity
-    $entity = tripal_load_entity('TripalEntity', array('id' => $entity_id));
+    $entity = tripal_load_entity('TripalEntity', array('id' => $entity_id), FALSE, array($field['id']));
     $entity = reset($entity);
 
-    // If we couldn't match this field argument to a field and entity then return
+    // If we couldn't find the entity then fail.
     if (!$entity) {
-      throw new Exception("Cannot find this record.");
+      throw new Exception("Cannot find the record with id $entity_id.");
     }
 
     // Check that the user has access to this entity.  If not then the
     // function call will throw an error.
     $this->checkAccess($entity);
 
-    list($field, $instance, $term) = $this->findField($bundle, $expfield);
 
     // Next add in the ID and Type for this resources.
-    $key = $term['name'];
-    $key_adj = strtolower(preg_replace('/ /', '_', $term['name']));
-    $this->resource->addContextItem($key_adj, $term['url']);
-    $this->resource->setID(urlencode($key));
-    $this->resource->setType($key_adj);
+    $this->setResourceType($this->resource, $term);
+    $this->resource->setID(urlencode($term['name']));
 
-    // Attach the field and then add it's values to the response.
-    field_attach_load($entity->type, array($entity->id => $entity),
-        FIELD_LOAD_CURRENT, array('field_id' => $field['id']));
+    if (!property_exists($entity, $field['field_name'])) {
+      // Attach the field and then add its values to the response.
+      field_attach_load($entity->type, array($entity->id => $entity),
+          FIELD_LOAD_CURRENT, array('field_id' => $field['id']));
+    }
 
-    $this->addEntityField($key_adj, $term, $entity, $bundle, $field, $instance, $service_path, $expfield);
+    $this->addEntityField($term, $entity, $bundle, $field, $instance, $service_path, $expfield);
   }
 
   /**
@@ -110,6 +120,11 @@ class TripalEntityService_v0_1 extends TripalWebService {
     foreach ($instances as $instance) {
       $field_name = $instance['field_name'];
       $field = field_info_field($field_name);
+      $field_type = $field['type'];
+      // Skip fields of remote data.
+      if ($field_type == 'remote__data') {
+        continue;
+      }
       $vocabulary = $instance['settings']['term_vocabulary'];
       $accession = $instance['settings']['term_accession'];
       $temp_term = tripal_get_term_details($vocabulary, $accession);
@@ -130,10 +145,12 @@ class TripalEntityService_v0_1 extends TripalWebService {
     $bundle = tripal_load_bundle_entity(array('label' => $ctype));
     $term = entity_load('TripalTerm', array('id' => $bundle->term_id));
     $term = reset($term);
-    $vocab = $term->vocab;
+
+    // Convert the $term to a simple array
+    $term = tripal_get_term_details($term->vocab->vocabulary, $term->accession);
 
     // Add the vocabulary to the context.
-    $this->resource->addContextItem($term->name, $term->url);
+    $this->resource->addContextItem($term['name'], $term['url']);
 
     // Get the TripalEntity.
     $entity = tripal_load_entity('TripalEntity', array('id' => $entity_id));
@@ -151,16 +168,12 @@ class TripalEntityService_v0_1 extends TripalWebService {
     $itemPage = tripal_get_term_details('schema', 'ItemPage');
     $label = tripal_get_term_details('rdfs', 'label');
     $this->resource->setID($entity_id);
-    $this->resource->setType($term->name);
-    $this->resource->addContextItem('label', $label['url']);
-    $this->resource->addContextItem('ItemPage', $itemPage['url']);
-    $this->resource->addProperty('label', $entity->title);
-    $this->resource->addProperty('ItemPage', url('/bio_data/' . $entity->id, array('absolute' => TRUE)));
+    $this->setResourceType($this->resource, $term);
+    $this->addResourceProperty($this->resource, $label, $entity->title);
+    $this->addResourceProperty($this->resource, $itemPage, url('/bio_data/' . $entity->id, array('absolute' => TRUE)));
 
+    // Add in the entitie's fields.
     $this->addEntityFields($entity, $bundle, $term, $service_path);
-
-//    tripal_ws_services_v0_1_get_content_add_fields($entity, $bundle, $api_url, $response, $ws_path, $ctype, $entity_id, $params);
-//    tripal_ws_services_v0_1_write_context($response, $ctype);
   }
 
   /**
@@ -199,6 +212,7 @@ class TripalEntityService_v0_1 extends TripalWebService {
     // in the order they were set for the display.
     $instances = field_info_instances('TripalEntity', $bundle->name);
 
+    // Sort the instances by their weight.
     uasort($instances, function($a, $b) {
       $a_weight = (is_array($a) && isset($a['widget']['weight'])) ? $a['widget']['weight'] : 0;
       $b_weight = (is_array($b) && isset($b['widget']['weight'])) ? $b['widget']['weight'] : 0;
@@ -221,6 +235,12 @@ class TripalEntityService_v0_1 extends TripalWebService {
       // Get the information about this field.
       $field = field_info_field($field_name);
 
+      // Skip the remote__data field that is provided by the tripal_Ws
+      // module.
+      if ($field['type'] == 'remote__data') {
+        continue;
+      }
+
       // By default, the label for the key in the output should be the
       // term from the vocabulary that the field is assigned. But in the
       // case that the field is not assigned a term, we must use the field name.
@@ -231,8 +251,6 @@ class TripalEntityService_v0_1 extends TripalWebService {
       if (!$term) {
         continue;
       }
-      $key = $term['name'];
-      $key_adj = strtolower(preg_replace('/ /', '_', $key));
 
       // If this field should not be attached by default then just add a link
       // so that the caller can get the information separately.
@@ -244,29 +262,28 @@ class TripalEntityService_v0_1 extends TripalWebService {
         // that information.
         $items = field_get_items('TripalEntity', $entity, $field_name);
         if ($items and count($items) > 0 and $items[0]['value']) {
-          $this->resource->addContextItem($key_adj, $term['url']);
-          $this->resource->addProperty($key_adj, $service_path . '/' . $entity->id . '/' . urlencode($term['name']));
+         $this->addResourceProperty($this->resource, $term, $service_path . '/' . $entity->id . '/' . urlencode($term['name']), array('lowercase', 'spacing'));
         }
         else {
           if ($hide_fields == 'show') {
-            $this->resource->addContextItem($key_adj, $term['url']);
-            $this->resource->addProperty($key_adj, NULL);
+            $this->addResourceProperty($this->resource, $term, NULL, array('lowercase', 'spacing'));
           }
         }
         continue;
       }
 
       // Get the details for this field for the JSON-LD response.
-      $this->addEntityField($key_adj, $term, $entity, $bundle, $field, $instance, $service_path);
+      $this->addEntityField($term, $entity, $bundle, $field, $instance, $service_path);
     }
   }
 
   /**
    * Adds the field as a property of the entity resource.
    */
-  private function addEntityField($key, $term, $entity, $bundle, $field, $instance,
+  private function addEntityField($term, $entity, $bundle, $field, $instance,
       $service_path, $expfield = NULL) {
 
+
     // 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');
@@ -288,7 +305,7 @@ class TripalEntityService_v0_1 extends TripalWebService {
 
     $values = array();
     for ($i = 0; $i < count($items); $i++) {
-      $values[$i] = $this->sanitizeFieldKeys($items[$i]['value'], $bundle, $service_path);
+      $values[$i] = $this->sanitizeFieldKeys($this->resource, $items[$i]['value'], $bundle, $service_path);
     }
 
     if ($hide_fields == 'hide' and empty($values[0])) {
@@ -307,27 +324,24 @@ class TripalEntityService_v0_1 extends TripalWebService {
           }
         }
         else {
-          $this->resource->addContextItem($key, $term['url']);
-          $this->resource->addProperty($key, $values[0]);
+          $this->addResourceProperty($this->resource, $term, $values[0], array('lowercase', 'spacing'));
         }
       }
       // If the value is not an array it's a scalar so add it as is to the
       // response.
       else {
-        $this->resource->addContextItem($key, $term['url']);
-        $this->resource->addProperty($key, $values[0]);
+        $this->addResourceProperty($this->resource, $term, $values[0], array('lowercase', 'spacing'));
       }
     }
 
-    // If the field cardinality is > 1
+    // If the field cardinality is > 1 or -1 (for unlimited)
     if ($field['cardinality'] != 1) {
 
       // If this is the expanded field page then we need to swap out
       // the resource for a collection.
       $response = new TripalWebServiceCollection($service_path . '/' . urlencode($expfield), $this->params);
       $label = tripal_get_term_details('rdfs', 'label');
-      $response->addContextItem('label', $label['url']);
-      $response->addProperty('label', $instance['label']);
+      $this->addResourceProperty($response, $label, $instance['label']);
       $i = 0;
       foreach ($values as $delta => $element) {
         $member = new TripalWebServiceResource($service_path . '/' . urlencode($expfield));
@@ -335,7 +349,7 @@ class TripalEntityService_v0_1 extends TripalWebService {
         // Add the context of the parent resource because all of the keys
         // were santizied and set to match the proper context.
         $member->setContext($this->resource);
-        $member->setType($key);
+        $this->setResourceType($member, $term);
         foreach ($element as $key => $value) {
           $member->addProperty($key, $value);
         }
@@ -346,7 +360,8 @@ class TripalEntityService_v0_1 extends TripalWebService {
         $this->resource = $response;
       }
       else {
-        $this->resource->addProperty($key, $response);
+        //$this->resource->addProperty($key, $response);
+        $this->addResourceProperty($this->resource, $term, $response, array('lowercase', 'spacing'));
       }
     }
   }
@@ -354,7 +369,8 @@ class TripalEntityService_v0_1 extends TripalWebService {
   /**
    * Rewrites the keys of a field's items array for use with web services.
    */
-  private function sanitizeFieldKeys($value, $bundle, $service_path) {
+  private function sanitizeFieldKeys($resource, $value, $bundle, $service_path) {
+
     // 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');
@@ -376,15 +392,15 @@ class TripalEntityService_v0_1 extends TripalWebService {
           $vocabulary = $matches[1];
           $accession = $matches[2];
           $term = tripal_get_term_details($vocabulary, $accession);
+          $key = $this->addContextTerm($resource, $term, array('lowercase', 'spacing'));
 
-          $key_adj = strtolower(preg_replace('/ /', '_', $term['name']));
           if (is_array($v)) {
-            $temp[$key_adj] = $this->sanitizeFieldKeys($v, $bundle, $service_path);
+            $temp[$key] = $this->sanitizeFieldKeys($resource, $v, $bundle, $service_path);
           }
           else {
-            $temp[$key_adj] = $v;
+            $temp[$key] = $v;
           }
-          $this->resource->addContextItem($key_adj, $term['url']);
+          $term['name'] = $key;
 
         }
         else {
@@ -491,7 +507,7 @@ class TripalEntityService_v0_1 extends TripalWebService {
       $dir = 'ASC';
 
       // If the user provided more than one order statement then those are
-      // separated by a semicolong.
+      // separated by a semicolon.
       $items = explode(';', $order_params);
       foreach ($items as $key) {
 
@@ -741,17 +757,19 @@ class TripalEntityService_v0_1 extends TripalWebService {
    */
   private function doContentTypeList($ctype) {
     $service_path = $this->getServicePath() . '/' . urlencode($ctype);
-    $label = tripal_get_term_details('rdfs', 'label');
     $this->resource = new TripalWebServiceCollection($service_path, $this->params);
-    $this->resource->addContextItem('label', $label['url']);
 
     // Get the TripalBundle, TripalTerm and TripalVocab type for this type.
     $bundle = tripal_load_bundle_entity(array('label' => $ctype));
     $term = entity_load('TripalTerm', array('id' => $bundle->term_id));
     $term = reset($term);
 
+    // Convert term to a simple array
+    $term = tripal_get_term_details($term->vocab->vocabulary, $term->accession);
+
     // Set the label for this collection.
-    $this->resource->addProperty('label', $bundle->label . " collection");
+    $label = tripal_get_term_details('rdfs', 'label');
+    $this->addResourceProperty($this->resource, $label, $bundle->label . " collection");
 
     // For quick lookup, get the mapping of WS keys to their appropriate fields.
     $field_mapping = $this->getFieldMapping($bundle);
@@ -832,13 +850,10 @@ class TripalEntityService_v0_1 extends TripalWebService {
       $itemPage = tripal_get_term_details('schema', 'ItemPage');
       $label = tripal_get_term_details('rdfs', 'label');
       $member = new TripalWebServiceResource($service_path);
-      $member->addContextItem('label', $label['url']);
-      $member->addContextItem('ItemPage', $itemPage['url']);
-      $member->addContextItem($term->name, $term->url);
       $member->setID($entity->id);
-      $member->setType($term->name);
-      $member->addProperty('label', $entity->title);
-      $member->addProperty('ItemPage', url('/bio_data/' . $entity->id, array('absolute' => TRUE)));
+      $this->setResourceType($member, $term);
+      $this->addResourceProperty($member, $label, $entity->title);
+      $this->addResourceProperty($member, $itemPage, url('/bio_data/' . $entity->id, array('absolute' => TRUE)));
       $this->resource->addMember($member);
     }
   }
@@ -848,10 +863,10 @@ class TripalEntityService_v0_1 extends TripalWebService {
    */
   private function doAllTypesList() {
     $service_path = $this->getServicePath();
-    $label = tripal_get_term_details('rdfs', 'label');
     $this->resource = new TripalWebServiceCollection($service_path, $this->params);
-    $this->resource->addContextItem('label', $label['url']);
-    $this->resource->addProperty('label', 'Content Types');
+
+    $label = tripal_get_term_details('rdfs', 'label');
+    $this->addResourceProperty($this->resource, $label, 'Content Types');
 
     // Get the list of published terms (these are the bundle IDs)
     $bundles = db_select('tripal_bundle', 'tb')
@@ -866,16 +881,21 @@ class TripalEntityService_v0_1 extends TripalWebService {
       $term = reset($entity);
       $vocab = $term->vocab;
 
+      // Convert the term to a simple array
+      $term = tripal_get_term_details($term->vocab->vocabulary, $term->accession);
+
       $member = new TripalWebServiceResource($service_path);
+      $member->setID(urlencode($bundle->label));
+
       // If the term has no URL then we'll default to using thie site's
       // term lookup service.
-      $url = $term->url;
+      $url = $term['url'];
       if (!$url) {
-        $url = url('cv/lookup/' . $term->vocab->vocabulary . '/' . $term->accession , array('absolute' => TRUE));
+        $url = url('cv/lookup/' . $term['vocabulary']['short_name'] . '/' . $term['accession'] , array('absolute' => TRUE));
+        $term['url'] = $url;
       }
-      $member->addContextItem($term->name, $url);
-      $member->addContextItem('label', $label['url']);
-      $member->addProperty('label', $bundle->label);
+      $this->setResourceType($member, $term);
+      $this->addResourceProperty($member, $label, $bundle->label);
       $member->addContextItem('description', 'hydra:description');
       // Get the bundle description. If no description is provided then
       // use the term definition
@@ -887,9 +907,6 @@ class TripalEntityService_v0_1 extends TripalWebService {
         $description = '';
       }
       $member->addProperty('description', $description);
-      $member->setID(urlencode($bundle->label));
-      $member->setType($term->name);
-
       $this->resource->addMember($member);
 
     }

+ 9 - 1
tripal_ws/includes/TripalWebServiceResource.inc

@@ -65,13 +65,21 @@ class TripalWebServiceResource {
   /**
    * Adds a term to the '@context' section for this resource.
    *
+   * This function should not be called directory.  Rather, the
+   * addContextTerm() and addContextVocab() functions built
+   * into the TripalWebService class should be used as  these  will help
+   * ensure terms are added proper for the context of the web service.
+   *
    * @param $name
    *   The term name.
    * @param $iri
    *   The Internationalized Resource Identifiers or it's shortcut.
+   *
    */
   public function addContextItem($name, $iri) {
-    // TODO: make sure that if a shortcut is used that the parent is present.
+    if (array_key_exists($name, $this->context)) {
+      return;
+    }
     $this->context[$name] = $iri;
   }
 

+ 78 - 0
tripal_ws/includes/tripal_ws.field_storage.inc

@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * Implements hook_field_storage_info().
+ */
+function tripal_ws_field_storage_info() {
+  return array(
+    'field_tripal_ws_storage' => array(
+      'label' => t('Tripal Web Services'),
+      'description' => t('Retreives fields data from a remote site using Tripal web services.'),
+      'settings' => array(),
+    ),
+  );
+}
+
+/**
+ * Implements hook_field_storage_load().
+ *
+ * Responsible for loading the fields from the Chado database and adding
+ * their values to the entity.
+ */
+function tripal_ws_field_storage_load($entity_type, $entities, $age,
+    $fields, $options) {
+
+  $load_current = $age == FIELD_LOAD_CURRENT;
+  global $language;
+  $langcode = $language->language;
+
+  foreach ($entities as $id => $entity) {
+
+    // Iterate through the entity's fields so we can get the column names
+    // that need to be selected from each of the tables represented.
+    foreach ($fields as $field_id => $ids) {
+
+      // By the time this hook runs, the relevant field definitions have been
+      // populated and cached in FieldInfo, so calling field_info_field_by_id()
+      // on each field individually is more efficient than loading all fields in
+      // memory upfront with field_info_field_by_ids().
+      $field = field_info_field_by_id($field_id);
+      $field_name = $field['field_name'];
+      $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);
+      }
+
+    } // end: foreach ($fields as $field_id => $ids) {
+  } // end: foreach ($entities as $id => $entity) {
+}
+
+/**
+ * Implements hook_field_storage_query().
+ */
+function tripal_ws_field_storage_query($query) {
+
+}
+/**
+ * Implements hook_field_storage_write().
+ */
+function tripal_ws_field_storage_write($entity_type, $entity, $op, $fields) {
+
+}
+

+ 95 - 0
tripal_ws/includes/tripal_ws.fields.inc

@@ -0,0 +1,95 @@
+<?php
+/**
+ * @file
+ * Contains all field specific code outside the classes.
+ */
+
+/**
+ * Implements hook_bundle_fields_info().
+ */
+function tripal_ws_bundle_fields_info($entity_type, $bundle) {
+  $fields = array();
+
+  // No fields are added programmatically.
+  return $fields;
+}
+
+/**
+ * Implements hook_bundle_instances_info().
+ */
+function tripal_ws_bundle_instances_info($entity_type, $bundle) {
+  $instances = array();
+
+  // No field instances are added programmatically.
+  return $instances;
+}
+
+/**
+ * Implements hook_bundle_create_user_field().
+ *
+ * A priviledged user has the ability to add new fields to the bundle. The
+ * remote__data field is allowed to be added dynamically by the user.
+ * But, Drupal doesn't know how to deal with it, so this function is called
+ * for any field attached to a TripalEntity bundle type. Any fields whose
+ * TripalField::$module argument is set to 'tripal_ws' and that can be
+ * added dynamically will result in a call to this function.
+ */
+function tripal_ws_bundle_create_user_field($new_field, $bundle) {
+
+  // Get the table this bundle is mapped to.
+  $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
+  $vocab = $term->vocab;
+  $params = array(
+    'vocabulary' => $vocab->vocabulary,
+    'accession' => $term->accession,
+  );
+
+  // We allow site admins to add new chado_linker__prop fields to an entity.
+  // This function will allow us to properly add them.  But at this point we
+  // don't know the controlled vocabulary term.  We'll have to use the
+  // defaults and let the user set it using the interface.
+  if ($new_field['type'] == 'remote__data') {
+    $field_name = $new_field['field_name'];
+    $field_type = 'remote__data';
+
+    // First add the field.
+    field_create_field(array(
+      'field_name' => $field_name,
+      'type' => $field_type,
+      'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+      'locked' => FALSE,
+      'storage' => array(
+        'type' => 'field_tripal_ws_storage',
+      ),
+    ));
+
+    // Now add the instance
+    field_create_instance(array(
+      'field_name' => $field_name,
+      'entity_type' => 'TripalEntity',
+      'bundle' => $bundle->name,
+      'label' => $new_field['label'],
+      'description' => '',
+      'required' => FALSE,
+      'settings' => array(
+        'auto_attach' => FALSE,
+        'term_vocabulary' => '',
+        'term_accession' => '',
+        'term_name' => ''
+      ),
+      'widget' => array(
+        'type' => 'remote__data_widget',
+        'settings' => array(
+          'display_label' => 1,
+        ),
+      ),
+      'display' => array(
+        'default' => array(
+          'label' => 'inline',
+          'type' => 'remote__data_formatter',
+          'settings' => array(),
+        ),
+      ),
+    ));
+  }
+}

+ 37 - 0
tripal_ws/theme/css/tripal_ws.css

@@ -0,0 +1,37 @@
+.tripal-remote--data-field {
+  max-height: 400px;
+  overflow: auto;
+  width: 100%;
+}
+.tripal-remote--data-field-logo {
+  max-height: 100px;
+}
+
+.tripal-remote--data-field-dl dt {
+  float: left;
+  padding-right: 5px;
+}
+
+.tripal-remote--data-field-table  {
+  width: 100%;
+}
+.tripal-remote--data-field-table tr {
+  background: none;
+}
+.tripal-remote--data-field-table td {
+  border-left: none;
+  border-right: none;
+  text-align: left;
+  vertical-align: top;
+}
+
+.tripal-remote--data-field-table th{
+  border: none;
+}
+.tripal-remote--data-field-table tr:last-child td {
+  border-bottom: none;
+}
+
+.tripal-remote--data-field-table-label {
+  font-weight: bold;
+}

+ 4 - 2
tripal_ws/tripal_ws.info

@@ -3,6 +3,8 @@ 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
+stylesheets[all][] = theme/css/tripal_ws.css
+
+dependencies[] = tripal

+ 5 - 1
tripal_ws/tripal_ws.install

@@ -1,5 +1,8 @@
 <?php
 
+
+function tripal_ws_install() {
+}
 /**
  * Implementation of hook_schema().
  *
@@ -60,4 +63,5 @@ function tripal_ws_tripal_sites_schema() {
     'primary key' => array('id'),
   );
   return $schema;
-}
+}
+

+ 43 - 0
tripal_ws/tripal_ws.module

@@ -1,10 +1,16 @@
 <?php
 
 require_once  "api/tripal_ws.api.inc";
+require_once  "includes/tripal_ws.field_storage.inc";
+require_once  "includes/tripal_ws.fields.inc";
 require_once  "includes/TripalWebService.inc";
 require_once  "includes/TripalWebServiceResource.inc";
 require_once  "includes/TripalWebServiceCollection.inc";
 
+// Web Services Fields
+require_once "includes/TripalFields/WebServicesField.inc";
+require_once "includes/TripalFields/WebServicesFieldWidget.inc";
+require_once "includes/TripalFields/WebServicesFieldFormatter.inc";
 
 /**
  * Implements hook_init()
@@ -409,4 +415,41 @@ function tripal_ws_load_remote_entity($site_id, $api_version, $ctype, $id) {
 
   return $content;
 
+}
+
+function tripal_ws_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
+  if ($form['#instance']['entity_type'] == 'TripalEntity') {
+      $form['field']['cardinality']['#access'] = FALSE;
+  }
+}
+
+/*
+* Returns the decoded json data for a specific field.
+*/
+function tripal_ws_remote_data_single_field_pull($field, $entity_url){
+  $options = array();
+  $full_url = $entity_url . '/' . $field;
+  $data = drupal_http_request($full_url, $options);
+  if(!empty($data)){
+    $data = drupal_json_decode($data->data);
+  }
+  return $data;
+}
+
+
+/**
+ * Implements hook_entity_info_alter()
+ *
+ * Add the web services display as a view mode.
+ */
+function tripal_ws_entity_info_alter(&$entity_info) {
+
+  // Set the controller class for nodes to an alternate implementation of the
+  // DrupalEntityController interface.
+  $entity_info['TripalEntity']['view modes']  += array(
+    'tripal_ws' => array(
+      'label' => t('Tripal Web Services'),
+      'custom settings' => FALSE,
+    ),
+  );
 }