ソースを参照

Merge pull request #1 from tripal/7.x-3.x

Sync with upstream
Douglas Senalik 5 年 前
コミット
6ebbdc538b
100 ファイル変更7123 行追加71 行削除
  1. 159 0
      .all-contributorsrc
  2. 0 18
      .github/ISSUE-TEMPLATE.md
  3. 40 0
      .github/ISSUE_TEMPLATE/bug_report.md
  4. 15 0
      .github/ISSUE_TEMPLATE/discussion.md
  5. 33 0
      .github/ISSUE_TEMPLATE/feature_request.md
  6. 31 0
      .github/PULL_REQUEST_TEMPLATE.md
  7. 12 0
      .gitignore
  8. 86 0
      .travis.yml
  9. 1 0
      CONTRIBUTING.md
  10. 58 52
      README.md
  11. 15 0
      composer.json
  12. 2206 0
      composer.lock
  13. 20 0
      docs/Makefile
  14. 1 0
      docs/README.md
  15. BIN
      docs/_images/TripalLogo_dark.png
  16. BIN
      docs/_images/dev_guide/data_structures/Terminology-Diagram.png
  17. BIN
      docs/_images/dev_guide/data_structures/Tripal-Semantic-web.png
  18. BIN
      docs/_images/user_guide/learn_chado/375px-ChadoLogo.png
  19. BIN
      docs/_static/hexagon_pattern.png
  20. BIN
      docs/_static/small_logoonly.png
  21. 183 0
      docs/_static/theme_overrides.css
  22. 170 0
      docs/conf.py
  23. 15 0
      docs/contributing.rst
  24. 64 0
      docs/contributing/documentation.rst
  25. 7 0
      docs/contributing/funding.rst
  26. 138 0
      docs/contributing/governance.rst
  27. 130 0
      docs/contributing/pull_requests.rst
  28. 188 0
      docs/contributing/tests.rst
  29. 19 0
      docs/dev_guide.rst
  30. 63 0
      docs/dev_guide/best_practices.rst
  31. 296 0
      docs/dev_guide/chado.rst
  32. BIN
      docs/dev_guide/custom_data_loader.0.png
  33. BIN
      docs/dev_guide/custom_data_loader.1.oob_file_interface.png
  34. BIN
      docs/dev_guide/custom_data_loader.2.oob_analysis_select.png
  35. BIN
      docs/dev_guide/custom_data_loader.3.cvterm_select.png
  36. 502 0
      docs/dev_guide/custom_data_loader.rst
  37. 49 0
      docs/dev_guide/custom_field.rst
  38. 114 0
      docs/dev_guide/custom_field/ajax_custom_formatter.rst
  39. 223 0
      docs/dev_guide/custom_field/create_instance.rst
  40. BIN
      docs/dev_guide/custom_field/custom_formatter.pager.1.png
  41. 208 0
      docs/dev_guide/custom_field/custom_formatter.rst
  42. BIN
      docs/dev_guide/custom_field/custom_formatter.settings.1.png
  43. BIN
      docs/dev_guide/custom_field/custom_formatter.settings.2.png
  44. 153 0
      docs/dev_guide/custom_field/custom_widget.rst
  45. 513 0
      docs/dev_guide/custom_field/manual_field_creation.rst
  46. BIN
      docs/dev_guide/custom_field/select_vocab_terms.1.organism.png
  47. BIN
      docs/dev_guide/custom_field/select_vocab_terms.2.cds.png
  48. BIN
      docs/dev_guide/custom_field/select_vocab_terms.3.go.png
  49. BIN
      docs/dev_guide/custom_field/select_vocab_terms.4.edam.png
  50. 157 0
      docs/dev_guide/custom_field/select_vocab_terms.rst
  51. BIN
      docs/dev_guide/custom_field/tripal_field_generator.TFG.png
  52. 9 0
      docs/dev_guide/custom_field/tripal_field_generator.rst
  53. 11 0
      docs/dev_guide/custom_modules.rst
  54. BIN
      docs/dev_guide/custom_web_services.1_hydra_console.png
  55. BIN
      docs/dev_guide/custom_web_services.2.png
  56. BIN
      docs/dev_guide/custom_web_services.3.png
  57. BIN
      docs/dev_guide/custom_web_services.4.png
  58. 347 0
      docs/dev_guide/custom_web_services.rst
  59. 57 0
      docs/dev_guide/data_structures.rst
  60. BIN
      docs/dev_guide/exporting_field_settings.1.assign_term.png
  61. BIN
      docs/dev_guide/exporting_field_settings.2.png
  62. BIN
      docs/dev_guide/exporting_field_settings.3.png
  63. BIN
      docs/dev_guide/exporting_field_settings.4.png
  64. BIN
      docs/dev_guide/exporting_field_settings.5.png
  65. BIN
      docs/dev_guide/exporting_field_settings.6.png
  66. BIN
      docs/dev_guide/exporting_field_settings.7.png
  67. 122 0
      docs/dev_guide/exporting_field_settings.rst
  68. 31 0
      docs/dev_guide/introduction.rst
  69. 55 0
      docs/dev_guide/rtd.rst
  70. 31 0
      docs/dev_guide/tutorials.rst
  71. 26 0
      docs/extensions.rst
  72. 52 0
      docs/extensions/3rd_party.rst
  73. BIN
      docs/extensions/Tripal-Bronze.png
  74. 0 0
      docs/extensions/Tripal-Bronze.svg
  75. BIN
      docs/extensions/Tripal-Gold.png
  76. 0 0
      docs/extensions/Tripal-Gold.svg
  77. BIN
      docs/extensions/Tripal-Silver.png
  78. 0 0
      docs/extensions/Tripal-Silver.svg
  79. 33 0
      docs/extensions/administrative.rst
  80. 50 0
      docs/extensions/analysis.rst
  81. 56 0
      docs/extensions/data_input.rst
  82. 53 0
      docs/extensions/developer.rst
  83. 30 0
      docs/extensions/in_development.rst
  84. 33 0
      docs/extensions/instructions.rst
  85. 83 0
      docs/extensions/module_rating.rst
  86. 42 0
      docs/extensions/search.rst
  87. 53 0
      docs/extensions/visualization.rst
  88. 16 0
      docs/index.rst
  89. 36 0
      docs/make.bat
  90. 1 1
      docs/tripal_doxygen.config
  91. 27 0
      docs/user_guide.rst
  92. BIN
      docs/user_guide/bulk_loader.1.png
  93. BIN
      docs/user_guide/bulk_loader.10.png
  94. BIN
      docs/user_guide/bulk_loader.11.png
  95. BIN
      docs/user_guide/bulk_loader.12.png
  96. BIN
      docs/user_guide/bulk_loader.13.png
  97. BIN
      docs/user_guide/bulk_loader.2.png
  98. BIN
      docs/user_guide/bulk_loader.3.png
  99. BIN
      docs/user_guide/bulk_loader.4.png
  100. BIN
      docs/user_guide/bulk_loader.5.png

+ 159 - 0
.all-contributorsrc

@@ -0,0 +1,159 @@
+{
+  "files": [
+    "README.md"
+  ],
+  "imageSize": 100,
+  "commit": false,
+  "contributors": [
+    {
+      "login": "spficklin",
+      "name": "Stephen Ficklin",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/1719352?v=4",
+      "profile": "https://github.com/spficklin",
+      "contributions": [
+        "code",
+        "eventOrganizing",
+        "doc",
+        "projectManagement",
+        "review"
+      ]
+    },
+    {
+      "login": "bradfordcondon",
+      "name": "Bradford Condon",
+      "avatar_url": "https://avatars2.githubusercontent.com/u/7063154?v=4",
+      "profile": "http://www.bradfordcondon.com/",
+      "contributions": [
+        "code",
+        "doc",
+        "projectManagement",
+        "eventOrganizing",
+        "review"
+      ]
+    },
+    {
+      "login": "laceysanderson",
+      "name": "Lacey-Anne Sanderson",
+      "avatar_url": "https://avatars3.githubusercontent.com/u/1566301?v=4",
+      "profile": "https://laceysanderson.github.io/",
+      "contributions": [
+        "code",
+        "doc",
+        "projectManagement",
+        "eventOrganizing",
+        "review"
+      ]
+    },
+    {
+      "login": "chunhuaicheng",
+      "name": "chunhuaicheng",
+      "avatar_url": "https://avatars2.githubusercontent.com/u/14333886?v=4",
+      "profile": "https://github.com/chunhuaicheng",
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "shawnawsu",
+      "name": "Shawna",
+      "avatar_url": "https://avatars1.githubusercontent.com/u/24374002?v=4",
+      "profile": "https://github.com/shawnawsu",
+      "contributions": [
+        "code",
+        "content",
+        "doc",
+        "review"
+      ]
+    },
+    {
+      "login": "mboudet",
+      "name": "mboudet",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/17642511?v=4",
+      "profile": "https://github.com/mboudet",
+      "contributions": [
+        "bug"
+      ]
+    },
+    {
+      "login": "guignonv",
+      "name": "Valentin Guignon",
+      "avatar_url": "https://avatars1.githubusercontent.com/u/7290244?v=4",
+      "profile": "https://github.com/guignonv",
+      "contributions": [
+        "bug"
+      ]
+    },
+    {
+      "login": "mestato",
+      "name": "Meg Staton",
+      "avatar_url": "https://avatars1.githubusercontent.com/u/508122?v=4",
+      "profile": "https://github.com/mestato",
+      "contributions": [
+        "fundingFinding",
+        "eventOrganizing"
+      ]
+    },
+    {
+      "login": "abretaud",
+      "name": "Anthony Bretaudeau",
+      "avatar_url": "https://avatars3.githubusercontent.com/u/238755?v=4",
+      "profile": "https://github.com/abretaud",
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "colthom",
+      "name": "colthom",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/17720870?v=4",
+      "profile": "https://github.com/colthom",
+      "contributions": [
+        "doc"
+      ]
+    },
+    {
+      "login": "almasaeed2010",
+      "name": "Abdullah Almsaeed",
+      "avatar_url": "https://avatars2.githubusercontent.com/u/1512664?v=4",
+      "profile": "http://almsaeedstudio.com",
+      "contributions": [
+        "code",
+        "review"
+      ]
+    },
+    {
+      "login": "btski",
+      "name": "btski",
+      "avatar_url": "https://avatars1.githubusercontent.com/u/32686196?v=4",
+      "profile": "https://github.com/btski",
+      "contributions": [
+        "question"
+      ]
+    },
+    {
+      "login": "ekcannon",
+      "name": "ekcannon",
+      "avatar_url": "https://avatars0.githubusercontent.com/u/3409057?v=4",
+      "profile": "https://github.com/ekcannon",
+      "contributions": [
+        "ideas",
+        "eventOrganizing"
+      ]
+    },
+    {
+      "login": "jlwegrzyn",
+      "name": "jlwegrzyn",
+      "avatar_url": "https://avatars1.githubusercontent.com/u/50996590?v=4",
+      "profile": "https://github.com/jlwegrzyn",
+      "contributions": [
+        "fundingFinding"
+      ]
+    }
+  ],
+  "contributorsPerLine": 7,
+  "projectName": "tripal",
+  "projectOwner": "tripal",
+  "repoType": "github",
+  "repoHost": "https://github.com",
+  "commitConvention": "none"
+}

+ 0 - 18
.github/ISSUE-TEMPLATE.md

@@ -1,18 +0,0 @@
-INSTRUCTIONS: The following template is meant to provide the information that will help other Tripal developers diagnose and reproduce your issue. Follow the directions below to complete the template. If the template is not appropriate for your issue you may remove it and describe your issue.
-
-### System information
-<!--Please enter the following information (if able). All information is available in your site's administrator report area (Administration Toolbar > Reports > Status Report) -->
-
-* Tripal Version:
-* Drupal Version:
-* PostgreSQL Version:
-* PHP Version:
-
-
-### Issue description
-<!-- Please describe your issue here 
-Some information you might want to include: the page you're seeing the issue on,  what behaviour you're experiencing versus what you expect, steps to reproduce...  really anything you think might best help us help you! -->
-
-
-
-<-- PS. Also feel free to ask questions, start discussions, suggest improvements We welcome any and all feedback! -->

+ 40 - 0
.github/ISSUE_TEMPLATE/bug_report.md

@@ -0,0 +1,40 @@
+---
+name: Bug report
+about: Create a report to help us improve
+
+---
+
+## BUG/ERROR report
+<!---
+INSTRUCTIONS: The following template is meant to provide the information that will help other Tripal
+ developers diagnose and reproduce your issue. Follow the directions below to complete the template.
+  Feel free to delete sections that are not applicable.
+--->
+
+
+
+### System information
+<!--Please enter the following information (if able). All information is available in your site's administrator report area (Administration Toolbar > Reports > Status Report) -->
+
+* Tripal Version:
+* Drupal Version:
+* PostgreSQL Version:
+* PHP Version:
+
+### Issue description
+<!-- Please describe your issue here 
+Some information you might want to include: the page you're seeing the issue on,  what behavior 
+you're experiencing versus what you expect, really anything you think might best help us help you! -->
+
+#### Steps to reproduce
+
+<!-- What steps are necessary to recreate the bug/error?  Clear instructions here will make it easier for
+ us to fix the problem! -->
+#### Error messages and screenshots
+<!-- Please include any error messages you find.  Including them in ``` as below will make the issue
+ more readable. -->
+<!-- Screenshots can be really helpful to describe the problem! -->
+
+```
+error message
+```

+ 15 - 0
.github/ISSUE_TEMPLATE/discussion.md

@@ -0,0 +1,15 @@
+---
+name: Discussion / Question 
+about: Discuss a topic / Ask for help.
+
+---
+
+<!---
+INSTRUCTIONS: Our intent is to use github as an open forum to help community members connect.
+If you are looking to do any of the following, you are in the right place! and Thank You!
+  - Ask for help/documentation/clarification of Tripal Functionality and Site Building/Management
+  - Discuss controlled vocabulary terms for your data
+  - Ask for input on site/module design questions
+  - State your intent to develop a specific extension module
+  - Really any type of discussion or question -we want to hear from you!
+--->

+ 33 - 0
.github/ISSUE_TEMPLATE/feature_request.md

@@ -0,0 +1,33 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+
+---
+
+
+## Feature Request/Discussion
+<!---
+INSTRUCTIONS: The following template is meant to structure your feature request. 
+  Please keep in mind, this issue may evolve into discussion for an extension module
+  if it's decided the feature is not a good fit for Tripal Core.
+--->
+
+<!--- Go over all the following points, and select the option in the brackets that applies to you. -->
+<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
+* This feature [does / does not] attempt to solve an existing problem with Tripal
+* I [am/am not] open to developing or collaborating on an extension module if this is not a good fit for Tripal Core
+<!--- (no pressure here -just good to know upfront :-) ) -->
+* This feature is [URGENT/Not Urgent]
+
+### Description
+<!--- A clear and concise description of what you want to happen. -->
+
+### Your Specific Use Case
+<!--- Please describe how you would use this feature in your own Tripal site and why you need it -->
+
+### Generally Applicable
+<!--- Why do you feel this is generally applicable? -->
+<!--- Suggest other use cases if possible. -->
+
+### Additional information/screenshots
+<!--- Add any other context or screenshots about the feature request here. -->

+ 31 - 0
.github/PULL_REQUEST_TEMPLATE.md

@@ -0,0 +1,31 @@
+<!--- Thank you for contributing! -->
+<!--- Provide a general summary of your changes in the Title above -->
+<!--- See our Contribution Guidelines here:
+          https://github.com/tripal/tripal/blob/7.x-3.x/CONTRIBUTING.md -->
+
+
+<!---  Please set the header below based on the PR type:
+# New Feature
+# Bug Fix
+# Documentation  --->
+
+#
+
+Issue #
+
+## Description
+<!--- Describe your changes in detail -->
+<!--- Why is this change required? What problem does it solve? -->
+
+## Testing?
+<!--- Please describe in detail how to test these changes. -->
+<!--- Reviewers will use this section to test the submission! -->
+<!--- If you've implemented PHPUnit tests, you can describe the test cases here. -->
+<!--- Unit testing guidelines: https://github.com/tripal/tripal/blob/7.x-3.x/tests/README.md -->
+
+## Screenshots (if appropriate):
+
+## Additional Notes (if any):
+
+<!--- New features should include in-line code documentation. -->
+<!--- Would a user or developer guide be helpful for this feature? -->

+ 12 - 0
.gitignore

@@ -1 +1,13 @@
 .DS_Store
+.idea/
+vendor/
+tests/.env
+.buildpath
+.project
+.settings/
+docs/_build
+docs/docs
+docs/html
+docs/latex
+docs/xml
+

+ 86 - 0
.travis.yml

@@ -0,0 +1,86 @@
+language: php
+
+services:
+  - docker
+  - postgresql
+
+sudo: required
+
+php:
+  - 7.1
+  - 7.2
+#  PHP 7.3 is not yet supported (issue: https://www.drupal.org/project/drupal/issues/3012308)
+#  - 7.3
+
+env:
+  - BASE_URL="http://127.0.0.1:8080"
+
+install:
+  - composer global require "drush/drush:~8"
+
+before_script:
+  - docker pull statonlab/tripal2
+  - psql -c "create database test_db encoding 'utf-8';" -U postgres
+  - psql -c "alter role postgres with password 'dbpass';" -U postgres
+  - cd ..
+
+  # Set additional environment variables
+  - export PATH="$HOME/.config/composer/vendor/bin:$PATH"
+  - export DRUPAL_ROOT="$(pwd)/drupal"
+
+  # Download and install Drupal
+  - drush dl drupal-7 -y
+  - mv drupal-7* drupal
+  - cd drupal
+  # Run the php server
+  - php -S 127.0.0.1:8080 &
+  - drush si -y --db-url='pgsql://postgres:dbpass@localhost:5432/test_db'
+                --account-name='admin'
+                --account-pass='admin_pass'
+                --site-mail='admin@example.com'
+                --site-name='Tripal 3'
+
+  # Download Dependencies
+  - drush dl -y field_group, field_group_table, field_formatter_class, field_formatter_settings, ctools, date, devel,
+                ds, link, entity, libraries, redirect, token, uuid, jquery_update, views, webform
+
+  # Enable dependencies
+  - drush en -y field_group, field_group_table, field_formatter_class, field_formatter_settings, ctools, date, devel,
+              ds, link, entity, libraries, redirect, token uuid, jquery_update, views, webform
+
+  # up memory limit of PHP
+  - echo "memory_limit=2G" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
+
+
+script:
+  # Link our repo to the modules directory
+  - mv ../tripal sites/all/modules/tripal
+
+  # Run a docker container with tripal 2 pre-installed
+  - docker run -it -d --rm --name tripal2 -v "$(pwd)/sites/all/modules/tripal":/tripal statonlab/tripal2
+
+  # Apply patches
+  - wget --no-check-certificate https://drupal.org/files/drupal.pgsql-bytea.27.patch
+  - patch -p1 < drupal.pgsql-bytea.27.patch
+  - cd sites/all/modules/views
+  - patch -p1 < ../tripal/tripal_chado_views/views-sql-compliant-three-tier-naming-1971160-30.patch
+  - cd ../tripal
+
+  # Install Tripal
+  - drush en -y tripal tripal_chado tripal_chado_views tripal_ds tripal_ws
+  - drush eval "module_load_include('inc', 'tripal_chado', 'includes/tripal_chado.install'); tripal_chado_load_drush_submit('Install Chado v1.3');"
+  - drush trp-run-jobs --username=admin
+
+  # Prepare Chado
+  - drush eval "module_load_include('inc', 'tripal_chado', 'includes/setup/tripal_chado.setup'); tripal_chado_prepare_drush_submit();"
+  - drush trp-run-jobs --username=admin
+
+  # Run PHPUnit tests
+  - composer update
+  - cp tests/.travis.env tests/.env
+  - ./vendor/bin/phpunit
+
+  # Test Tripal v2 to v3 upgrade steps
+  - docker exec -it tripal2 drush pm-disable tripal_core -y
+  - docker exec -it tripal2 bash -c "rm -rf /modules/tripal && ln -s /tripal /modules/tripal"
+  - docker exec -it tripal2 drush en -y tripal

+ 1 - 0
CONTRIBUTING.md

@@ -0,0 +1 @@
+Please see the [Tripal online documentation](https://tripal.readthedocs.io/en/latest/contributing/pull_requests.html#pull-request-pr-guideline) for more information about contributing to Tripal and Tripal governance.

+ 58 - 52
README.md

@@ -1,22 +1,21 @@
+[![7.x-3.x Build Status](https://travis-ci.org/tripal/tripal.svg?branch=7.x-3.x)](https://travis-ci.org/tripal/tripal)
+[![All Contributors](https://img.shields.io/badge/all_contributors-14-orange.svg?style=flat-square)](#contributors)
+[![Documentation Status](https://readthedocs.org/projects/tripal/badge/?version=latest)](https://tripal.readthedocs.io/en/latest/?badge=latest)
+
+[![DOI](https://zenodo.org/badge/42666405.svg)](https://zenodo.org/badge/latestdoi/42666405)
+
+
 ![alt tag](https://raw.githubusercontent.com/tripal/tripal/7.x-3.x/tripal/theme/images/tripal_logo.png)
 
-Tripal is a toolkit for construction of online biological (genetics, genomics,
-breeding, etc), community database, and is a member of the 
-[GMOD](http://www.gmod.org) family of tools. Tripal v3 provides by default
-integration with the [GMOD Chado database](http://gmod.org/wiki/Chado_-_Getting_Started).
-Tripal's primary goals are: 
+Tripal is a toolkit for constructing online biological (genetics, genomics, breeding, etc.) community databases, and Tripal is a member of the [GMOD](http://www.gmod.org) family of tools. **Tripal v3** provides integration with the [GMOD Chado database](http://gmod.org/wiki/Chado_-_Getting_Started) by default.
 
-Genomics, genetics, breeding and other biological data are increasingly complicated and time consuming to publish online for other researchers to search, browse and make discoveries.   Tripal provides a framework to reduce the complexity of creating such a site, and provides access to a community of similar groups that share community-standards, and interact to address questions and learn best practices for sharing, storing and visualizing complex biological data.
+Genetics, genomics, breeding, and other biological data are increasingly complicated and time-consuming to publish online for others to search, browse and make discoveries with. Tripal provides a framework to reduce the complexity of creating such a site, and provides access to a community of similar groups that share community-standards. The users of Tripal are encouraged to interact to address questions and learn the best practices for sharing, storing, and visualizing complex biological data.
 
-1. Provide a framework for those with genomic, genetic and breeding data that
-can facility creation of an online site for display, search and visualization.
-2. To use community-derived standards and ontologies to facility continuity
-between sites which in turn fosters collaboration and sharing 
-3. Provide an out-of-the-box setup for a genomics site for those who simply 
-want to put new genome assemblies and annotations online.
-4. Provide Application Programming Interfaces (APIs) for complete customization 
-such that more advanced displays, look-and-feel, and new functionality
-can be supported. 
+The primary goals of Tripal are to:
+1.	Provide a framework for creating sites that allow display, search, and visualization of biological data, including genetics, genomics, and breeding data;
+2.	Use community-derived standards and ontologies to facilitate continuity between sites and foster collaboration and sharing;
+3.	Provide an out-of-the-box setup for a genomics site to put new genome assemblies and annotations online; and
+4.	Provide Application Programming Interfaces (APIs) to support customized displays, look-and-feel, and new functionality.
 
 
 # Features
@@ -59,55 +58,62 @@ are available in Tripal v3.
 
 
 # Installation
-Please follow the instructions in the online Tripal User's Guide:
-http://tripal.info/tutorials/v2.0/installation
+Please follow the instructions in the online Tripal User's Guide for [Tripal v2](https://tripal.info/tutorials/v2.x/installation) or [Tripal v3](https://tripal.readthedocs.io/en/latest/user_guide.html).
 
 
 # Upgrade from Tripal v2.x to v3.x
-Note:  Upgrade can only be performed using 'drush' command.
+Please follow the [Upgrade Instructions](https://tripal.readthedocs.io/en/latest/user_guide/install_tripal/upgrade_from_tripal2.html) in the Tripal v3 User's Guide
 
-Note: Deprecated API functions from Tripal v1.x have been removed from Tripal
-v3.  Therefore, use of deprecated API functions in templates or custom 
-modules may cause a white screen of death (WSOD).  Check teh server logs if this
-occurs to find where deprecated functions may be used.
 
-Upgrade Instructions:
+# Customization
+Tripal can be used “as is” but also allows for complete customization.
+PHP-based template files are provided for all data types to allow for 
+precise customizations as required by the community. A well-developed 
+Tripal API provides a uniform set of variables and functions for 
+accessing any and all data within the Chado database. See the Tripal 3.x
+Developer's Handbook for additional details.
+
 
-Step 1: Put the site in maintenance mode.
+# Development Testing
 
-Step 2: Disable tripal modules. Disabling the core module will disable all
-other Tripal modules:
+To run PHP unit tests on your local system, run `composer install` to install developer-specific requirements.  Next, create a `.env` file in your `/Tests/` directory that defines the `DRUPAL_ROOT` variable, for example 
 
-  drush pm-disable tripal_core
-  
-Step 3: Remove old Tripal v2 package and replace with Tripal v3 package
-Step 4: Enable the tripal module
+```
+DRUPAL_ROOT=/var/www/html
+```
+Then run PHPUnit from your root Tripal directory.
 
-  drush pm-enable tripal
- 
-Step 5: Enable the tripal_chado module  
+PHPUnit tests will also be run in the Travis CI build.
 
-  drush pm-enable tripal_chado
-  
-Step 6:  Tripal v2 modules are now called 'legacy modules'. these are the
-modules that were disabled in step #2.  For backwards compatibility, you 
-should re-enable these modules:
+Read our [testing guidelines](tests/README.md)
 
-  drush pm-enable tripal_core, tripal_views, tripal_db, tripal_cv, \
-    tripal_analysis, tripal_organism, tripal_feature, tripal_pub, \
-    tripal_stock
+## Contributors
 
-Be sure to enable any additional modules not included in the example
-drush command above.
+Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
 
-Step 7:  Return to your Tripal site, and click the link that appears for
-preparing Chado and launch the job.
+<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
+<!-- prettier-ignore -->
+<table>
+  <tr>
+    <td align="center"><a href="https://github.com/spficklin"><img src="https://avatars0.githubusercontent.com/u/1719352?v=4" width="100px;" alt="Stephen Ficklin"/><br /><sub><b>Stephen Ficklin</b></sub></a><br /><a href="https://github.com/tripal/tripal/commits?author=spficklin" title="Code">💻</a> <a href="#eventOrganizing-spficklin" title="Event Organizing">📋</a> <a href="https://github.com/tripal/tripal/commits?author=spficklin" title="Documentation">📖</a> <a href="#projectManagement-spficklin" title="Project Management">📆</a> <a href="#review-spficklin" title="Reviewed Pull Requests">👀</a></td>
+    <td align="center"><a href="http://www.bradfordcondon.com/"><img src="https://avatars2.githubusercontent.com/u/7063154?v=4" width="100px;" alt="Bradford Condon"/><br /><sub><b>Bradford Condon</b></sub></a><br /><a href="https://github.com/tripal/tripal/commits?author=bradfordcondon" title="Code">💻</a> <a href="https://github.com/tripal/tripal/commits?author=bradfordcondon" title="Documentation">📖</a> <a href="#projectManagement-bradfordcondon" title="Project Management">📆</a> <a href="#eventOrganizing-bradfordcondon" title="Event Organizing">📋</a> <a href="#review-bradfordcondon" title="Reviewed Pull Requests">👀</a></td>
+    <td align="center"><a href="https://laceysanderson.github.io/"><img src="https://avatars3.githubusercontent.com/u/1566301?v=4" width="100px;" alt="Lacey-Anne Sanderson"/><br /><sub><b>Lacey-Anne Sanderson</b></sub></a><br /><a href="https://github.com/tripal/tripal/commits?author=laceysanderson" title="Code">💻</a> <a href="https://github.com/tripal/tripal/commits?author=laceysanderson" title="Documentation">📖</a> <a href="#projectManagement-laceysanderson" title="Project Management">📆</a> <a href="#eventOrganizing-laceysanderson" title="Event Organizing">📋</a> <a href="#review-laceysanderson" title="Reviewed Pull Requests">👀</a></td>
+    <td align="center"><a href="https://github.com/chunhuaicheng"><img src="https://avatars2.githubusercontent.com/u/14333886?v=4" width="100px;" alt="chunhuaicheng"/><br /><sub><b>chunhuaicheng</b></sub></a><br /><a href="https://github.com/tripal/tripal/commits?author=chunhuaicheng" title="Code">💻</a></td>
+    <td align="center"><a href="https://github.com/shawnawsu"><img src="https://avatars1.githubusercontent.com/u/24374002?v=4" width="100px;" alt="Shawna"/><br /><sub><b>Shawna</b></sub></a><br /><a href="https://github.com/tripal/tripal/commits?author=shawnawsu" title="Code">💻</a> <a href="#content-shawnawsu" title="Content">🖋</a> <a href="https://github.com/tripal/tripal/commits?author=shawnawsu" title="Documentation">📖</a> <a href="#review-shawnawsu" title="Reviewed Pull Requests">👀</a></td>
+    <td align="center"><a href="https://github.com/mboudet"><img src="https://avatars0.githubusercontent.com/u/17642511?v=4" width="100px;" alt="mboudet"/><br /><sub><b>mboudet</b></sub></a><br /><a href="https://github.com/tripal/tripal/issues?q=author%3Amboudet" title="Bug reports">🐛</a></td>
+    <td align="center"><a href="https://github.com/guignonv"><img src="https://avatars1.githubusercontent.com/u/7290244?v=4" width="100px;" alt="Valentin Guignon"/><br /><sub><b>Valentin Guignon</b></sub></a><br /><a href="https://github.com/tripal/tripal/issues?q=author%3Aguignonv" title="Bug reports">🐛</a></td>
+  </tr>
+  <tr>
+    <td align="center"><a href="https://github.com/mestato"><img src="https://avatars1.githubusercontent.com/u/508122?v=4" width="100px;" alt="Meg Staton"/><br /><sub><b>Meg Staton</b></sub></a><br /><a href="#fundingFinding-mestato" title="Funding Finding">🔍</a> <a href="#eventOrganizing-mestato" title="Event Organizing">📋</a></td>
+    <td align="center"><a href="https://github.com/abretaud"><img src="https://avatars3.githubusercontent.com/u/238755?v=4" width="100px;" alt="Anthony Bretaudeau"/><br /><sub><b>Anthony Bretaudeau</b></sub></a><br /><a href="https://github.com/tripal/tripal/commits?author=abretaud" title="Code">💻</a></td>
+    <td align="center"><a href="https://github.com/colthom"><img src="https://avatars0.githubusercontent.com/u/17720870?v=4" width="100px;" alt="colthom"/><br /><sub><b>colthom</b></sub></a><br /><a href="https://github.com/tripal/tripal/commits?author=colthom" title="Documentation">📖</a></td>
+    <td align="center"><a href="http://almsaeedstudio.com"><img src="https://avatars2.githubusercontent.com/u/1512664?v=4" width="100px;" alt="Abdullah Almsaeed"/><br /><sub><b>Abdullah Almsaeed</b></sub></a><br /><a href="https://github.com/tripal/tripal/commits?author=almasaeed2010" title="Code">💻</a> <a href="#review-almasaeed2010" title="Reviewed Pull Requests">👀</a></td>
+    <td align="center"><a href="https://github.com/btski"><img src="https://avatars1.githubusercontent.com/u/32686196?v=4" width="100px;" alt="btski"/><br /><sub><b>btski</b></sub></a><br /><a href="#question-btski" title="Answering Questions">💬</a></td>
+    <td align="center"><a href="https://github.com/ekcannon"><img src="https://avatars0.githubusercontent.com/u/3409057?v=4" width="100px;" alt="ekcannon"/><br /><sub><b>ekcannon</b></sub></a><br /><a href="#ideas-ekcannon" title="Ideas, Planning, & Feedback">🤔</a> <a href="#eventOrganizing-ekcannon" title="Event Organizing">📋</a></td>
+    <td align="center"><a href="https://github.com/jlwegrzyn"><img src="https://avatars1.githubusercontent.com/u/50996590?v=4" width="100px;" alt="jlwegrzyn"/><br /><sub><b>jlwegrzyn</b></sub></a><br /><a href="#fundingFinding-jlwegrzyn" title="Funding Finding">🔍</a></td>
+  </tr>
+</table>
 
+<!-- ALL-CONTRIBUTORS-LIST:END -->
 
-# Customization
-Tripal can be used “as is” but also allows for complete customization.
-PHP-based template files are provided for all data types to allow for 
-precise customizations as required by the community. A well-developed 
-Tripal API provides a uniform set of variables and functions for 
-accessing any and all data within the Chado database. See the Tripal 3.x
-Developer's Handbook for additional details.
+This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!

+ 15 - 0
composer.json

@@ -0,0 +1,15 @@
+{
+  "name": "tripal",
+  "description": "Tripal is an toolkit to facilitate construction of online genomic, genetic (and other biological) websites.",
+  "require-dev": {
+    "doctrine/instantiator": "1.0.*",
+    "statonlab/tripal-test-suite": "1.*"
+  },
+  "autoload": {
+    "files": [
+      "tests/TripalFieldTestHelper.php"
+    ]
+  },
+  "require": {
+  }
+}

+ 2206 - 0
composer.lock

@@ -0,0 +1,2206 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "fd3ab0a8fd06e0532a7764f20a9aa52b",
+    "packages": [],
+    "packages-dev": [
+        {
+            "name": "doctrine/instantiator",
+            "version": "1.0.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/instantiator.git",
+                "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
+                "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3,<8.0-DEV"
+            },
+            "require-dev": {
+                "athletic/athletic": "~0.1.8",
+                "ext-pdo": "*",
+                "ext-phar": "*",
+                "phpunit/phpunit": "~4.0",
+                "squizlabs/php_codesniffer": "~2.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Marco Pivetta",
+                    "email": "ocramius@gmail.com",
+                    "homepage": "http://ocramius.github.com/"
+                }
+            ],
+            "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
+            "homepage": "https://github.com/doctrine/instantiator",
+            "keywords": [
+                "constructor",
+                "instantiate"
+            ],
+            "time": "2015-06-14T21:17:01+00:00"
+        },
+        {
+            "name": "fzaninotto/faker",
+            "version": "v1.8.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/fzaninotto/Faker.git",
+                "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/f72816b43e74063c8b10357394b6bba8cb1c10de",
+                "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3.3 || ^7.0"
+            },
+            "require-dev": {
+                "ext-intl": "*",
+                "phpunit/phpunit": "^4.8.35 || ^5.7",
+                "squizlabs/php_codesniffer": "^1.5"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.8-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Faker\\": "src/Faker/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "François Zaninotto"
+                }
+            ],
+            "description": "Faker is a PHP library that generates fake data for you.",
+            "keywords": [
+                "data",
+                "faker",
+                "fixtures"
+            ],
+            "time": "2018-07-12T10:23:15+00:00"
+        },
+        {
+            "name": "guzzlehttp/guzzle",
+            "version": "6.3.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/guzzle.git",
+                "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba",
+                "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba",
+                "shasum": ""
+            },
+            "require": {
+                "guzzlehttp/promises": "^1.0",
+                "guzzlehttp/psr7": "^1.4",
+                "php": ">=5.5"
+            },
+            "require-dev": {
+                "ext-curl": "*",
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
+                "psr/log": "^1.0"
+            },
+            "suggest": {
+                "psr/log": "Required for using the Log middleware"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "6.3-dev"
+                }
+            },
+            "autoload": {
+                "files": [
+                    "src/functions_include.php"
+                ],
+                "psr-4": {
+                    "GuzzleHttp\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                }
+            ],
+            "description": "Guzzle is a PHP HTTP client library",
+            "homepage": "http://guzzlephp.org/",
+            "keywords": [
+                "client",
+                "curl",
+                "framework",
+                "http",
+                "http client",
+                "rest",
+                "web service"
+            ],
+            "time": "2018-04-22T15:46:56+00:00"
+        },
+        {
+            "name": "guzzlehttp/promises",
+            "version": "v1.3.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/promises.git",
+                "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
+                "reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\Promise\\": "src/"
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                }
+            ],
+            "description": "Guzzle promises library",
+            "keywords": [
+                "promise"
+            ],
+            "time": "2016-12-20T10:07:11+00:00"
+        },
+        {
+            "name": "guzzlehttp/psr7",
+            "version": "1.6.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/guzzle/psr7.git",
+                "reference": "239400de7a173fe9901b9ac7c06497751f00727a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a",
+                "reference": "239400de7a173fe9901b9ac7c06497751f00727a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.4.0",
+                "psr/http-message": "~1.0",
+                "ralouphie/getallheaders": "^2.0.5 || ^3.0.0"
+            },
+            "provide": {
+                "psr/http-message-implementation": "1.0"
+            },
+            "require-dev": {
+                "ext-zlib": "*",
+                "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8"
+            },
+            "suggest": {
+                "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.6-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "GuzzleHttp\\Psr7\\": "src/"
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Michael Dowling",
+                    "email": "mtdowling@gmail.com",
+                    "homepage": "https://github.com/mtdowling"
+                },
+                {
+                    "name": "Tobias Schultze",
+                    "homepage": "https://github.com/Tobion"
+                }
+            ],
+            "description": "PSR-7 message implementation that also provides common utility methods",
+            "keywords": [
+                "http",
+                "message",
+                "psr-7",
+                "request",
+                "response",
+                "stream",
+                "uri",
+                "url"
+            ],
+            "time": "2019-07-01T23:21:34+00:00"
+        },
+        {
+            "name": "myclabs/deep-copy",
+            "version": "1.9.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/myclabs/DeepCopy.git",
+                "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/007c053ae6f31bba39dfa19a7726f56e9763bbea",
+                "reference": "007c053ae6f31bba39dfa19a7726f56e9763bbea",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1"
+            },
+            "replace": {
+                "myclabs/deep-copy": "self.version"
+            },
+            "require-dev": {
+                "doctrine/collections": "^1.0",
+                "doctrine/common": "^2.6",
+                "phpunit/phpunit": "^7.1"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "DeepCopy\\": "src/DeepCopy/"
+                },
+                "files": [
+                    "src/DeepCopy/deep_copy.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "Create deep copies (clones) of your objects",
+            "keywords": [
+                "clone",
+                "copy",
+                "duplicate",
+                "object",
+                "object graph"
+            ],
+            "time": "2019-08-09T12:45:53+00:00"
+        },
+        {
+            "name": "phar-io/manifest",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phar-io/manifest.git",
+                "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phar-io/manifest/zipball/2df402786ab5368a0169091f61a7c1e0eb6852d0",
+                "reference": "2df402786ab5368a0169091f61a7c1e0eb6852d0",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-phar": "*",
+                "phar-io/version": "^1.0.1",
+                "php": "^5.6 || ^7.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Arne Blankerts",
+                    "email": "arne@blankerts.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Heuer",
+                    "email": "sebastian@phpeople.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)",
+            "time": "2017-03-05T18:14:27+00:00"
+        },
+        {
+            "name": "phar-io/version",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phar-io/version.git",
+                "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phar-io/version/zipball/a70c0ced4be299a63d32fa96d9281d03e94041df",
+                "reference": "a70c0ced4be299a63d32fa96d9281d03e94041df",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.6 || ^7.0"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Arne Blankerts",
+                    "email": "arne@blankerts.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Heuer",
+                    "email": "sebastian@phpeople.de",
+                    "role": "Developer"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "Developer"
+                }
+            ],
+            "description": "Library for handling version information and constraints",
+            "time": "2017-03-05T17:38:23+00:00"
+        },
+        {
+            "name": "phpdocumentor/reflection-common",
+            "version": "2.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
+                "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a",
+                "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "~6"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jaap van Otterdijk",
+                    "email": "opensource@ijaap.nl"
+                }
+            ],
+            "description": "Common reflection classes used by phpdocumentor to reflect the code structure",
+            "homepage": "http://www.phpdoc.org",
+            "keywords": [
+                "FQSEN",
+                "phpDocumentor",
+                "phpdoc",
+                "reflection",
+                "static analysis"
+            ],
+            "time": "2018-08-07T13:53:10+00:00"
+        },
+        {
+            "name": "phpdocumentor/reflection-docblock",
+            "version": "4.3.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
+                "reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
+                "reference": "b83ff7cfcfee7827e1e78b637a5904fe6a96698e",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0",
+                "phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0",
+                "phpdocumentor/type-resolver": "~0.4 || ^1.0.0",
+                "webmozart/assert": "^1.0"
+            },
+            "require-dev": {
+                "doctrine/instantiator": "^1.0.5",
+                "mockery/mockery": "^1.0",
+                "phpunit/phpunit": "^6.4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": [
+                        "src/"
+                    ]
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mike van Riel",
+                    "email": "me@mikevanriel.com"
+                }
+            ],
+            "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
+            "time": "2019-09-12T14:27:41+00:00"
+        },
+        {
+            "name": "phpdocumentor/type-resolver",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpDocumentor/TypeResolver.git",
+                "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/2e32a6d48972b2c1976ed5d8967145b6cec4a4a9",
+                "reference": "2e32a6d48972b2c1976ed5d8967145b6cec4a4a9",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1",
+                "phpdocumentor/reflection-common": "^2.0"
+            },
+            "require-dev": {
+                "ext-tokenizer": "^7.1",
+                "mockery/mockery": "~1",
+                "phpunit/phpunit": "^7.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "phpDocumentor\\Reflection\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Mike van Riel",
+                    "email": "me@mikevanriel.com"
+                }
+            ],
+            "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
+            "time": "2019-08-22T18:11:29+00:00"
+        },
+        {
+            "name": "phpspec/prophecy",
+            "version": "1.8.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/phpspec/prophecy.git",
+                "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76",
+                "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/instantiator": "^1.0.2",
+                "php": "^5.3|^7.0",
+                "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
+                "sebastian/comparator": "^1.1|^2.0|^3.0",
+                "sebastian/recursion-context": "^1.0|^2.0|^3.0"
+            },
+            "require-dev": {
+                "phpspec/phpspec": "^2.5|^3.2",
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.8.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Prophecy\\": "src/Prophecy"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Konstantin Kudryashov",
+                    "email": "ever.zet@gmail.com",
+                    "homepage": "http://everzet.com"
+                },
+                {
+                    "name": "Marcello Duarte",
+                    "email": "marcello.duarte@gmail.com"
+                }
+            ],
+            "description": "Highly opinionated mocking framework for PHP 5.3+",
+            "homepage": "https://github.com/phpspec/prophecy",
+            "keywords": [
+                "Double",
+                "Dummy",
+                "fake",
+                "mock",
+                "spy",
+                "stub"
+            ],
+            "time": "2019-06-13T12:50:23+00:00"
+        },
+        {
+            "name": "phpunit/php-code-coverage",
+            "version": "6.0.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+                "reference": "4cab20a326d14de7575a8e235c70d879b569a57a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4cab20a326d14de7575a8e235c70d879b569a57a",
+                "reference": "4cab20a326d14de7575a8e235c70d879b569a57a",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-xmlwriter": "*",
+                "php": "^7.1",
+                "phpunit/php-file-iterator": "^1.4.2",
+                "phpunit/php-text-template": "^1.2.1",
+                "phpunit/php-token-stream": "^3.0",
+                "sebastian/code-unit-reverse-lookup": "^1.0.1",
+                "sebastian/environment": "^3.1",
+                "sebastian/version": "^2.0.1",
+                "theseer/tokenizer": "^1.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7.0"
+            },
+            "suggest": {
+                "ext-xdebug": "^2.6.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "6.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.",
+            "homepage": "https://github.com/sebastianbergmann/php-code-coverage",
+            "keywords": [
+                "coverage",
+                "testing",
+                "xunit"
+            ],
+            "time": "2018-05-28T11:49:20+00:00"
+        },
+        {
+            "name": "phpunit/php-file-iterator",
+            "version": "1.4.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+                "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4",
+                "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.4.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sb@sebastian-bergmann.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "FilterIterator implementation that filters files based on a list of suffixes.",
+            "homepage": "https://github.com/sebastianbergmann/php-file-iterator/",
+            "keywords": [
+                "filesystem",
+                "iterator"
+            ],
+            "time": "2017-11-27T13:52:08+00:00"
+        },
+        {
+            "name": "phpunit/php-text-template",
+            "version": "1.2.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-text-template.git",
+                "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
+                "reference": "31f8b717e51d9a2afca6c9f046f5d69fc27c8686",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Simple template engine.",
+            "homepage": "https://github.com/sebastianbergmann/php-text-template/",
+            "keywords": [
+                "template"
+            ],
+            "time": "2015-06-21T13:50:34+00:00"
+        },
+        {
+            "name": "phpunit/php-timer",
+            "version": "2.1.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-timer.git",
+                "reference": "1038454804406b0b5f5f520358e78c1c2f71501e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/1038454804406b0b5f5f520358e78c1c2f71501e",
+                "reference": "1038454804406b0b5f5f520358e78c1c2f71501e",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.1-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Utility class for timing",
+            "homepage": "https://github.com/sebastianbergmann/php-timer/",
+            "keywords": [
+                "timer"
+            ],
+            "time": "2019-06-07T04:22:29+00:00"
+        },
+        {
+            "name": "phpunit/php-token-stream",
+            "version": "3.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/php-token-stream.git",
+                "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff",
+                "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff",
+                "shasum": ""
+            },
+            "require": {
+                "ext-tokenizer": "*",
+                "php": "^7.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.1-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Wrapper around PHP's tokenizer extension.",
+            "homepage": "https://github.com/sebastianbergmann/php-token-stream/",
+            "keywords": [
+                "tokenizer"
+            ],
+            "time": "2019-09-17T06:23:10+00:00"
+        },
+        {
+            "name": "phpunit/phpunit",
+            "version": "7.1.5",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/phpunit.git",
+                "reference": "ca64dba53b88aba6af32aebc6b388068db95c435"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ca64dba53b88aba6af32aebc6b388068db95c435",
+                "reference": "ca64dba53b88aba6af32aebc6b388068db95c435",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-json": "*",
+                "ext-libxml": "*",
+                "ext-mbstring": "*",
+                "ext-xml": "*",
+                "myclabs/deep-copy": "^1.6.1",
+                "phar-io/manifest": "^1.0.1",
+                "phar-io/version": "^1.0",
+                "php": "^7.1",
+                "phpspec/prophecy": "^1.7",
+                "phpunit/php-code-coverage": "^6.0.1",
+                "phpunit/php-file-iterator": "^1.4.3",
+                "phpunit/php-text-template": "^1.2.1",
+                "phpunit/php-timer": "^2.0",
+                "phpunit/phpunit-mock-objects": "^6.1.1",
+                "sebastian/comparator": "^3.0",
+                "sebastian/diff": "^3.0",
+                "sebastian/environment": "^3.1",
+                "sebastian/exporter": "^3.1",
+                "sebastian/global-state": "^2.0",
+                "sebastian/object-enumerator": "^3.0.3",
+                "sebastian/resource-operations": "^1.0",
+                "sebastian/version": "^2.0.1"
+            },
+            "require-dev": {
+                "ext-pdo": "*"
+            },
+            "suggest": {
+                "ext-xdebug": "*",
+                "phpunit/php-invoker": "^2.0"
+            },
+            "bin": [
+                "phpunit"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "7.1-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "The PHP Unit Testing framework.",
+            "homepage": "https://phpunit.de/",
+            "keywords": [
+                "phpunit",
+                "testing",
+                "xunit"
+            ],
+            "time": "2018-04-29T15:09:19+00:00"
+        },
+        {
+            "name": "phpunit/phpunit-mock-objects",
+            "version": "6.1.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git",
+                "reference": "f9756fd4f43f014cb2dca98deeaaa8ce5500a36e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/f9756fd4f43f014cb2dca98deeaaa8ce5500a36e",
+                "reference": "f9756fd4f43f014cb2dca98deeaaa8ce5500a36e",
+                "shasum": ""
+            },
+            "require": {
+                "doctrine/instantiator": "^1.0.5",
+                "php": "^7.1",
+                "phpunit/php-text-template": "^1.2.1",
+                "sebastian/exporter": "^3.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7.0"
+            },
+            "suggest": {
+                "ext-soap": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "6.1-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Mock Object library for PHPUnit",
+            "homepage": "https://github.com/sebastianbergmann/phpunit-mock-objects/",
+            "keywords": [
+                "mock",
+                "xunit"
+            ],
+            "abandoned": true,
+            "time": "2018-05-29T13:54:20+00:00"
+        },
+        {
+            "name": "psr/container",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/container.git",
+                "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+                "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Container\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common Container Interface (PHP FIG PSR-11)",
+            "homepage": "https://github.com/php-fig/container",
+            "keywords": [
+                "PSR-11",
+                "container",
+                "container-interface",
+                "container-interop",
+                "psr"
+            ],
+            "time": "2017-02-14T16:28:37+00:00"
+        },
+        {
+            "name": "psr/http-message",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/http-message.git",
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Http\\Message\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for HTTP messages",
+            "homepage": "https://github.com/php-fig/http-message",
+            "keywords": [
+                "http",
+                "http-message",
+                "psr",
+                "psr-7",
+                "request",
+                "response"
+            ],
+            "time": "2016-08-06T14:39:51+00:00"
+        },
+        {
+            "name": "ralouphie/getallheaders",
+            "version": "3.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/ralouphie/getallheaders.git",
+                "reference": "120b605dfeb996808c31b6477290a714d356e822"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822",
+                "reference": "120b605dfeb996808c31b6477290a714d356e822",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.6"
+            },
+            "require-dev": {
+                "php-coveralls/php-coveralls": "^2.1",
+                "phpunit/phpunit": "^5 || ^6.5"
+            },
+            "type": "library",
+            "autoload": {
+                "files": [
+                    "src/getallheaders.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ralph Khattar",
+                    "email": "ralph.khattar@gmail.com"
+                }
+            ],
+            "description": "A polyfill for getallheaders.",
+            "time": "2019-03-08T08:55:37+00:00"
+        },
+        {
+            "name": "sebastian/code-unit-reverse-lookup",
+            "version": "1.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git",
+                "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
+                "reference": "4419fcdb5eabb9caa61a27c7a1db532a6b55dd18",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.6 || ^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^5.7 || ^6.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Looks up which function or method a line of code belongs to",
+            "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
+            "time": "2017-03-04T06:30:41+00:00"
+        },
+        {
+            "name": "sebastian/comparator",
+            "version": "3.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/comparator.git",
+                "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
+                "reference": "5de4fc177adf9bce8df98d8d141a7559d7ccf6da",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1",
+                "sebastian/diff": "^3.0",
+                "sebastian/exporter": "^3.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Volker Dusch",
+                    "email": "github@wallbash.com"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@2bepublished.at"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Provides the functionality to compare PHP values for equality",
+            "homepage": "https://github.com/sebastianbergmann/comparator",
+            "keywords": [
+                "comparator",
+                "compare",
+                "equality"
+            ],
+            "time": "2018-07-12T15:12:46+00:00"
+        },
+        {
+            "name": "sebastian/diff",
+            "version": "3.0.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/diff.git",
+                "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
+                "reference": "720fcc7e9b5cf384ea68d9d930d480907a0c1a29",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^7.5 || ^8.0",
+                "symfony/process": "^2 || ^3.3 || ^4"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Kore Nordmann",
+                    "email": "mail@kore-nordmann.de"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Diff implementation",
+            "homepage": "https://github.com/sebastianbergmann/diff",
+            "keywords": [
+                "diff",
+                "udiff",
+                "unidiff",
+                "unified diff"
+            ],
+            "time": "2019-02-04T06:01:07+00:00"
+        },
+        {
+            "name": "sebastian/environment",
+            "version": "3.1.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/environment.git",
+                "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/cd0871b3975fb7fc44d11314fd1ee20925fce4f5",
+                "reference": "cd0871b3975fb7fc44d11314fd1ee20925fce4f5",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.1"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.1.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Provides functionality to handle HHVM/PHP environments",
+            "homepage": "http://www.github.com/sebastianbergmann/environment",
+            "keywords": [
+                "Xdebug",
+                "environment",
+                "hhvm"
+            ],
+            "time": "2017-07-01T08:51:00+00:00"
+        },
+        {
+            "name": "sebastian/exporter",
+            "version": "3.1.2",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/exporter.git",
+                "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/68609e1261d215ea5b21b7987539cbfbe156ec3e",
+                "reference": "68609e1261d215ea5b21b7987539cbfbe156ec3e",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0",
+                "sebastian/recursion-context": "^3.0"
+            },
+            "require-dev": {
+                "ext-mbstring": "*",
+                "phpunit/phpunit": "^6.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.1.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Volker Dusch",
+                    "email": "github@wallbash.com"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net"
+                },
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@gmail.com"
+                }
+            ],
+            "description": "Provides the functionality to export PHP variables for visualization",
+            "homepage": "http://www.github.com/sebastianbergmann/exporter",
+            "keywords": [
+                "export",
+                "exporter"
+            ],
+            "time": "2019-09-14T09:02:43+00:00"
+        },
+        {
+            "name": "sebastian/global-state",
+            "version": "2.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/global-state.git",
+                "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4",
+                "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.0"
+            },
+            "suggest": {
+                "ext-uopz": "*"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Snapshotting of global state",
+            "homepage": "http://www.github.com/sebastianbergmann/global-state",
+            "keywords": [
+                "global state"
+            ],
+            "time": "2017-04-27T15:39:26+00:00"
+        },
+        {
+            "name": "sebastian/object-enumerator",
+            "version": "3.0.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+                "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/7cfd9e65d11ffb5af41198476395774d4c8a84c5",
+                "reference": "7cfd9e65d11ffb5af41198476395774d4c8a84c5",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0",
+                "sebastian/object-reflector": "^1.1.1",
+                "sebastian/recursion-context": "^3.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+            "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+            "time": "2017-08-03T12:35:26+00:00"
+        },
+        {
+            "name": "sebastian/object-reflector",
+            "version": "1.1.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/object-reflector.git",
+                "reference": "773f97c67f28de00d397be301821b06708fca0be"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/773f97c67f28de00d397be301821b06708fca0be",
+                "reference": "773f97c67f28de00d397be301821b06708fca0be",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Allows reflection of object attributes, including inherited and non-public ones",
+            "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+            "time": "2017-03-29T09:07:27+00:00"
+        },
+        {
+            "name": "sebastian/recursion-context",
+            "version": "3.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/recursion-context.git",
+                "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
+                "reference": "5b0cd723502bac3b006cbf3dbf7a1e3fcefe4fa8",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Jeff Welch",
+                    "email": "whatthejeff@gmail.com"
+                },
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                },
+                {
+                    "name": "Adam Harvey",
+                    "email": "aharvey@php.net"
+                }
+            ],
+            "description": "Provides functionality to recursively process PHP variables",
+            "homepage": "http://www.github.com/sebastianbergmann/recursion-context",
+            "time": "2017-03-03T06:23:57+00:00"
+        },
+        {
+            "name": "sebastian/resource-operations",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/resource-operations.git",
+                "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
+                "reference": "ce990bb21759f94aeafd30209e8cfcdfa8bc3f52",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.6.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de"
+                }
+            ],
+            "description": "Provides a list of PHP built-in functions that operate on resources",
+            "homepage": "https://www.github.com/sebastianbergmann/resource-operations",
+            "time": "2015-07-28T20:34:47+00:00"
+        },
+        {
+            "name": "sebastian/version",
+            "version": "2.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/sebastianbergmann/version.git",
+                "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/99732be0ddb3361e16ad77b68ba41efc8e979019",
+                "reference": "99732be0ddb3361e16ad77b68ba41efc8e979019",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.6"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.0.x-dev"
+                }
+            },
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Sebastian Bergmann",
+                    "email": "sebastian@phpunit.de",
+                    "role": "lead"
+                }
+            ],
+            "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+            "homepage": "https://github.com/sebastianbergmann/version",
+            "time": "2016-10-03T07:35:21+00:00"
+        },
+        {
+            "name": "statonlab/tripal-test-suite",
+            "version": "1.6.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/tripal/TripalTestSuite.git",
+                "reference": "3e877af0204c59b9aa6b7ef0324ca4b985a7e3b4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/tripal/TripalTestSuite/zipball/3e877af0204c59b9aa6b7ef0324ca4b985a7e3b4",
+                "reference": "3e877af0204c59b9aa6b7ef0324ca4b985a7e3b4",
+                "shasum": ""
+            },
+            "require": {
+                "fzaninotto/faker": "^1.7",
+                "guzzlehttp/guzzle": "^6.3",
+                "phpunit/phpunit": "^5 || ^6 || ^7.0",
+                "symfony/console": "^3 || ^4.0"
+            },
+            "bin": [
+                "tripaltest"
+            ],
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "StatonLab\\TripalTestSuite\\": "src/"
+                },
+                "files": [
+                    "src/Helpers/helpers.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "GPL-3.0"
+            ],
+            "authors": [
+                {
+                    "name": "Abdullah Almsaeed",
+                    "email": "aalmsaee@utk.edu"
+                },
+                {
+                    "name": "Bradford Condon",
+                    "email": "bcondon@utk.edu"
+                }
+            ],
+            "time": "2019-04-09T18:52:51+00:00"
+        },
+        {
+            "name": "symfony/console",
+            "version": "v4.3.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/console.git",
+                "reference": "de63799239b3881b8a08f8481b22348f77ed7b36"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/console/zipball/de63799239b3881b8a08f8481b22348f77ed7b36",
+                "reference": "de63799239b3881b8a08f8481b22348f77ed7b36",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1.3",
+                "symfony/polyfill-mbstring": "~1.0",
+                "symfony/polyfill-php73": "^1.8",
+                "symfony/service-contracts": "^1.1"
+            },
+            "conflict": {
+                "symfony/dependency-injection": "<3.4",
+                "symfony/event-dispatcher": "<4.3",
+                "symfony/process": "<3.3"
+            },
+            "provide": {
+                "psr/log-implementation": "1.0"
+            },
+            "require-dev": {
+                "psr/log": "~1.0",
+                "symfony/config": "~3.4|~4.0",
+                "symfony/dependency-injection": "~3.4|~4.0",
+                "symfony/event-dispatcher": "^4.3",
+                "symfony/lock": "~3.4|~4.0",
+                "symfony/process": "~3.4|~4.0",
+                "symfony/var-dumper": "^4.3"
+            },
+            "suggest": {
+                "psr/log": "For using the console logger",
+                "symfony/event-dispatcher": "",
+                "symfony/lock": "",
+                "symfony/process": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "4.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Console\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Console Component",
+            "homepage": "https://symfony.com",
+            "time": "2019-08-26T08:26:39+00:00"
+        },
+        {
+            "name": "symfony/polyfill-ctype",
+            "version": "v1.12.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-ctype.git",
+                "reference": "550ebaac289296ce228a706d0867afc34687e3f4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/550ebaac289296ce228a706d0867afc34687e3f4",
+                "reference": "550ebaac289296ce228a706d0867afc34687e3f4",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "suggest": {
+                "ext-ctype": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.12-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Ctype\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Gert de Pagter",
+                    "email": "BackEndTea@gmail.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for ctype functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "ctype",
+                "polyfill",
+                "portable"
+            ],
+            "time": "2019-08-06T08:03:45+00:00"
+        },
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "v1.12.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git",
+                "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/b42a2f66e8f1b15ccf25652c3424265923eb4f17",
+                "reference": "b42a2f66e8f1b15ccf25652c3424265923eb4f17",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.12-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for the Mbstring extension",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "mbstring",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2019-08-06T08:03:45+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php73",
+            "version": "v1.12.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php73.git",
+                "reference": "2ceb49eaccb9352bff54d22570276bb75ba4a188"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/2ceb49eaccb9352bff54d22570276bb75ba4a188",
+                "reference": "2ceb49eaccb9352bff54d22570276bb75ba4a188",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.12-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php73\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ],
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2019-08-06T08:03:45+00:00"
+        },
+        {
+            "name": "symfony/service-contracts",
+            "version": "v1.1.6",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/service-contracts.git",
+                "reference": "ea7263d6b6d5f798b56a45a5b8d686725f2719a3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ea7263d6b6d5f798b56a45a5b8d686725f2719a3",
+                "reference": "ea7263d6b6d5f798b56a45a5b8d686725f2719a3",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1.3",
+                "psr/container": "^1.0"
+            },
+            "suggest": {
+                "symfony/service-implementation": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Contracts\\Service\\": ""
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Generic abstractions related to writing services",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "abstractions",
+                "contracts",
+                "decoupling",
+                "interfaces",
+                "interoperability",
+                "standards"
+            ],
+            "time": "2019-08-20T14:44:19+00:00"
+        },
+        {
+            "name": "theseer/tokenizer",
+            "version": "1.1.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/theseer/tokenizer.git",
+                "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
+                "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-tokenizer": "*",
+                "ext-xmlwriter": "*",
+                "php": "^7.0"
+            },
+            "type": "library",
+            "autoload": {
+                "classmap": [
+                    "src/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Arne Blankerts",
+                    "email": "arne@blankerts.de",
+                    "role": "Developer"
+                }
+            ],
+            "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
+            "time": "2019-06-13T22:48:21+00:00"
+        },
+        {
+            "name": "webmozart/assert",
+            "version": "1.5.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/webmozart/assert.git",
+                "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/webmozart/assert/zipball/88e6d84706d09a236046d686bbea96f07b3a34f4",
+                "reference": "88e6d84706d09a236046d686bbea96f07b3a34f4",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3.3 || ^7.0",
+                "symfony/polyfill-ctype": "^1.8"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^4.8.36 || ^7.5.13"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.3-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Webmozart\\Assert\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Bernhard Schussek",
+                    "email": "bschussek@gmail.com"
+                }
+            ],
+            "description": "Assertions to validate method input/output with nice error messages.",
+            "keywords": [
+                "assert",
+                "check",
+                "validate"
+            ],
+            "time": "2019-08-24T08:43:50+00:00"
+        }
+    ],
+    "aliases": [],
+    "minimum-stability": "stable",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": [],
+    "platform-dev": []
+}

+ 20 - 0
docs/Makefile

@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line.
+SPHINXOPTS    =
+SPHINXBUILD   = sphinx-build
+SPHINXPROJ    = Tripal
+SOURCEDIR     = .
+BUILDDIR      = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

+ 1 - 0
docs/README.md

@@ -0,0 +1 @@
+Please see the [Tripal online documentation](https://tripal.readthedocs.io/en/latest/dev_guide/contributing.html) for contribution instructions.

BIN
docs/_images/TripalLogo_dark.png


BIN
docs/_images/dev_guide/data_structures/Terminology-Diagram.png


BIN
docs/_images/dev_guide/data_structures/Tripal-Semantic-web.png


BIN
docs/_images/user_guide/learn_chado/375px-ChadoLogo.png


BIN
docs/_static/hexagon_pattern.png


BIN
docs/_static/small_logoonly.png


+ 183 - 0
docs/_static/theme_overrides.css

@@ -0,0 +1,183 @@
+/* override table width restrictions
+See: https://rackerlabs.github.io/docs-rackspace/tools/rtd-tables.html
+ */
+@media screen and (min-width: 767px) {
+
+   .wy-table-responsive table td {
+      white-space: normal !important;
+   }
+
+   .wy-table-responsive {
+      overflow: visible !important;
+   }
+}
+
+/**
+ * Buttons on Extension Module pages.
+ */
+#administrative .section p:last-child a,
+  #analysis-annotation .section p:last-child a,
+  #data-loading-collection .section p:last-child a,
+  #developer-tools .section p:last-child a,
+  #in-development .section p:last-child a,
+  #third-party-integration .section p:last-child a,
+  #searching .section p:last-child a,
+  #visualization-display .section p:last-child a {
+   font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
+   display: inline-block;
+   vertical-align: middle;
+   text-align: center;
+   border-radius: 4px;
+   margin-bottom: 20px;
+   margin-right: 8px;
+   -webkit-font-smoothing: antialiased;
+   letter-spacing: 0.03em;
+   font-weight: 500;
+   line-height: 14px;
+   font-size: 14px;
+   padding: 9px 10px;
+   color: #666666;
+   background: #FFFFFF;
+   background: -webkit-linear-gradient(top, #FFFFFF, #f1f1f1);
+   border: 1px solid #c8c8c8;
+   box-shadow: 0 1px 0px #e9e9e9;
+ }
+ #administrative .section p:last-child a:hover,
+   #analysis-annotation .section p:last-child a:hover,
+   #data-loading-collection .section p:last-child a:hover,
+   #developer-tools .section p:last-child a:hover,
+   #in-development .section p:last-child a:hover,
+   #third-party-integration .section p:last-child a:hover,
+   #searching .section p:last-child a:hover,
+   #visualization-display .section p:last-child a:hover  {
+  background: -webkit-linear-gradient(top, #FFFFFF, #e6e6e6);
+}
+
+/**
+ * BRANDING
+ */
+
+/* Sidebar Title */
+.wy-side-nav-search {
+  background: none !important;
+}
+.wy-side-nav-search input[type="text"] {
+  border-color: black;
+}
+.wy-nav-side {
+  color: #CCCCCC !important;
+  background: #2D2D34 !important;
+  background-image: url("hexagon_pattern.png") !important;
+  background-repeat: repeat !important;
+  background: -webkit-linear-gradient(left, $light-grey , $gray-base) !important;
+  background: linear-gradient(to right, $light-grey , $gray-base) !important;
+}
+/* Logo */
+.fa-home::before, .icon-home::before {
+  content: url('small_logoonly.png')
+}
+/* Sidebar TOC */
+.wy-menu-vertical li.current {
+  color: #CCCCCC;
+  border-color: black;
+  background: rgba(0,0,0,0.5);
+}
+.wy-menu-vertical li.toctree-l1.current > a {
+  color: black;
+  border-color: whitesmoke;
+  background-color: whitesmoke;
+}
+.wy-menu-vertical li.toctree-l2.current > a,
+  .wy-menu-vertical li.toctree-l2.current li.toctree-l3 > a,
+  .wy-menu-vertical li.toctree-l3.current li.toctree-l4 > a {
+    color: #CCCCCC;
+    border-color: black;
+    background-color: rgba(0,0,0,0.05);
+}
+.wy-menu-vertical li.current a:hover,
+  .wy-menu-vertical li.toctree-l1.current > a:hover,
+  .wy-menu-vertical li.toctree-l2.current > a:hover,
+  .wy-menu-vertical li.toctree-l2.current li.toctree-l3 > a:hover,
+  .wy-menu-vertical li.toctree-l3.current li.toctree-l4 > a:hover {
+    color: white;
+    border-color: black;
+    font-weight: bold;
+    background-color: rgba(0,0,0);
+}
+.wy-menu-vertical li.toctree-l1 a,
+  .wy-menu-vertical li.toctree-l2 a,
+  .wy-menu-vertical li.toctree-l3 a,
+  .wy-menu-vertical li.toctree-l4 a {
+    color: #CCCCCC;
+    border-color: black;
+}
+/* Main Content */
+.wy-nav-content-wrap {
+  background: whitesmoke;
+}
+a, a:hover {
+  color: #990000;
+}
+a:visited {
+  color: #cc0000;
+}
+a.icon-home:visited {
+  color: #ffffff;
+}
+/* Code blocks */
+.highlight {
+  background: #e1f0db;
+}
+/* Default Notes */
+.wy-alert.wy-alert-info,
+  .rst-content .note,
+  .rst-content .wy-alert-info.attention,
+  .rst-content .wy-alert-info.caution,
+  .rst-content .wy-alert-info.danger,
+  .rst-content .wy-alert-info.error,
+  .rst-content .wy-alert-info.hint,
+  .rst-content .wy-alert-info.important,
+  .rst-content .wy-alert-info.tip,
+  .rst-content .wy-alert-info.warning,
+  .rst-content .seealso, .rst-content
+  .wy-alert-info.admonition-todo,
+  .rst-content .wy-alert-info.admonition {
+    background: #d9d9d9;
+}
+.wy-alert.wy-alert-info
+  .wy-alert-title,
+  .rst-content .note .wy-alert-title,
+  .rst-content .wy-alert-info.attention .wy-alert-title,
+  .rst-content .wy-alert-info.caution .wy-alert-title,
+  .rst-content .wy-alert-info.danger .wy-alert-title,
+  .rst-content .wy-alert-info.error .wy-alert-title,
+  .rst-content .wy-alert-info.hint .wy-alert-title,
+  .rst-content .wy-alert-info.important .wy-alert-title,
+  .rst-content .wy-alert-info.tip .wy-alert-title,
+  .rst-content .wy-alert-info.warning .wy-alert-title,
+  .rst-content .seealso .wy-alert-title,
+  .rst-content .wy-alert-info.admonition-todo .wy-alert-title,
+  .rst-content .wy-alert-info.admonition .wy-alert-title,
+  .wy-alert.wy-alert-info .rst-content .admonition-title,
+  .rst-content .wy-alert.wy-alert-info .admonition-title,
+  .rst-content .note .admonition-title,
+  .rst-content .wy-alert-info.attention .admonition-title,
+  .rst-content .wy-alert-info.caution .admonition-title,
+  .rst-content .wy-alert-info.danger .admonition-title,
+  .rst-content .wy-alert-info.error .admonition-title,
+  .rst-content .wy-alert-info.hint .admonition-title,
+  .rst-content .wy-alert-info.important .admonition-title,
+  .rst-content .wy-alert-info.tip .admonition-title,
+  .rst-content .wy-alert-info.warning .admonition-title,
+  .rst-content .seealso .admonition-title, .rst-content
+  .wy-alert-info.admonition-todo .admonition-title,
+  .rst-content .wy-alert-info.admonition .admonition-title {
+    background: #737373;
+}
+/* Note Warnings */
+.wy-alert.wy-alert-warning .wy-alert-title, .rst-content .wy-alert-warning.note .wy-alert-title, .rst-content .attention .wy-alert-title, .rst-content .caution .wy-alert-title, .rst-content .wy-alert-warning.danger .wy-alert-title, .rst-content .wy-alert-warning.error .wy-alert-title, .rst-content .wy-alert-warning.hint .wy-alert-title, .rst-content .wy-alert-warning.important .wy-alert-title, .rst-content .wy-alert-warning.tip .wy-alert-title, .rst-content .warning .wy-alert-title, .rst-content .wy-alert-warning.seealso .wy-alert-title, .rst-content .admonition-todo .wy-alert-title, .rst-content .wy-alert-warning.admonition .wy-alert-title, .wy-alert.wy-alert-warning .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-warning .admonition-title, .rst-content .wy-alert-warning.note .admonition-title, .rst-content .attention .admonition-title, .rst-content .caution .admonition-title, .rst-content .wy-alert-warning.danger .admonition-title, .rst-content .wy-alert-warning.error .admonition-title, .rst-content .wy-alert-warning.hint .admonition-title, .rst-content .wy-alert-warning.important .admonition-title, .rst-content .wy-alert-warning.tip .admonition-title, .rst-content .warning .admonition-title, .rst-content .wy-alert-warning.seealso .admonition-title, .rst-content .admonition-todo .admonition-title, .rst-content .wy-alert-warning.admonition .admonition-title {
+  background: #e6b800;
+}
+.wy-alert.wy-alert-warning, .rst-content .wy-alert-warning.note, .rst-content .attention, .rst-content .caution, .rst-content .wy-alert-warning.danger, .rst-content .wy-alert-warning.error, .rst-content .wy-alert-warning.hint, .rst-content .wy-alert-warning.important, .rst-content .wy-alert-warning.tip, .rst-content .warning, .rst-content .wy-alert-warning.seealso, .rst-content .admonition-todo, .rst-content .wy-alert-warning.admonition {
+  background: #FcFde6;
+}

+ 170 - 0
docs/conf.py

@@ -0,0 +1,170 @@
+# -*- coding: utf-8 -*-
+#
+# Configuration file for the Sphinx documentation builder.
+#
+# This file does only contain a selection of the most common options. For a
+# full list see the documentation:
+# http://www.sphinx-doc.org/en/master/config
+
+
+#PHP stuff
+#see: https://www.sitepoint.com/using-sphinx-for-php-project-documentation/
+
+from sphinx.highlighting import lexers
+from pygments.lexers.web import PhpLexer
+lexers["php"] = PhpLexer(startinline=True, linenos=1)
+lexers["php-annotations"] = PhpLexer(startinline=True, linenos=1)
+
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+
+# -- Project information -----------------------------------------------------
+
+project = u'Tripal'
+author = u''
+
+# The short X.Y version
+version = u''
+# The full version, including alpha/beta/rc tags
+release = u'7.x-3.x'
+
+
+# -- General configuration ---------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#
+# needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# The suffix(es) of source filenames.
+# You can specify multiple suffix as a list of string:
+#
+# source_suffix = ['.rst', '.md']
+source_suffix = '.rst'
+
+# The master toctree document.
+master_doc = 'index'
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = None
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path .
+exclude_patterns = [u'_build', 'Thumbs.db', '.DS_Store']
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = "sphinx_rtd_theme"
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#
+# html_theme_options = {}
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
+
+# Override the base theme.
+# We add the stylesheet this way so that it's loaded after the default.css
+# See https://docs.readthedocs.io/en/latest/guides/adding-custom-css.html
+def setup(app):
+    app.add_stylesheet('theme_overrides.css');  
+    
+# Custom sidebar templates, must be a dictionary that maps document names
+# to template names.
+#
+# The default sidebars (for documents that don't match any pattern) are
+# defined by theme itself.  Builtin themes are using these templates by
+# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
+# 'searchbox.html']``.
+#
+# html_sidebars = {}
+
+
+# -- Options for HTMLHelp output ---------------------------------------------
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'Tripaldoc'
+
+
+# -- Options for LaTeX output ------------------------------------------------
+
+latex_elements = {
+    # The paper size ('letterpaper' or 'a4paper').
+    #
+    # 'papersize': 'letterpaper',
+
+    # The font size ('10pt', '11pt' or '12pt').
+    #
+    # 'pointsize': '10pt',
+
+    # Additional stuff for the LaTeX preamble.
+    #
+    # 'preamble': '',
+
+    # Latex figure (float) alignment
+    #
+    # 'figure_align': 'htbp',
+}
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+    (master_doc, 'Tripal.tex', u'Tripal Documentation',
+     u'Stephen Ficklin, Lacey Sanderson, Bradford Condon et al', 'manual'),
+]
+
+
+# -- Options for manual page output ------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    (master_doc, 'tripal', u'Tripal Documentation',
+     [author], 1)
+]
+
+
+# -- Options for Texinfo output ----------------------------------------------
+
+# Grouping the document tree into Texinfo files. List of tuples
+# (source start file, target name, title, author,
+#  dir menu entry, description, category)
+texinfo_documents = [
+    (master_doc, 'Tripal', u'Tripal Documentation',
+     author, 'Tripal', 'One line description of project.',
+     'Miscellaneous'),
+]

+ 15 - 0
docs/contributing.rst

@@ -0,0 +1,15 @@
+Tripal Community
+==================
+
+
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Table of Contents
+
+
+   contributing/pull_requests
+   contributing/tests
+   contributing/documentation
+   contributing/governance
+   contributing/funding

+ 64 - 0
docs/contributing/documentation.rst

@@ -0,0 +1,64 @@
+.. _tripal_rtd:
+
+Contributing to the Documentation
+==================================
+
+The Tripal documentation is written in `**Restructured Text** <http://docutils.sourceforge.net/rst.html>`_, compiled with `Sphinx <http://www.sphinx-doc.org/en/master/usage/quickstart.html>`_, and built/hosted with `ReadTheDocs  <https://readthedocs.org/>`_.  The ``docs`` directory, when compiled, is hosted at https://tripal.readthedocs.io/en/latest/.
+
+For minor changes, you can simply `Edit the file using the Github editor <https://help.github.com/articles/editing-files-in-your-repository/>`_, which will allow you to make a Pull Request.  Once approved, your changes will be reflected in the documentation automatically!
+
+Guide
+------
+
+
+Install Sphinx
+~~~~~~~~~~~~~~~~~
+
+For minor changes, you don't need to build the documentation!  If you want to see how your changes will look on the built site, however, you will need Sphinx installed.
+
+For more information, please see the Sphinx setup guide:
+http://www.sphinx-doc.org/en/master/usage/quickstart.html
+
+
+Building your changes
+~~~~~~~~~~~~~~~~~~~~~~~
+
+
+For more extensive edits, or when contributing new guides, you should build the documentation locally. From the ``docs`` root (eg ``/var/www/html/sites/all/modules/tripal/docs/``, execute ``make html``.  The built site will be in ``docs/_build/html/index.html``.
+
+Tripal conventions
+~~~~~~~~~~~~~~~~~~~~~~~
+
+Please follow these guidelines when updating our docs. Let us know if you have any questions or something isn't clear.
+
+Please place images in the same folder as the guide text file, following the convention [file_name].[n].[optional description].[extension].  For example, ``configuring_page_display.3.rearrange.png`` or ``configuring_page_display.1.png`` are both located in ``docs/user_guide/`` and are part of the ``configuring_page_display.rst`` guide.
+
+We currently use the following syntax:
+
+.. code-block:: rst
+
+  Title of File (using title case)
+  =================================
+
+  Introduction text.
+
+  Section Title
+  -------------
+
+  We use double backticks to indicate ``inline-code`` including file names, function and method names, paths, etc.
+
+  Longer code-blocks should begin with the ``.. code-block:: [type]`` directive and should be indented at least one
+  level. There should also be a blank line before and after it as shown below.
+
+  .. code-block:: sql
+    if ($needs_documentation) {
+        use $these_guidelines;
+        $contribute_docs = $appreciated;
+    }
+
+  Section 1.1 Title
+  ^^^^^^^^^^^^^^^^^
+
+  The use of appropriate sections makes reading documentation and later specific details easier. Sub sections such
+  as this one will be hidden unless the main section is already selected.
+  ```

+ 7 - 0
docs/contributing/funding.rst

@@ -0,0 +1,7 @@
+
+Funding Proposal Development
+==============================
+
+Tripal fully supports all community members who want to submit grant proposals for Tripal extensions. Extension development is outside the purview of the Tripal governance structure, although the Tripal Project Management Committee (TPMC) and Tripal Steering Committee (TSC) are available to help with grant submissions, through alignment with Tripal core long term goals, letters of support, etc.
+
+PIs planning to write a grant that would fund any development related to Tripal core are strongly encouraged to engage the TPMC and TSC early in the planning process to ensure the grant aligns with the long term community goals. Any core development plans are still required to go through the Tripal governance structure. Specifically, only development approved by the TPMC and TSC will be implemented or accepted in core. PIs, especially if funded, are strongly encouraged to engage with the Tripal community and to request membership in the TPMC and/or TSC.

+ 138 - 0
docs/contributing/governance.rst

@@ -0,0 +1,138 @@
+Tripal Governance
+==================
+
+We wish to maintain Tripal as an open source project and therefore want to empower  Tripal adopters and developers as much as possible in the development of Tripal, while keeping the project coherent, focused, and useable to a wide range of adopters. As the Tripal community grows, it is prudent to set up a formal governance model. This document describes this model.
+
+The Tripal project recognizes these roles:
+ - **End-Users**: They are users of Tripal-based websites (not developers of a site).
+ - **Adopters**: They have downloaded Tripal. Maybe they even have a site!
+ - **Extension Contributors**: they extend Tripal through modules, themes, views, data loaders, fields, and/or libraries.
+ - **Core Code Contributors**: contribute code to the `Tripal "core" <https://github.com/tripal/tripal>`_, comments, discussion, questions, bug reports.
+ - **Core Code Committers**: write access to the `Tripal "core" repository <https://github.com/tripal/tripal>`_.
+ - **Tripal Project Management Committee (PMC)**: make *code relevant* decisions, (i.e. ensure code standards, robustness, and long-term design objectives are maintained).
+ - **Tripal Advisory Committee (TAC)**: Provide guidance to the PMC for *policy-level* and *future planning* recommendations.
+
+Adopters
+----------
+
+Any person who wishes to or has downloaded/set up a Tripal site.  Everyone in this group is invited to the monthly user meetings and is encouraged to ask questions and make suggestions.
+
+Extension Contributors
+-----------------------
+
+These are developers who are extending Tripal. Extension contributors are encouraged to make their extensions available on the `Tripal GitHub organization <https://github.com/tripal>`_ and list them on the `Tripal Documentation <https://tripal.readthedocs.io/en/latest/extensions.html>`_. Extension contributors are also encouraged to use the `Tripal Module Rating System <https://tripal.readthedocs.io/en/latest/extensions/module_rating.html>`_ as a guideline for developing high quality modules, which are easier to adopt by the greater Tripal community.
+
+Core Code Contributors
+------------------------
+Core Code Contributors are active members of the Tripal community who make suggestions to improve and/or contribute code to Tripal Core. Core Code Contributors are encouraged to submit pull requests to the Tripal core and attend monthly user calls. For more information, see the `Guidelines for Contributing to Tripal core <https://tripal.readthedocs.io/en/latest/dev_guide/contributing/pull_requests.html>`_.
+
+Responsibilities include:
+ - Monitor Tripal core issue queue.
+ - Submit pull requests.
+
+Committers
+------------
+
+These are dedicated Tripal developers who are trusted to commit pull requests directly to the Tripal core repository. They are encouraged to be active in the Tripal community and routinely review pull requests. Developers are added to to committers group by unanimous agreement from the PMC.
+
+Responsibilities include:
+ - Monitor Tripal core issue queue.
+ - Review and merge pull requests.
+
+See the `guidelines for contributors <https://tripal.readthedocs.io/en/latest/dev_guide/contributing/pull_requests.html>`_ for more details.
+
+The Tripal Project Management Committee (PMC)
+------------------------------------------------
+
+This group consists of experienced Tripal developers.
+
+Responsibilities include:
+ - Ensure good practices, for example, submitting errors, questions, and requests via GitHub.
+ - Monitor issue queue (though this responsibility isn't limited to the PMC).
+ - Resolve questions and differences of opinions among Contributors.
+ - Work with the TAC to make decisions about significant new features. Examples:
+     - a new core module,
+     - designing a module-specific REST API,
+     - new technologies or libraries used by the core code.
+ - Avoid feature bloat; judge what contributions benefit many versus those that are specific to the needs of the contributor.
+ - Final approval of submitting guidelines (see `guidelines for contribution <https://tripal.readthedocs.io/en/latest/dev_guide/contributing.html>`_).
+ - Set coding standards.
+ - Ensure Tripal remains generic and useful to a wide range of groups.
+
+The PMC will strive to obtain consensus, and all members ensure that the Tripal community and the TAC are informed on decisions. Any individual member can call a meeting. The term will be two years with the possibility of extension. At least one member will serve on the TAC; this person will be elected by vote within the PMC.
+
+Communication and Meetings
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The PMC will meet as necessary. It is expected that frequent decisions will need to be made, some through GitHub issue comments, Slack, e-mail, or conference calls.
+
+Tripal Advisory Committee (TAC)
+---------------------------------
+The Tripal Advisory Committee (TAC) provides leadership, guidance, future planning, advocacy and coordination for Tripal. The TAC acts in an advisory capacity only by determining sets of recommendations for Tripal. All recommendations will be provided to the PMC. Topics include recommended technologies, overall approach, software architecture, development priorities, timelines, funding strategies, best practices, support for a fair and focused open source development strategy.
+
+At least one member of the PMC must be on the TAC to ensure that the reality of what is and is not feasible for the developers is not lost. Additionally, close communication between the TAC and PMC is critical, as is transparency of the TAC discussions to the entire Tripal community. All members of the PMC are welcome at TAC meetings.
+
+Membership
+^^^^^^^^^^^^
+The TAC should include "internal" and "external" members. Internal members are individuals who manage Tripal websites or lead teams engaged in active development, possibly with funding to do so. External members may be outside the Tripal community altogether, and may include government, non-profit, or industry representatives who are stakeholders for one or more Tripal databases (but not active managers of a Tripal site) and/or specialists in such disciplines as cyberinfrastructure, bioinformatics, genomics, breeding.
+
+- Terms are for two years.
+- Two year memberships can be renewed for individuals who wish to stay on to complete a particular objective.
+- Membership is capped at 15.
+   - Initial Setup:
+      - Start small and move larger as needed.
+      - Set minimum sizes for number of internal and external members.
+      - Committee should be organized before inviting external members.
+      - Stagger ends of terms to ensure continuity.
+- The minimum number of internal members is 3.
+- The number of internal members should not be less than 1/2.
+- The target number of external members is 5.
+- If the TAC decides to replace a leaving member, the current members will develop a list of possible candidates. The chair will contact each in turn until the membership slot is filled.
+- Members will be asked to serve by the current TAC.
+
+Responsibilities include:
+ - Serving a minimum two year term, beginning with the yearly board meeting (see below) in conjunction with the January Plant and Animal Genome Conference in San Diego.
+ - Respond to issues in a timely manner when contacted directly. Members are strongly encouraged to become part of the TAC GitHub group, and if they wish to comment or discuss agenda items directly with the community, to do so in the GitHub issue queue (instead of the email list serve).
+ - Attend the annual January meeting at PAG and at least three of the quarterly meetings.
+ - Review agenda and supporting materials prior to meetings.
+ - Stay informed about Tripal, its member databases, developers, and users.
+
+In addition, internal members are responsible for:
+ - Actively communicating with the Tripal community, both to collect ideas and concerns and to inform the community about TAC plans for Tripal.
+ - Engaging in the Tripal Core GitHub Issue queue on “discussion” issues.
+
+TAC Chair
+^^^^^^^^^^^
+
+The board will be led by a chair to be elected by TAC members at the January meeting annually (see below). One or more vice-chairs can be designated by the chair. The chair will ensure that the following is accomplished, delegating responsibilities as needed:
+ - Organize, announce and lead TAC meetings.
+ - Write the meeting agenda and post to Tripal.info.
+ - Provide supporting materials for review at least 1 week before TAC meetings.
+ - Ensure that the agenda items that would benefit from review by the community are posted to the GitHub Tripal core issue queue. Ensure that any GitHub issue discussions are linked on the agenda and available for review by the TAC.
+ - Ensure meeting notes are taken by someone present at the meeting and posted to Tripal.info.
+ - Call for votes on TAC recommendations when community voting is required.
+ - Call additional meetings if needed.
+ - Facilitate communication between the TAC and PMC.
+ - Filling vacant slots on the TAC.
+ - The chair has voting privileges.
+
+TAC Meeting Agenda Items
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Strongly encouraged to be posted to the GitHub Tripal core issue queue as well as to tripal.info, to inform and solicit community comment. TAC meeting agendas will include issues tagged “TAC Next Meeting” on the GitHub Tripal core issue queue. Other agenda items may be added by the TAC chair or members, or by the PMC. These issues will be closed after the meeting.
+
+Communication and Meetings
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The primary TAC meeting will be held in January of each year, at the Tripal codefest. In-person attendance is strongly encouraged, but a teleconference option will be provided. Each issue on the agenda will be discussed, and if needed, the chair will call for a vote to determine the final recommendation of the TAC. Votes carry based on simple majority. All discussion, votes and objections will be recorded in meeting notes, which will be posted on Tripal.info.
+
+Additional teleconference TAC meetings will be held once per quarter (April, July, October).  These could be held in place of the monthly Tripal User's Meeting to avoid meeting overload.
+
+TAC meetings outside the above schedule may be called by the TAC chair. These will only be called in urgent situations. In less urgent situations, the TAC chair or the TPMC can contact the internal members of the TAC and request a meeting or solicit comments via email, GitHub issue, or Slack.
+
+At any time the TPMC may communicate with members of the TAC with expertise in specific areas if needed to assist in decision making.
+
+Changes to this Document
+--------------------------
+
+These guidelines define the structure for official Tripal Management and Governance. If you have comments or questions, please `post them as a Github issue <https://github.com/tripal/tripal/issues/new?template=discussion.md>`_ or ask them at the user's meeting. Changes to this document will be made after adequate discussion has occurred and the project management committee has voted in favor of the change.

+ 130 - 0
docs/contributing/pull_requests.rst

@@ -0,0 +1,130 @@
+Guidelines for Contribution to Tripal
+========================================
+
+The following guidelines are meant to encourage contribution to Tripal source-code on GitHub by making the process open, transparent and collaborative. If you have any feedback including suggestions for improvement or constructive criticism, please `comment on the Github issue <https://github.com/tripal/tripal/issues/344>`_. **These guidelines apply to everyone contributing to Tripal whether it's your first time (Welcome!) or project management committee members.**
+
+.. note::
+
+  These guidelines are specifically for contributing to `Tripal <https://github.com/tripal/tripal>`_. However, we encourage all Tripal extension modules to consider following these guidelines to foster collaboration among the greater Tripal Community.
+
+.. note::
+
+	Guidelines serve as suggestions ( **should** ) or requirements (**must**). When the word "should" is used in the text below, the stated policy is expected but there may be minor exceptions.  When the word "must" is used there are no exceptions to the stated policy.
+
+
+Github Communication Tips
+---------------------------
+
+- Don't be afraid to mention people (@username) who are knowledgeable on the topic or invested.  *We are academics and overcommitted, it's too easy for issues to go unanswered: don't give up on us!*
+- Likewise, don't be shy about bumping an issue if no one responds after a few days. *Balancing responsibilities is hard.*
+- Want to get more involved? Issues marked with "Good beginner issue" are a good place to start if you want to try your hand at submitting a PR.
+- Everyone is encouraged/welcome to comment on the issue queue! Tell us if you
+    - are experiencing the same problem
+    - have tried a suggested fix
+    - know of a potential solution or work-around
+    - have an opinion, idea or feedback of any kind!
+- Be kind when interacting with others on Github! (see Code of Conduct below for further guidelines). We want to foster a welcoming, inclusive community!
+    - Constructive criticism is welcome and encouraged but should be worded such that it is helpful :-) Direct criticism towards the idea or solution rather than the person and focus on alternatives or improvements.
+
+Bugs
+-----
+
+
+- Every bug **should** be reported as a Github issue.
+    - Even if a bug is found by a committer who intends to fix it themselves immediately, they **should** create an issue and assign it to themselves to show their intent.
+- Please follow the issue templates as best you can.  This information makes discussion easier and helps us resolve the problem faster.
+    - Also provide as much information as possible :-)  Screenshots or links to the issue on a development site can go a long way!
+- Bonus points for unit tests to ensure the bug does not return :-)
+
+Feature Requests
+------------------
+
+- Every feature request should start as an issue so that discussion is encouraged :-)
+- Please provide the following information (bold is required; underlined strengthens your argument):
+    - **Use Case:** fully describe why you need/want this feature
+    - Generally Applicable: Why do you feel this is generally applicable? Suggest other use cases if possible. Mention (@) others that might want/need this feature.
+    - Implementation: Describe a possible implementation. Bonus points for configuration, use of ontologies, ease of use, permission control, security considerations
+- All features **should** be optional so that Tripal admin can choose to make it available to their users.
+    - When applicable, new features should be designed such that Tripal-site admin can disable them.
+    - Bonus points: for making new features configurable and easily themed.
+- Feature requests will be voted on by the project management and advisory/steering committees to determine if it should be included in core, an existing extension module or it's own extension module.
+    - Votes should be based on whether this feature is generally applicable and doesn't exclude existing users and not be biased by the needs of your own Tripal site.
+- If a feature isn't suitable for inclusion within Tripal core, use the issue discussion as a springboard to create a Tripal extension module!
+
+.. note::
+
+  In the future there will be a set of guidelines for what should be included in core. This will make the process of requesting new features more streamlined, inclusive and transparent.
+
+Pull Request (PR) Guideline
+----------------------------
+
+The goal of this document is to make it easy for **A)** contributors to make pull requests that will be accepted, and **B)** Tripal committers to determine if a pull request should be accepted.
+
+- PRs that address a specific issue **must** link to the related issue page.
+    - In almost every case, there should be an issue for a PR.  This allows feedback and discussion before the coding happens.  Not grounds to reject, but encourage users to create issues at start of their PR.  Better late than never :).
+- Each PR **must** be tested/approved by at least 1 contributor, if approved, a "trusted committer" will merge the PR.
+    - Testers **should** describe how the testing was performed if applicable (allows others to replicate the test).    
+    - While Tripal's review body is small, the code review must be a thorough functional test.
+    - At the Project Management Committee's (PMC) discretion, a PR may be subject to a non-functional review.  Generally these are small and obvious commits.
+    - Tripal's guiding philosophy is to encourage open contribution.  With this in mind, committers should **work with contributors** to resolve issues in their PRs.  PRs that will not be merged should be closed, **transparently citing** the reason for closure.  In an ideal world, features that would be closed are discouraged at the **issue phase** before the code is written!
+    - The pull request branch should be deleted after merging (if not from a forked repository) by the person who performs the merge.
+- PRs that include new functionality **must** also provide Unit Tests.
+    - Tests **must** test the new functionality added.
+    - Bonus points for testing all surrounding functionality.
+    - For example, when adding feature X to custom tables, the PR must include tests for feature X and we would be greatly appreciative if it included tests for custom tables in general :-D.
+- PRs **should** pass all Travis-CI tests before they are merged.
+- Branches **should** follow the following format:
+    - ``[issue\_number]-[tripal\_version]-[short\_description]``
+    - ``tripal\_version`` being Tv2, Tv3, etc.
+    - ``-[short\_description]`` being optional but highly encouraged
+- **Must** follow `Drupal code standards <https://www.drupal.org/docs/develop/standards>`_
+- PRs for new feature should remain open until adequately discussed (see guidelines below) and approved by a vote (all members of the PMC must vote in favour).
+
+
+.. note::
+
+  If you need more instructions creating a pull request, see for example the `KnowPulse workflow <https://knowpulse.readthedocs.io/en/latest/developer_docs/dev_workflow.html#workflow>`_
+
+How PRs and Issues are Handled
+------------------------------
+The Project Management Committee (PMC) and trusted committers will follow specific rules when working with all issues and pull requests. The rules are listed below. Anyone may provide bug fixes in which case some of the following will apply to all:
+
+- **Every task related to Tripal (bug, feature requests, documentation, discussions) should be in Github, either as it's own issue or grouped with like tasks into a single issue.** This effectively puts our todo list on github making it transparent to anyone who wants to help. It has the benefit of showing how active our community is, keeps everyone informed with where Tripal is headed and makes it easy for others to chime in with experience, comments and support.
+- **Guidelines for Tagging Issues:**
+    - The first committer who comments on an issue should tag it with the version of Tripal it applies to.
+    - Issues with a suggested fix or work-around should be tagged with "Fix Required" to let others know a PR is needed.
+    - Only tag an issue with "bug" once it has been shown to be reproducible. If it's not reproducible by a committer but you feel it is a bug then tag it as "potential bug".
+    - If multiple users have commented that a bug affects them, tag it as "affects multiple users".
+    - Issues that require a PR and someone with relatively little Tripal experience could fix should be tagged with "Good beginner issue"
+    - All feature requests should be tagged as an "enhancement"
+    - If you are the first reviewer to confirm a PR works, tag it with "Reviewer #1 Approval"
+- **Guidelines for Discussion:**
+    - Issues that do not require discussion (PRs still require 2 reviews): minor bug fixes, changes to inline comments, addition of unit tests, minor code typos
+    - Issues that require discussion: major changes, new features, and issue at the discretion of the PMC
+      - Add the "discussion" tag to any issue requiring discussion
+      - Discussion Tag is removed when adequate discussion has taken place (at the discretion of the person who added the tag)
+      - Additionally, new features require that all members of the PMC have had a chance to contribute to the discussion and feel satisfied.
+- Please use the **assignment** feature to clarify who will be contributing the code to prevent duplication of effort.
+    - When assigning yourself, comment on what your timeline is. This allows others to jump in if they have time sooner.
+    - If you would like to **take over a PR assigned to someone else** , comment asking for an update and offer your services.
+    - If the author of the issue plans on contributing the fix themselves but is not a committer, they should indicate that in the issue.  A committer will assign them the issue.
+- When you start working on an issue, you **should** create the branch and push to it regularly. If you are working on a fork, you're **encouraged** to link to it in the issue.
+    - Committers can work on a fork or directly.  If the branch is on tripal/tripal, then other committers should contribute via PR unless otherwise agreed
+- If an issue is identified as being relevant to another repository (ie a tripal module, not core), a new issue **should** be created, cross referenced, and the original issue should be closed encouraging discussion in the module.
+
+Code of Conduct
+----------------
+
+
+- Be nice!  If that's insufficient, Tripal community defers to https://www.contributor-covenant.org/
+
+Testing/CI
+------------
+
+
+Comprehensive guides to testing are available in the :ref:`tests` section.  Below are guiding principles.
+
+- All tests pass.
+- Tests don't modify db: use transactions and factories.
+- Tests are organized properly: by submodule and function.
+- Tests run quietly.

+ 188 - 0
docs/contributing/tests.rst

@@ -0,0 +1,188 @@
+.. _tests:
+
+Unit Tests for Tripal
+=======================
+
+This guide is for developers looking to contribute code to the core Tripal project.  It introduces the testing philosophy and guidelines for Tripal core.  Tripal uses Tripal Test Suite, which brings bootstraps your Tripal site for PHPUnit.  It also provides conveniences like name spacing, seeders, transactions, and data factories.
+
+Tripal Test Suite
+-------------------
+
+For a basic introduction of Tripal Testing, please see the `Test Suite documentation <https://tripaltestsuite.readthedocs.io/en/latest/>`_.
+
+Installation
+~~~~~~~~~~~~~~
+
+After cloning the `Tripal Github repo <https://github.com/tripal/tripal>`_, you will need to install the developer dependencies required to run tests locally.  To do this, you'll need to `install Composer <https://getcomposer.org/doc/00-intro.md>`_, and then execute ``composer install`` in your project root.
+
+Remember to run ``composer update`` to update Tripal TestSuite before writing and running new tests. This is especially important when running pull requests that contribute unit tests. If tests are passing on the Travis environment but not on your machine, running composer update might resolve the problem.
+
+Testing criteria
+-----------------
+
+For facilitate accepting your pull requests, your code should include tests.  The tests should meet the following guidelines:
+
+* All tests pass
+* Tests pass in all environments (Travis)
+* Tests don't modify the database (use transactions or clean up after yourself)
+* Tests are properly organized (see organization section below)
+* Tests run quietly
+
+Test organization
+------------------
+
+Tests should be placed in ``tests/``.  This root directory contains the following files:
+ - ``bootstrap.php``: Test directory configuration.  Don't modify this.
+ - ``DatabasSeeders/``: `Database seeders <https://github.com/statonlab/TripalTestSuite#database-seeders>`_, for filling Chado with permanent test data.
+ - ``DataFactory.php``: `Data factories <https://github.com/statonlab/TripalTestSuite#factories>`_, for providing test-by-test Chado data.
+ - ``example.env``: An example environment file.  Configure this to match your development site and save as ``.env``.  Read more here: https://tripaltestsuite.readthedocs.io/en/latest/environment.html
+
+Test files must end with ``Test.php`` to be recognized by PHPUnit.  The tests themselves should be organized by submodule, and category.
+
+Submodules
+~~~~~~~~~~~
+
+* tripal
+* tripal_bulk_loader
+* tripal_chado
+* tripal_chado_views
+* tripal_daemon
+* tripal_ds
+* tripal_ws
+
+Categories
+~~~~~~~~~~
+
+* API
+* theme
+* views
+* drush
+* fields
+* entities
+* admin
+* loaders
+
+So for example, tests for the file ``tripal/api/tripal.jobs.api.inc`` should go in ``tests/tripal/api/TripalJobsAPITest.php``. tests that don't fit in any of these categories should be placed in ``tests/[submodule]/``.
+
+In order for tests to run locally, you'll need an environmental file ``tests/.env`` with the project root, base url, and locale.  See ``tests/example.env`` for an example.
+
+Writing tests
+--------------
+
+Tagging tests
+~~~~~~~~~~~~~~~~
+
+You should tag your test with relevant groups.  For example, our Tripal Chado API tests should be tagged with ``@group api``.  We don't need to tag it with ``@group chado`` because it is in the *testsuite* (the submodule folder) Chado.
+
+If your test is related to a specific issue on the Tripal repo, thats great! You can use the ``@ticket`` tag to link it: ie, ``@ticket 742`` for issue number 742.
+
+Defining the test class
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The test class file should extend ``StatonLab\TripalTestSuite\TripalTestCase`` instead of ``TestCase`` to take advantage of the Tripal Test Suite tools.  Tests should use a database transaction to ensure the database state is the same at the start and end of the test case.  Your test class name should match the file.
+
+
+.. code-block:: php
+
+  use StatonLab\TripalTestSuite\DBTransaction;
+  use StatonLab\TripalTestSuite\TripalTestCase;
+
+  class TripalChadoOrganismAPITest extends TripalTestCase {
+  	use DBTransaction;
+  }
+
+
+Defining individual tests
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+An ideal test operates *independently* of other tests: by default, unit tests run in random order.  How, then, do we provide our test with relevant data?  We use **Factories**, which you can read about on in the `Tripal Test Suite documentation <https://tripaltestsuite.readthedocs.io/en/latest/factories.html>`_.  In the below example, we create an organism with known information, and assert that we can retrieve it with the Chado API functions.
+
+
+.. code-block:: php
+
+
+  namespace Tests\tripal_chado\api;
+
+  use StatonLab\TripalTestSuite\DBTransaction;
+  use StatonLab\TripalTestSuite\TripalTestCase;
+
+  class TripalChadoOrganismAPITest extends TripalTestCase {
+
+    use DBTransaction;
+
+    /**
+     * Test tripal_get_organism.
+     *
+     * @group api
+     */
+    public function test_tripal_get_organism() {
+
+      $genus_string = 'a_genius_genus';
+      $species_string = 'fake_species';
+
+      $organism = factory('chado.organism')->create([
+        'genus' => $genus_string,
+        'species' => $species_string,
+      ]);
+
+      $results = [];
+
+      $results[] = tripal_get_organism(['organism_id' => $organism->organism_id]);
+      $results[] = tripal_get_organism([
+        'genus' => $genus_string,
+        'species' => $species_string,
+      ]);
+
+      foreach ($results as $result) {
+        $this->assertNotFalse($result);
+        $this->assertNotNull($result);
+        $this->assertObjectHasAttribute('genus', $result);
+        $this->assertEquals($genus_string, $result->genus);
+      }
+    }
+
+    public function test_tripal_get_organism_fails_gracefully() {
+      $result = tripal_get_organism([
+        'genus' => uniqid(),
+        'species' => uniqid(),
+      ]);
+
+      $this->assertNull($result);
+    }
+  }
+
+
+.. note::
+
+  You typically will want at least one test per public method in your file or class. Tests should start with ``test_``, otherwise it wont run by default in PHPUnit (you can also declare that it is a test in the method documentation using ``@test``.
+
+Testing quietly
+~~~~~~~~~~~~~~~~
+
+Tests should run quietly.  If the output goes to standard out, you can use ``ob_start()`` and ``ob_end_clean()``.
+
+
+.. code-block:: php
+
+
+    ob_start();//dont display the job message
+    $bool = tripal_chado_publish_records($values);
+    ob_end_clean();
+
+
+If the message comes from the Tripal error reporter, you must use ``"TRIPAL_SUPPRESS_ERRORS=TRUE"`` to suppress the Tripal error reporter message.
+
+.. code-block:: php
+
+
+  /**
+   * Test chado_publish_records returns false given bad bundle.
+   *
+   * @group api
+   */
+  public function test_tripal_chado_publish_records_false_with_bad_bundle() {
+    putenv("TRIPAL_SUPPRESS_ERRORS=TRUE");//this will fail, so we suppress the tripal error reporter
+    $bool = tripal_chado_publish_records(['bundle_name' => 'never_in_a_million_years']);
+    $this->assertFalse($bool);
+    putenv("TRIPAL_SUPPRESS_ERRORS");//unset
+  }

+ 19 - 0
docs/dev_guide.rst

@@ -0,0 +1,19 @@
+Developer's Guide
+==================
+
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Table of Contents
+
+   dev_guide/introduction
+   dev_guide/data_structures
+   dev_guide/best_practices
+   dev_guide/chado
+   dev_guide/custom_modules
+   dev_guide/custom_field
+   dev_guide/exporting_field_settings
+   dev_guide/custom_data_loader
+   dev_guide/custom_web_services
+   dev_guide/rtd
+   dev_guide/tutorials

+ 63 - 0
docs/dev_guide/best_practices.rst

@@ -0,0 +1,63 @@
+Module Development Best Practice
+================================
+
+
+If you create custom Tripal Modules, here are some best practices and suggestions.
+
+The Drupal Devel Module
+-----------------------
+
+
+Before staring your development work, it is suggested that you download and install the `Drupal devel module <https://drupal.org/project/devel>`_. This module helps greatly with debugging your custom theme or module. A very useful function of this module is the dpm function. You can use the dpm function to print to the web page an interactive view of the contents of any variable. This can be extremely helpful when accessing Chado data in objects and arrays returned by Tripal.
+
+Add your module to Tripal.info
+------------------------------
+
+Add your modules to the Tripal ReadtheDocs :doc:`../extensions` list. The :doc:`../extensions/module_rating` was designed to give guidance on Tripal module development best practices.
+
+
+Coding Best Practices
+---------------------
+
+Host your code on GitHub
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+We recommend making your code open source and hosting it on GitHub. It’s free, it let’s people easily find, use, and contribute to your source code.
+
+Associate the GitHub repository with Tripal
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Once your module is on GitHub, consider joining the Tripal organization. Your lab group can exist as a team and maintain control over your code, but your projects will be listed in the main Tripal group.
+
+If you’d rather not, you can still tag your project as Tripal by clicking on the Manage Topics Link at the top of your repository.
+
+DOIs
+^^^^
+
+When your module is release ready, why not create a Digital Object Identifier (DOI) for it with `Zenodo <https://zenodo.org/>`_? It’s free! Sync your github account and create a new release (Zenodo won’t find old releases). You can then display your DOI badge on your module’s page.
+
+Additionally, there is a `Tripal Community group <https://zenodo.org/communities/tripal/>`_ on Zenodo. You can edit your record to associate your DOI with the Tripal community.
+
+Testing and Continuous Integration
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+`Tripal Test Suite <https://github.com/statonlab/TripalTestSuite>`_ is a full-featured testing module that makes writing tests much easier. which will automatically set up a PHPUnit and Travis testing environment for you.
+
+* Test with PHPUnit
+* Run tests as you push code with Travis CI
+
+
+Documentation
+^^^^^^^^^^^^^
+
+Every repository can include a README file that will be displayed on the repository page. A README file should at a minimum include:
+
+* An overview of the module
+* Instructions on how to install & use the module
+
+Consider documenting your Code itself. Tripal documents in the `Doxygen style <http://www.stack.nl/~dimitri/doxygen/>`_ which allows documentation webpages to be automatically generated. Even if you don’t build HTML documentation, the in-line code documentation will be very helpful to contributors.
+
+Coding Standards
+^^^^^^^^^^^^^^^^
+
+Drupal has defined `coding standards <https://www.drupal.org/docs/develop/standards/coding-standards>`_ that Tripal modules should meet.

+ 296 - 0
docs/dev_guide/chado.rst

@@ -0,0 +1,296 @@
+Accessing Chado
+================
+
+Primarily biological data made available to Tripal is stored in the GMOD Chado
+schema. As such, you will likely need to interact with Chado at some point.
+Tripal has developed a number of API functions and classes to make this
+interaction easier and more generic.
+
+The Chado Query API
+--------------------
+
+Provides an API for querying of chado including inserting, updating, deleting and selecting from specific chado tables. There is also a generic function, ``chado_query()``, to execute and SQL statement on chado. It is ideal to use these functions to interact with chado in order to keep your module compatible with both local & external chado databases. Furthermore, it ensures connection to the chado database is taken care of for you.
+
+Generic Queries to a specifc chado table
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Selecting Records
+""""""""""""""""""
+
+``chado_select_record( [table name], [columns to select], [specify record to select], [options*] )``
+
+This function allows you to select various columns from the specified chado table. Although you can only select from a single table, you can specify the record to select using values from related tables through use of a nested array. For example, the following code shows you how to select the name and uniquename of a feature based on it's type and source organism.
+
+.. code-block:: php
+
+  $values =  array(
+    'organism_id' => array(
+      'genus' => 'Citrus',
+      'species' => 'sinensis',
+    ),
+    'type_id' => array (
+      'cv_id' => array (
+        'name' => 'sequence',
+      ),
+      'name' => 'gene',
+      'is_obsolete' => 0
+    ),
+  );
+
+  $result = chado_select_record(
+    'feature',                      // table to select from
+    array('name', 'uniquename'),    // columns to select
+    $values                         // record to select (see variable defn. above)
+  );
+
+Inserting Records
+""""""""""""""""""
+
+``chado_insert_record( [table name], [values to insert], [options*] )``
+
+This function allows you to insert a single record into a specific table. The values to insert are specified using an associative array where the keys are the column names to insert into and they point to the value to be inserted into that column. If the column is a foreign key, the key will point to an array specifying the record in the foreign table and then the primary key of that record will be inserted in the column. For example, the following code will insert a feature and for the type_id, the cvterm.cvterm_id of the cvterm record will be inserted and for the organism_id, the organism.organism_id of the organism_record will be inserted.
+
+.. code-block:: php
+
+  $values =  array(
+    'organism_id' => array(
+        'genus' => 'Citrus',
+        'species' => 'sinensis',
+     ),
+    'name' => 'orange1.1g000034m.g',
+    'uniquename' => 'orange1.1g000034m.g',
+    'type_id' => array (
+        'cv_id' => array (
+           'name' => 'sequence',
+        ),
+        'name' => 'gene',
+        'is_obsolete' => 0
+     ),
+  );
+  $result = chado_insert_record(
+    'feature',             // table to insert into
+    $values                // values to insert
+  );
+
+Updating Records
+""""""""""""""""""
+
+``chado_update_record( [table name], [specify record to update], [values to change], [options*] )``
+
+This function allows you to update records in a specific chado table. The record(s) you wish to update are specified the same as in the select function above and the values to be update are specified the same as the values to be inserted were. For example, the following code species that a feature with a given uniquename, organism_id, and type_id (the unique constraint for the feature table) will be updated with a new name, and the type changed from a gene to an mRNA.
+
+.. code-block:: php
+
+  $umatch = array(
+    'organism_id' => array(
+      'genus' => 'Citrus',
+      'species' => 'sinensis',
+    ),
+    'uniquename' => 'orange1.1g000034m.g7',
+    'type_id' => array (
+      'cv_id' => array (
+        'name' => 'sequence',
+      ),
+      'name' => 'gene',
+      'is_obsolete' => 0
+    ),
+  );
+  $uvalues = array(
+    'name' => 'orange1.1g000034m.g',
+    'type_id' => array (
+      'cv_id' => array (
+        'name' => 'sequence',
+      ),
+      'name' => 'mRNA',
+      'is_obsolete' => 0
+    ),
+  );
+  $result = chado_update_record('feature',$umatch,$uvalues);
+
+Deleting Records
+"""""""""""""""""
+
+``chado_delete_record( [table name], [specify records to delete], [options*] )``
+
+This function allows you to delete records from a specific chado table. The record(s) to delete are specified the same as the record to select/update was above. For example, the following code will delete all genes from the organism Citrus sinensis.
+
+.. code-block:: php
+
+  $values =  array(
+    'organism_id' => array(
+        'genus' => 'Citrus',
+        'species' => 'sinensis',
+     ),
+    'type_id' => array (
+        'cv_id' => array (
+           'name' => 'sequence',
+        ),
+        'name' => 'gene',
+        'is_obsolete' => 0
+     ),
+  );
+  $result = chado_select_record(
+     'feature',                      // table to select from
+     $values                         // records to delete (see variable defn. above)
+  );
+
+Generic Queries for any SQL
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Often it is necessary to select from more then one table in chado or to execute other complex queries that cannot be handled efficiently by the above functions. It is for this reason that the ``chado_query( [sql string], [arguments to sub-in to the sql] )`` function was created. This function allows you to execute any SQL directly on the chado database and should be used with care. If any user input will be used in the query make sure to put a placeholder in your SQL string and then define the value in the arguments array. This will make sure that the user input is sanitized and safe through type-checking and escaping. The following code shows an example of how to use user input resulting from a form and would be called with the form submit function.
+
+.. code-block:: php
+
+  $sql = "SELECT F.name, CVT.name as type_name, ORG.common_name
+           FROM feature F
+           LEFT JOIN cvterm CVT ON F.type_id = CVT.cvterm_id
+           LEFT JOIN organism ORG ON F.organism_id = ORG.organism_id
+           WHERE
+             F.uniquename = :feature_uniquename";
+  $args = array( ':feature_uniquename' => $form_state['values']['uniquename'] );
+  $result = chado_query( $sql, $args );
+  foreach ($result as $r) { [Do something with the records here] }
+
+If you are going to need more then a couple fields, you might want to use the Chado Variables API (specifically ``chado_generate_var()``) to select all of the common fields needed including following foreign keys.
+
+Loading of Variables from chado data
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+These functions, ``chado_generate_var()`` and ``chado_expand_var()``, generate objects containing the full details of a record(s) in chado. These should be used in all theme templates.
+
+This differs from the objects returned by ``chado_select_record`` in so far as all foreign key relationships have been followed meaning you have more complete details. Thus this function should be used whenever you need a full variable and ``chado_select_record`` should be used if you only case about a few columns.
+
+The initial variable is generated by the ``chado_generate_var([table], [filter criteria], [optional options])`` function. An example of how to use this function is:
+
+.. code-block:: php
+
+  $values = array(
+    'name' => 'Medtr4g030710'
+  );
+  $features = chado_generate_var('feature', $values);
+
+This will return an object if there is only one feature with the name Medtr4g030710 or it will return an array of feature objects if more than one feature has that name.
+
+Some tables and fields are excluded by default. To have those tables & fields added to your variable you can use the ``chado_expand_var([chado variable], [type], [what to expand], [optional options])`` function. An example of how to use this function is:
+
+.. code-block:: php
+
+  // Get a chado object to be expanded
+  $values = array(
+    'name' => 'Medtr4g030710'
+  );
+
+  $features = chado_generate_var('feature', $values);
+
+  // Expand the organism node
+  $feature = chado_expand_var($feature, 'node', 'organism');
+
+  // Expand the feature.residues field
+  $feature = chado_expand_var($feature, 'field', 'feature.residues');
+
+  // Expand the feature properties (featureprop table)
+  $feature = chado_expand_var($feature, 'table', 'featureprop');
+
+
+The Chado Schema API
+--------------------
+
+The Chado Schema API provides an application programming interface (API) for describing Chado tables, accessing these descriptions and checking for compliancy of your current database to the chado schema. This API consists of the ChadoSchema class which provides methods for interacting with the Chado Schema API and a collection of supporting functions, one for each table in Chado, which describe each version of the Chado schema. Each function simply returns a Drupal style array that defines the table.
+
+Ensuring columns Tables & Columns exist
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Generally you can assume the tables and columns in the Chado schema have been unaltered. That said, there are still cases where you might want to check that specific tables and columns exist. For example, when using a custom table, it is best practice to ensure it is there before querying as it can be removed through the administrative interface.
+
+To check the existence of a specific table and column, you can use the following:
+
+.. code-block:: php
+
+  $chado_schema = new \ChadoSchema();
+
+  // Check that the organism_feature_count custom table exists.
+  $table_name = 'organism_feature_count';
+  $table_exists = $chado_schema->checkTableExists($table_name);
+
+  if ($table_exists) {
+
+    // Check that the organism_feature_count.feature_id column exists.
+    $column_name = 'feature_id';
+    $column_exists = $chado_schema->checkColumnExists($table_name, $column_name);
+
+    if ($column_exists) {
+
+      [ do your query, etc. here ]
+
+    } else { [warn the admin using tripal_repot_error()] }
+  } else { [warn the admin using tripal_repot_error()] }
+
+Checking the Schema Version
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+If you are using chado tables specific to a given version of Chado, it is best practice to check the chado version of the current site before querying those tables. You can use the following query to do this:
+
+.. code-block:: php
+
+  $chado_schema = new \ChadoSchema();
+  $version = $chado_schema-getVersion();
+  if ($version == '1.3') {
+    [do your chado v1.3 specific querying here]
+  } else { [warn the admin using tripal_report_error() ] }
+
+
+Retrieving a list of tables
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To retrieve a list of Chado tables, you can use the following:
+
+.. code-block:: php
+
+  $chado_schema = new \ChadoSchema();
+
+  // All Chado Tables including custom tables
+  $all_tables = $chado_schema->getTableNames(TRUE);
+
+  // All Chado Tables without custom tables
+  $all_tables = $chado_schema->getTableNames();
+
+  // Chado tables designated as Base Tables by Tripal.
+  $base_tables = $chado_schema->getBaseTables();
+
+
+Ensuring your Chado instance is compliant
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Checking compliancy of your Chado instance with the released Chado Schema is a great way to **confirm an upgrade has gone flawlessly**. Additionally, while it is not recommended, sometimes customizations to the Chado schema may be necessary. In these cases, you should **ensure backwards compatibility** through compliance checking to confirm Tripal will work as expected.
+
+Chado compliancy testing is provided with Tripal's automated PHPUnit testing. As such, to test compliancy of your specific Chado instance, you first need to install Composer. Luckily this can be as easy as:
+
+.. code-block:: bash
+
+  php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
+  php -r "if (hash_file('SHA384', 'composer-setup.php') === '544e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586475ca9813a858088ffbc1f233e9b180f061') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
+  php composer-setup.php
+  php -r "unlink('composer-setup.php');"
+
+Once you have Composer, you need to install PHPUnit. This is installed locally within your Tripal repository. The following bash snippet shows you how to both install composer locally and run compliance checking.
+
+.. code-block:: php
+
+  cd [DRUPAL_ROOT]/sites/all/modules/tripal
+  composer up
+
+  # Now run compliance checking
+  ./vendor/bin/phpunit --group chado-compliance
+
+Schema Definition
+^^^^^^^^^^^^^^^^^^
+
+To retrieve the schema definition for a specific table, you can execute the following:
+
+.. code-block:: php
+
+  $table_name = 'feature';
+  $chado_schema = new \ChadoSchema();
+  $table_schema = $chado_schema->getTableSchema($table_name);
+
+The resulting ``$table_schema`` variable contains a Drupal-style array describing the schema definition of the table specified by ``$table_name``. This is a great tool when trying to develop generic queries, since you can extract information about an unknown table and use it to build a query for that table. For more information on the format of this array, see `the Drupal Schema API documentation <https://api.drupal.org/api/drupal/includes%21database%21schema.inc/group/schemaapi/7.x>`_.

BIN
docs/dev_guide/custom_data_loader.0.png


BIN
docs/dev_guide/custom_data_loader.1.oob_file_interface.png


BIN
docs/dev_guide/custom_data_loader.2.oob_analysis_select.png


BIN
docs/dev_guide/custom_data_loader.3.cvterm_select.png


+ 502 - 0
docs/dev_guide/custom_data_loader.rst

@@ -0,0 +1,502 @@
+Creating Custom Data Loaders
+==============================
+
+.. note::
+
+  This guide is also available as a `video on youtube. <https://www.youtube.com/watch?v=5y8rDtDQEg0>`_
+
+The ``TripalImporter`` class can be extended to create your own data loader.  This class provides many conveniences to simplify loader construction. For example, it simplifies and unifies input form development, automatically handles files upload by the user, provides job submission, logging and progress updates. Using the TripalImporter to create your loader also makes it easy to share your loader with other Tripal users!
+
+To document how to create a new importer, we will describe use of the ``TripalImporter`` class within the context of a new simple importer called the ``ExampleImporter``. This importer will read in a comma-separated file containing genomic features and their properties ( a fictional "Test Format" file).  The loader will split each line into feature and property values, and then insert each property into the ``featureprop`` table of Chado using a controlled vocabulary term (supplied by the user) as the ``type_id`` for the property.
+
+.. note::
+  Prior to starting your data loader you should plan how the data will be imported into Chado. Chado is a flexible database schema and it may be challenging at times to decide in to which tables data should be placed.  It is recommended to reach out to the Chado community to solicit advice. Doing so will allow you to share your loader will other Tripal users more easily!
+
+Create a Custom Module
+----------------------
+To create your own importer, you first need to have a custom extension module in which the loader will be provided.  If you do not know how to create a module, see the section titled **Creating a Custom Module** for further direction. Providing your new importer within a custom module will allow you to more easily share your loader with other Tripal users. Any site that downloads and enables your extension module will be able to use your data loader.  For this document we will describe creation of a new importer in a module named ``tripal_example_importer``.
+
+Create the Class File
+---------------------
+To define a new class that extends ``TripalImporter``, you should create a new class file with a ``.inc`` extension within your custom module in the directory: ``includes/TripalImporter/``.  If this is your first importer, then you will need to create this directory. For the example described here, we will create a new ``TripalImporter`` class named ``ExampleImporter``. Therefore, we must name the file the same as the class (with the .inc extension) and place the file here: ``tripal_example_importer/includes/TripalImporter/ExampleImporter.inc``.  Initially, our new class is empty:
+
+.. code-block:: php
+
+  class ExampleImporter extends TripalImporter {
+  }
+
+There is no need to include the importer via a ``require_once`` statement in your module file. Placing it in the ``/includes/TripalImporter/`` directory of your module is all you need for Tripal to find it. Tripal will automatically place a link for your importer at ``admin -> Tripal -> Data Loaders``.
+
+.. note::
+
+  If after creation of your importer file, Tripal does not show a link for it in the Data Loaders page, check that you have named your class file correctly and it is in the path described above. Sometimes a clear cache is necessary (``drush cc all``).
+
+
+Static Variables
+-----------------
+The next step in creation of your importer is setting the static member variables. Open the ``TripalImporter`` class file that comes with Tripal and found here ``tripal/includes/TripalImporter.inc``. Copy the  ``public static`` member variables at the top of the class into your own class. For your importer, override any of the ``public static`` variables that need to be different from the default.
+
+.. note::
+
+  For the sake of simplicity in this document, many of the default settings are not changed, and therefore, not all are included.
+
+Our ``ExampleImporter`` class now appears as follows:
+
+.. code-block:: php
+
+
+  /**
+   * @see TripalImporter
+   */
+   class ExampleImporter extends TripalImporter {
+
+    /**
+     * The name of this loader.  This name will be presented to the site
+     * user.
+     */
+    public static $name = 'Example TST File Importer';
+
+    /**
+     * The machine name for this loader. This name will be used to construct
+     * the URL for the loader.
+     */
+    public static $machine_name = 'tripal_tst_loader';
+
+    /**
+     * A brief description for this loader.  This description will be
+     * presented to the site user.
+     */
+    public static $description = 'Loads TST files';
+
+    /**
+     * An array containing the extensions of allowed file types.
+     */
+    public static $file_types = ['txt', 'tst', 'csv'];
+
+    /**
+     * Provides information to the user about the file upload.  Typically this
+     * may include a description of the file types allowed.
+     */
+    public static $upload_description = 'TST is a fictional format.  Its a 2-column, CSV file.  The columns should be of the form featurename, and text';
+
+    /**
+     * Indicates the methods that the file uploader will support.
+     */
+    public static $methods = [
+      // Allow the user to upload a file to the server.
+      'file_upload' => TRUE,
+      // Allow the user to provide the path on the Tripal server for the file.
+      'file_local' => TRUE,
+      // Allow the user to provide a remote URL for the file.
+      'file_remote' => TRUE,
+    ];
+  }
+
+.. warning::
+
+  The variables that are ``private static`` **should not** be copied and should not be changed. Only copy and change the ``public static`` member variables.
+
+
+Now that we've given our importer a name and description, it will show up at ``/admin/tripal/loaders``:
+
+.. image:: ./custom_data_loader.0.png
+
+
+Form Components
+-----------------
+
+By default, the ``TripalImporter`` class will provide the necessary upload widgets to allow a user to upload files for import.  The static variables we set in the previous step dictate how that uploader appears to the user.  However, for this example, our importer needs additional information from the user before data can be loaded.  We need to provide additional form widgets.
+
+Typically, to create forms, Drupal provides form hooks: ``form``, ``form_validate``, ``form_submit``. The **TripalImporter** wraps these for us as class functions named ``form``, ``formValidate`` and ``formSubmit``.  We can override these class functions to provide additional widgets to the form.
+
+.. note::
+
+  Typically we only need to implement the ``form`` and ``formValidate`` functions. The ``formSubmit`` does not need to be modified.
+
+.. note::
+
+  If you are not familiar with form creation in Drupal you may want to find a Drupal reference book that provides step-by-step instructions.  Additionally, you can explore the `API documentation for form construction for Drupal 7 <https://api.drupal.org/api/drupal/developer%21topics%21forms_api_reference.html/7.x>`_.  Here, this example expects you are comfortable with form construction in Drupal.
+
+
+The form function
+^^^^^^^^^^^^^^^^^
+To provide custom widgets for our importer we need to implement the ``form`` function.  However, let's review the current form provided by the TripalImporter for us already.  Using the static variables settings specified above the form automatically provides a **File Upload** field set, and an **Analysis** selector.  The **File Upload** area lets users choose to upload a file, provide a **Server path** to a file already on the web server or a **Remote path** for files located via a downloadable link on the web.  The **Analysis** selector is important because it allows the user to specify an analysis that describes how the data file was created.
+
+.. image:: ./custom_data_loader.1.oob_file_interface.png
+
+.. image:: ./custom_data_loader.2.oob_analysis_select.png
+
+For our example TST file importer these upload options are sufficient.  However, for our data import we want the user provide a CV term.  We want our importer to read the file, split it into feature and values, and insert properties into the ``featureprop`` table of Chado using the the CV term as the ``type_id`` for the table.
+
+To add a widget that allows the user to provide a CV term, we must implement the ``form`` function and include code using Drupal's Form API that will add the widget.
+
+.. code-block:: php
+  :name: ExampleImporter::form
+
+
+  public function form($form, &$form_state) {
+
+
+    // For our example loader let's assume that there is a small list of
+    // vocabulary terms that are appropriate as properties for the genomics
+    // features. Therefore, we will provide an array of sequence ontology terms
+    // the user can select from.
+    $terms = [
+      ['id' => 'SO:0000235'],
+      ['id' => 'SO:0000238'],
+      ['id' => 'SO:0000248']
+    ];
+
+    // Construct the options for the select drop down.
+    $options = [];
+    // Iterate through the terms array and get the term id and name using
+    // appropriate Tripal API functions.
+    foreach ($terms as $term){
+      $term_object = chado_get_cvterm($term);
+      $id = $term_object->cvterm_id;
+      $options[$id] = $term_object->name;
+    }
+
+    // Provide the Drupal Form API array for a select box.
+    $form['pick_cvterm'] =  [
+      '#title' => 'CVterm',
+      '#description' => 'Please pick a CVterm.  The loaded TST file will associate the values with this term as a feature property.',
+      '#type' => 'select',
+      '#default_value' => '0',
+      '#options' => $options,
+      '#empty_option' => '--please select an option--'
+    ];
+
+    // The form function must always return our form array.
+    return $form;
+  }
+
+Our form now has a select box!
+
+.. image:: ./custom_data_loader.3.cvterm_select.png
+
+
+Using AJAX in forms
+"""""""""""""""""""
+
+.. note::
+
+  This section is not yet available. For now, check out the Drupal AJAX guide https://api.drupal.org/api/drupal/includes%21ajax.inc/group/ajax/7.x
+
+
+The formValidate function
+^^^^^^^^^^^^^^^^^^^^^^^^^
+The ``formValidate`` function is responsible for verifying that the user supplied values from the form submission are valid.  To warn the user of inappropriate values, the Drupal API function, ``form_set_error()`` is used. It provides an error message, highlights in red the widget containing the bad value, and prevents the form from being submitted--allowing the user to make corrections. In our example code, we will check that the user selected a CV term from the ``pick_cvterm`` widget.
+
+
+.. code-block:: php
+
+  public function formValidate($form, &$form_state) {
+
+    // Always call the TripalImporter (i.e. parent) formValidate as it provides
+    // some important feature needed to make the form work properly.
+    parent::formValidate($form, $form_state);
+
+    // Get the chosen CV term form the form state and if there is no value
+    // set warn the user.
+    $chosen_cvterm = $form_state['values']['pick_cvterm'];
+    if ($chosen_cvterm == 0) {
+      form_set_error('pick_cvterm', 'Please choose a CVterm.');
+    }
+  }
+
+The implementation above looks for the ``pick_cvterm`` element of the ``$form_state`` and ensures the user selected something.  This is a simple example. An implementation for a more complex loader with a variety of widgets will require more validation checks.
+
+.. note::
+
+  If our importer followed best practices, it would not need a validator at all.  The cvterm select box in the form could be defined as below.  Note the ``'#required' => True`` line: this would handle the validation for us.  For this tutorial, however, we implement the validation ourselves to demonstrate the function.
+
+  .. code-block:: php
+
+    // Provide the Drupal Form API array for a select box.
+    $form['pick_cvterm'] =  [
+      '#title' => 'CVterm',
+      '#description' => 'Please pick a CVterm.  The loaded TST file will associate the values with this term as a feature property.',
+      '#type' => 'select',
+      '#default_value' => '0',
+      '#options' => $options,
+      '#empty_option' => '--please select an option--'
+      '#required' => True
+    ];
+
+
+When an importer form is submitted and passes all validation checks, a job is automatically added to the **Tripal Job** system. The ``TripalImporter`` parent class does this for us! The **Tripal Job** system is meant to allow long-running jobs to execute behind-the-scenes on a regular time schedule.  As jobs are added they are executed in order.  Therefore, if a user submits a job using the importer's form then the **Tripal Job** system will automatically run the job the next time it is scheduled to run or it can be launched manually by the site administrator.
+
+
+Importer Execution
+------------------
+The ``form`` and ``formValidate`` functions allow our Importer to receive an input file and additional values needed for import of the data.  To execute loading a file the ``TripalImporter`` provides several additional overridable functions:  ``run``, ``preRun`` and ``postRun``.  When the importer is executed, the ``preRun`` function is called first. It allows the importer to perform setup prior to full execution.  The ``run`` function is where the full execution occurs and the ``postRun`` function is used to perform "cleanup" prior to completion. For our ``ExampleImporter`` class we only need to implement the ``run`` function.  We have no need to perform any setup or cleanup outside of the typical run.
+
+The run function
+^^^^^^^^^^^^^^^^
+The ``run`` function is called automatically when Tripal runs the importer. For our ``ExampleImporter``, the run function should collect the values provided by the user, read and parse the input file and load the data into Chado. The first step, is to retrieve the user provided values and file details. The inline comments in the code below provide instructions for retrieving these details.
+
+
+.. code-block:: php
+
+    /**
+     * @see TripalImporter::run()
+     */
+    public function run() {
+
+      // All values provided by the user in the Importer's form widgets are
+      // made available to us here by the Class' arguments member variable.
+      $arguments = $this->arguments['run_args'];
+
+      // The path to the uploaded file is always made available using the
+      // 'files' argument. The importer can support multiple files, therefore
+      // this is an array of files, where each has a 'file_path' key specifying
+      // where the file is located on the server.
+      $file_path = $this->arguments['files'][0]['file_path'];
+
+      // The analysis that the data being imported is associated with is always
+      // provided as an argument.
+      $analysis_id = $arguments['analysis_id'];
+
+      // Any of the widgets on our form are also available as an argument.
+      $cvterm_id = $arguments['pick_cvterm'];
+
+      // Now that we have our file path, analysis_id and CV term we can load
+      // the file.  We'll do so by creating a new function in our class
+      // called "loadMyFile" and pass these arguments to it.
+      $this->loadMyFile($analysis_id, $file_path, $cvterm_id);
+    }
+
+.. note::
+
+  We do not need to validate in the ``run`` function that all of the necessary values in the arguments array are valid.  Remember, this was done by the ``formValidate`` function when the user submitted the form.  Therefore, we can trust that all of the necessary values we need for the import are correct.  That is of course provided our ``formValidate`` function sufficiently checks the user input.
+
+Importing the File
+^^^^^^^^^^^^^^^^^^
+To keep the ``run`` function small, we will implement a new function named ``loadMyFile`` that will perform parsing and import of the file into Chado. As seen in the code above, the ``loadMyFile`` function is called in the ``run`` function.
+
+Initially, lets get a feel for how the importer will work.  Lets just print out the values provided to our importer:
+
+
+.. code-block:: php
+
+  public function loadMyFile($analysis_id, $file_path, $cvterm){
+    var_dump(["this is running!", $analysis_id, $file_path, $cvterm]);
+  }
+
+To test our importer navigate to ``admin > Tripal > Data Importers`` and click the link for our TFT importer. Fill out the form and press submit.  If there are no validation errors, we'll receive notice that our job was submitted and given a command to execute the job manually. For example:
+
+..
+
+  drush trp-run-jobs --username=admin --root=/var/www/html
+
+
+If we execute our importer we should see the following output:
+
+
+.. code-block:: bash
+
+    Calling: tripal_run_importer(146)
+
+    Running 'Example TST File Importer' importer
+    NOTE: Loading of file is performed using a database transaction.
+    If it fails or is terminated prematurely then all insertions and
+    updates are rolled back and will not be found in the database
+
+    array(4) {
+      [0]=>
+      string(16) "This is running!"
+      [1]=>
+      string(3) "147"
+      [2]=>
+      string(3) "695"
+      [3]=>
+      string(72) "/Users/chet/UTK/tripal/sites/default/files/tripal/users/1/expression.tsv"
+    }
+
+    Done.
+
+    Remapping Chado Controlled vocabularies to Tripal Terms...
+
+
+As you can see, running the job executes our run script, and we have all the variables we need to load the data.  All we need to do now is write the code!
+
+To import data into Chado we will use the Tripal API. After splitting each line of the input file into a genomic feature and its property, we will use the ``chado_select_record`` to match the feature's name with a record in the ``feature`` table of Chado, and the ``chado_insert_property`` to add the property value.
+
+
+.. code-block:: php
+
+  public function loadMyFile($analysis_id, $file_path, $cvterm_id){
+
+    // We want to provide a progress report to the end-user so that they:
+    // 1) Recognize that the loader is not hung if running a large file, but is
+    //    executing
+    // 2) Provides some indicatation for how long the file will take to load.
+    //
+    // Here we'll get the size of the file and tell the TripalImporter how
+    // many "items" we have to process (in this case bytes of the file).
+    $filesize = filesize($file_path);
+    $this->setTotalItems($filesize);
+    $this->setItemsHandled(0);
+
+    // Loop through each line of file.  We use the fgets function so as not
+    // to load the entire file into memory but rather to iterate over each
+    // line separately.
+    $bytes_read = 0;
+    $in_fh = fopen($file_path, "r");
+    while ($line = fgets($in_fh)) {
+  
+      // Calculate how many bytes we have read from the file and let the
+      // importer know how many have been processed so it can provide a
+      // progress indicator.
+      $bytes_read += drupal_strlen($line);
+      $this->setItemsHandled($bytes_read);
+
+      // Remove any trailing white-space from the line.
+      $line = trim($line);
+
+      // Split line on a comma into an array.  The feature name appears in the
+      // first "column" of data and the property in the second.
+      $cols = explode(",", $line);
+      $feature_name = $cols[0];
+      $this_value = $cols[1];
+
+      // Our file has a header with the name 'Feature name' expected as the
+      // title for the first column. If we see this ignore it.
+      if ($feature_name == 'Feature name'){
+         continue;
+      }
+
+      // Using the name of the feature from the file, see if we can find a
+      // record in the feature table of Chado that matches.  Note: in reality
+      // the feature table of Chado has a unique contraint on the uniquename,
+      // organism_id and type_id columns of the feature table.  So, to ensure
+      // we find a single record ideally we should include the organism_id and
+      // type_id in our filter and that would require more widgets on our form!
+      // For simplicity, we will just search on the uniquename and hope we
+      // find unique features.
+      $match = ['uniquename' => $feature_name];
+      $results = chado_select_record('feature', ['feature_id'], $match);
+
+      // The chado_select_record function always returns an array of matches. If
+      // we found no matches then this feature doesn't exist and we'll skip
+      // this line of the file.  But, log this issue so the user knows about it.
+      if (count($results) == 0) {
+        $this->logMessage('The feature, !feature, does not exist in the database',
+          ['!feature' => $feature_name], TRIPAL_WARNING);
+        continue;
+      }
+
+      // If we failed to find a unique feature then we should warn the user
+      // but keep on going.
+      if (count($results) == 0) {
+        $this->logMessage('The feature, !feature, exists multiple times. ' .
+          'Cannot add a property', ['!feature' => $feature_name], TRIPAL_WARNING);
+        continue;
+      }
+
+      // If we've made it this far then we have a feature and we can do the
+      // insert.
+      $feature = $results[0];
+      $record = [
+        'table' => 'feature',
+        'id' => $feature->feature_id
+      ];
+      $property = [
+        'type_id' => $cvterm_id,
+        'value' => $this_value,
+      ];
+      $options = ['update_if_present' => TRUE];
+      chado_insert_property($record, $property, $options);
+    }
+  }
+
+Logging and Progress
+--------------------
+During execution of our importer it is often useful to inform the user of progress, status and issues encountered.  There are several functions to assist with this. These include the ``logMessage``,  ``setTotalItems`` and ``setItemsHandled`` functions.  All three of these functions were used in the sample code above of the ``loadMyFile`` function.  Here, we provide a bit more detail.
+
+The logMessage function
+^^^^^^^^^^^^^^^^^^^^^^^
+The ``logMessage`` function is meant to allow the importer to provide status messages to the user while the importer is running.  The function takes three arguments:
+
+1) a message string.
+2) an array of substitution values.
+3) a message status.
+
+The message string contains the message for the user.  You will notice that no variables are included in the string but rather tokens are used as placeholders for variables.  This is a security feature provided by Drupal.  Consider these lines from the code above:
+
+.. code-block:: php
+
+  $this->logMessage('The feature, !feature, does not exist in the database',
+    ['!feature' => $feature_name], TRIPAL_WARNING);
+
+Notice that ``!feature`` is used in the message string as a placeholder for the feature name. The mapping of ``!feature`` to the actually feature name is provided in the array provided as the second argument.  The third argument supports several message types including ``TRIPAL_NOTICE``, ``TRIPAL_WARNING`` and ``TRIPAL_ERROR``.  The message status indicates a severity level for the message.  By default if no message type is provided the message is of type ``TRIPAL_NOTICE``.
+
+Any time the ``logMessage`` function is used the message is stored in the job log, and a site admin can review these logs by clicking on the job in the ``admin > Tripal > Tripal Jobs`` page.
+
+.. note::
+
+  You should avoid using ``print`` or ``print_r`` statements in a loader to provide messages to the end-user while loading the file.  Always use the ``logMessage`` function to ensure all messages are sent to the job's log.
+
+The setTotalItems and setItemsHandled functions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+The ``TripalImporter`` class is capable of providing progress updates to the end-user while the importer job is running. This is useful as it gives the end-user a sense for how long the job will take. As shown in the sample code above for the ``loadMyFile`` function, The first step is to tell the ``TripalImporter`` how many items need processing.  An **item** is an arbitrary term indicating some measure of countable "units" that will be processed by our importer.
+
+In the code above we consider a byte as an item, and when all bytes from a file are read we are done loading that file.  Therefore the ``setTotalItems`` function is used to tell the importer how many bytes we need to process.  As we read each line, we count the number of bytes read and provide that number to the ``setItemsHandled`` function.  The ``TripalImporter`` class will automatically calculate progress and print a message to the end-user indicating the percent complete, and some additional details such as the total amount of memory consumed during the loading.
+
+.. note::
+
+  All importers are different and the "item" need not be the number of bytes in the file.  However, if you want to provide progress reports you must identify an "item" and the total number of items there are for processing.
+
+Testing Importers
+------------------
+Unit Testing is a critically important component of any software project. You should always strive to write tests for your software.  Tripal provides unit testing using the ``phpunit`` testing framework. The Tripal Test Suite provides a strategy for adding tests for your new Importer.  It will automatically set up and bootstrap Drupal and Tripal for your testing environment, as well as provide database transactions for your tests, and factories to quickly generate data.  We will use the Tripal Test Suite to provide unit testing for our ``ExampelImporter``.
+
+.. note::
+  Before continuing, please install and configure Tripal Test Suite.
+
+  For instructions on how to install, configure, and run Tripal Test Suite, `please see the Tripal Test Suite documentation. <https://tripaltestsuite.readthedocs.io/en/latest/>`_
+
+
+Example file
+^^^^^^^^^^^^
+When developing tests, consider including a small example file as this is good practice both to ensure that your loader works as intended, and for new developers to easily see the expected file format.  For our ``ExampleImporter``, we'll include the following sample file and store it in this directory of our module:  ``tests/data/example.txt``.
+
+.. csv-table:: Example input file
+  :header: "Feature name", "CVterm value"
+
+  "test_gene_1", "blue"
+  "test_gene_2", "red"
+
+
+Loading the Importer
+^^^^^^^^^^^^^^^^^^^^
+Testing your loader requires a few setup steps.  First, TripalImporters are not explicitly loaded in your module (note that we never use ``include_once()`` or ``require_once`` in the ``.module`` file).  Normally Tripal finds the importer automatically, but for unit testing we must include it to our test class explicitly.  Second, we must initialize an instance of our importer class. Afterwards we can perform any tests to ensure our loader executed properly.  The following function provides an example for setup of the loader for testing:
+
+.. code-block:: php
+
+  private function run_loader(){
+
+    // Load our importer into scope.
+    module_load_include('inc', 'tripal_example_importer', 'includes/TripalImporter/ExampleImporter');
+
+    // Create an array of arguments we'll use for testing our importer.
+    $run_args = [
+      'analysis_id' => $some_analysis_id,
+      'cvterm' => $some_cvterm_id
+    ];
+    $file = ['file_local' => __DIR__ . '/../data/exampleFile.txt'];
+
+    // Create a new instance of our importer.
+    $importer = new \ExampleImporter();
+    $importer->create($run_args, $file);
+
+    // Before we run our loader we must let the TripalImporter prepare the
+    // files for us.
+    $importer->prepareFiles();
+    $importer->run();
+  }
+
+.. note::
+
+  We highly recommend you make use of database transactions in your tests, especially when running loaders.  Simply add ``use DBTransaction;`` at the start of your test class.  Please see the `Tripal Test Suite documentation for more information <https://tripaltestsuite.readthedocs.io/en/latest/>`_.

+ 49 - 0
docs/dev_guide/custom_field.rst

@@ -0,0 +1,49 @@
+Creating a Custom Field
+=======================
+
+The most common way that new content will be added to an existing site is by creating new fields, or field displays.  In Tripal v2 customizations were added by editing PHP templates files.  These template files were  relatively easy to create and customize, but they provided less flexibility and did not integrate well with other Drupal features such as GUI-based page layout and Drupal Views.  Tripal v3 fields now provide this flexibility.  They also support data exchange and data collections!
+
+By default Tripal v3 provides many fields for display of Chado data. However, you may find that these fields do not display data as you want, or you want to display data that the current fields do not already provide. This section of the Handbook describes how to create new fields that are integrated into the display, search and exchange abilities of both Drupal and Tripal.
+
+If you are already familiar with Drupal fields you may be aware of the API functions and hooks that Drupal provides.  However, for the quantity of fields needed to support biological data, the Drupal API hooks quickly become overwhelming.  Additionally, documentation for fields in the Drupal API can someitmes be difficult to discover when first working with fields.   Therefore, Tripal provides several new PHP classes to simplify creation of fields and to consolidate all functionality into one easy to find set of files.  To develop new fields you should be somewhat familar working with PHP's Object-Oriented Classes. The new classes provided by Tripal are these:
+
+
++----------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Class Name           | Description                                                                                                                                                                                                               |
++----------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| TripalField          | The TripalField class provides the basic information about a new field. It provides loaders for extracting data from the database and functions for querying data managed by the field.                                   |
++----------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| TripalFieldWidget    | The TripalFieldWidget class provides the necessary form elements when editing and Entity to allow the end-user to edit the value of the field (if desired). It provides the necessary validators and submittor functions. |
++----------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| TripalFieldFormatter | The TripalFieldFormatter class provides the visualization of the field when viewed on the page.                                                                                                                           |
++----------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| ChadoField           | The ChadoField class extends the TripalField class and provides the necessary settings to allow the field to map entities to data in Chado                                                                                |
++----------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| ChadoFieldWidget     | Extends the TripalFieldWidget class but currently provides no additional functionality. Use this class when working with Chado data to ensure future backwards compatibility.                                             |
++----------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| ChadoFieldFormatter  | Extends the TriplFieldFormatter class but currently provides no additional functionality. Use this class when working with Chado data to ensure future backwards compatibility.                                           |
++----------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+
+
+
+The process for creating a custom field are as follows:
+
+* Determine the controlled vocabulary term that best describes the data your field will create.
+* Decide if you need the Chado field classes or the base Tripal field classes.  If you intend to work with data housed in Chado then you should use the Chado field classes.
+* Decide if you want to build your class manually from the ground up or speed development by using the Staton Lab Fields Generator tool.
+* Create new implementations of classes that extend those listed in the table above.  If you implement the functions properly your field is plug-and-play!  Tripal will find it and be able to use it.
+
+The rest of this section will walk you through these steps.
+
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Table of Contents
+
+   custom_field/select_vocab_terms
+   custom_field/manual_field_creation
+   custom_field/custom_widget
+   custom_field/custom_formatter
+   custom_field/ajax_custom_formatter
+   custom_field/create_instance
+   custom_field/tripal_field_generator

+ 114 - 0
docs/dev_guide/custom_field/ajax_custom_formatter.rst

@@ -0,0 +1,114 @@
+AJAX Responsive Formatters
+===========================
+
+
+Some fields need to be responsive.  For example, a user might select an analysis or organism to display data from. Drupal developers often use AJAX to rebuild the page based on user input.
+
+Drupal and AJAX
+---------------
+
+Drupal has its own special way of doing AJAX! This is important to ensure that changes are executed in the correct order. You should `read the documentation carefully! <https://api.drupal.org/api/drupal/includes%21ajax.inc/group/ajax/7.x>`_  The Drupal AJAX API works best on forms, and field formatters **are not forms**.  Instead, they are `renderable arrays. <https://www.drupal.org/docs/7/api/render-arrays/render-arrays-overview>`_
+As such, rather than accepting ``$form`` and ``&$form_state``, they accept ``&$element``, ``$entity_type``, ``$entity``, ``$langcode``, ``$items``, and ```$display``, where ``$element`` is the renderable array.
+
+This means if you want to add an AJAX callback to a field formatter, you  need a **separate form function** that gets added in using ``drupal_get_form()``.  If you do this, you can build the AJAX as Drupal expects it.
+
+
+Example form and field
+----------------------
+
+Here's an example form file below: as you can see it's a standard form following Drupal AJAX conventions.  We provide a ``rendered_maps`` fieldset with the prefix defining the wrapper (``examplemap-featuremap-organism-selector-wrapper``).  This is what we want to re-draw depending on what the user selects.
+
+The selector has specified that wrapper, and the AJAX callback function ``examplemap_organism_featuremap_callback``.  We then define that function to simply return  the piece of the form that should be rebuilt: the ``rendered_maps`` fieldset!
+
+
+
+.. code-block:: php
+
+  /**
+   * AJAX-enabled form for [field formatter name].
+   */
+  function tripal_example_map_organism_featuremap_selector_form($form, &$form_state, $select) {
+
+    $selected = 0;
+
+    // $form_state['values'] will be set if the form has been submitted via AJAX
+    if (isset($form_state['values']['featuremap_select'])) {
+      $selected = isset($form_state['values']['featuremap_select']);
+    }
+
+    // We need to provide a container for Dupal AJAX to replace.
+    // Here we use a fieldset with a set ID which we can refer to below.
+    $form['rendered_maps'] = [
+      '#type' => 'fieldset',
+      '#collapsible' => FALSE,
+      '#prefix' => '<div id="examplemap-featuremap-organism-selector-wrapper">',
+      '#suffix' => '</div>',
+    ];
+
+    // This is the element which will trigger AJAX.
+    $form['rendered_maps']['featuremap_select'] = [
+      '#type' => 'select',
+      '#options' => $select,
+      '#title' => 'Please select a map to view',
+      '#default_value' => $selected,
+      '#ajax' => [
+        // Your Drupal AJAX callback
+        // which simply returns the form element to be re-rendered.
+        'callback' => 'examplemap_organism_featuremap_callback',
+        // This should be the ID you set above on your container to be replaced.
+        'wrapper' => 'examplemap-featuremap-organism-selector-wrapper',
+        'effect' => 'fade',
+      ],
+    ];
+
+    // Check the AJAX submitted values...
+    $chosen = 0;
+    if (isset($form_state['values']['featuremap_select'])) {
+      $chosen = $form_state['input']['featuremap_select'];
+    }
+
+    // If the user chose an option (triggered AJAX).
+    if ($chosen != 0) {
+      // Then change the form accordingly...
+      // Notice that you react to the AJAX change in the form
+      // not in the AJAX callback.
+      $mini_form = tripal_example_map_genetic_map_overview_form([], $form_state, $chosen);
+
+      $form['rendered_maps']['map'] = $mini_form;
+
+      return $form;
+    }
+
+    return $form;
+  }
+
+  /**
+   * The callback will return the part of the form you want to re-draw.
+   */
+  function examplemap_organism_featuremap_callback($form, &$form_state) {
+
+    return $form['rendered_maps'];
+  }
+
+
+
+In the field formatter, we simply add this form and put the markup in the element:
+
+.. code-block:: php
+
+    /**
+     * In our Our__field_formatter.inc
+     */
+    public function view(&$element, $entity_type, $entity, $langcode, $items, $display) {
+
+      // Select choices would be loaded in the base field's load method.
+      $select = $items[0]['select_choices'];
+
+      $form = drupal_get_form('tripal_example_map_organism_featuremap_selector_form', $select);
+      $content = drupal_render($form);
+      $element[] = [
+          '#type' => 'markup',
+          '#markup' => $content,
+      ];
+      return $element;
+    }

+ 223 - 0
docs/dev_guide/custom_field/create_instance.rst

@@ -0,0 +1,223 @@
+Attach Fields to Content Types
+==============================
+In summary, creation of a new field requires creation of three classes that inherit from the ``TripalField``, ``TripalFieldWidget`` and ``TripalFieldFormatter`` base classes.  If the fields are created correctly and placed in the ``includes/TripalFields`` directory of your module then Tripal will automatically find them.  However, the field is not yet attached to any content type. They must be attached.  Fields can be attached programmatically or via the online Drupal interface by a site admin. 
+
+The hook_bundle_field_info() function
+-------------------------------------
+ The three TripalField classes simply define how the field will function, but Drupal does not yet know about the field.  The ``hook_bundle_field_info`` function tells Drupal about your field. It must be implemented in a custom Drupal module, and provides an array that tells Drupal about the fields and the classes to use for the field.  Suppose we were creating a field named ``obi__genus`` which displays the Genus for a species and we have a custom module named ``tripal_org2``.  The hook function would be named ``tripal_org2_bundle_field_info()``:
+
+.. code-block:: php
+  :linenos:
+
+  function tripal_org2_bundle_field_info($entity_type, $bundle) {
+    $info = [];
+    
+    // Make sure this bundle is an organism (OBI:0100026) then we'll attach our 
+    // field to display the genus.
+    $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
+    $term_accession = $term->vocab->vocabulary . '__' . $term->accession;
+    if ($term_accession == 'OBI:0100026') {
+      $field_name = 'obi__genus';
+      $field_type = 'obi__genus';
+      $info[$field_name] = [
+        'field_name' => $field_name,
+        'type' => $field_type,
+        'cardinality' => 1,
+        'locked' => FALSE,
+        'storage' => [
+          'type' => 'field_chado_storage',
+        ],
+        'settings' => [],
+      ];
+   }
+    
+    return $info
+  }
+  
+This function receives as its second argument the ``$bundle`` object. This is the bundle that Drupal is requesting new fields for.  For this example we only want to attach the field if the content type is the organism content type.  The format of the returned ``$info`` array should have the field name as the key and an array that follows the instructions provided by Drupal's `field_create_field() <https://api.drupal.org/api/drupal/modules%21field%21field.crud.inc/function/field_create_field/7.x>`_ function. 
+
+The settings indicate the field name, the field type, the cardinality (how many values are allowed), any default settings and the storage type.  Because we expect our data to come from Chado we set the ``field_chado_storage`` as the type.  The ``locked`` setting is set to FALSE indicating that Drupal will allow the field to be deleted if the site developer desires.
+
+When the site administrator navigates to **Administer > Structure > Tripal Content Types**, clicks on a content type, and then the **manage fields** tab, a link appears at the top titled **Checkfor new fields**.  When that link is clicked, this hook function is called.
+
+Programmatically Attaching Fields
+---------------------------------
+You probably want to programmatically attach fields to content types if your have existing data that you know should be made available. For example, an organism always has a genus and only one genus.  If we have a field that displays the genus for an organism then we will want it automatically attached on installation of our module.  We can do this programmatically using two hook functions: ``hook_bundle_field_info()`` and ``hook_bundle_instance_info()``.  Both functions are required to attach a field to a content type. 
+
+The hook_bundle_instance_info() function.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The previous hook tells Drupal that our field exists and is allowed to be connected to the organism bundle.  Next we need to create an actual instance of this field for the bundle.  We do this with the ``hook_bundle_instance_info()`` function.  The format is the same as the previous hook but the info array is different.  For example:
+
+.. code-block:: php
+  :linenos:
+
+  function tripal_org2_bundle_instances_info($entity_type, $bundle) {
+    $info = []
+    
+    // Make sure this bundle is an organism (OBI:0100026) then we'll attach our 
+    // field to display the genus.
+    $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
+    $term_accession = $term->vocab->vocabulary . '__' . $term->accession;
+    if ($term_accession == 'OBI:0100026') {
+    
+      $field_name = 'obi__genus';
+      $is_required = FALSE;
+      $info[$field_name] =  [
+        'field_name' => $field_name,
+        'entity_type' => $entity_type,
+        'bundle' => $bundle->name,
+        'label' => 'Genus',
+        'description' => 'The genus for the organism',
+        'required' => TRUE,
+        'settings' => [
+          'auto_attach' => TRUE,
+          'chado_table' => 'organism',
+          'chado_column' => 'genus',
+          'base_table' => 'organism',
+          'term_accession' => '0000005',
+          'term_vocabulary' => 'TAXRANK',
+          'term_name' => 'Genus',
+        ],
+        'widget' => [
+          'type' => 'obi__genus_widget',
+          'settings' => [
+            'display_label' => 1,
+          ),
+        ],
+        'display' => [
+          'default' => [
+            'label' => 'inline',
+            'type' => 'obi__genus_formatter',
+            'settings' => [],
+          ],
+        ],
+      ];
+    }
+    return $info;
+  }
+  
+The format of the returned ``$info`` array should have the field name as the key and an array that follows the instructions provided by Drupal's `field_create_instance() <https://api.drupal.org/api/drupal/modules%21field%21field.crud.inc/function/field_create_instance/7.x>`_ function. 
+
+Unique to this info array are the settings related to Chado.  Because we expect our data to be loaded from Chado we must specify these settings:
+
+ - ``base_table``: the name of the base table to which the record will be associated. In our case the ``organism`` table of Chado is the base table.
+ - ``chado_table``: the name of the actual table form which the value of the field will be loaded or saved to.  In our case the ``organism`` table is also the ``chado_table``.  
+ - ``chado_column``: the name of the column in the ``chado_table`` where the data is loaded from. if the ``base_table`` and ``chado_table`` are the same then this is the name of the column. In our case the ``genus`` columns.  If the base and chado tables are different then it is the name o the primary key column in the ``chado_table``
+ - ``auto_attach``:  set this to TRUE if you want the field to automatically be added to an entity when it is generated for viewing.  Set it to FALSE to allow the field to be added via AJAX. For fields that require time to load setting to FALSE is preferred. 
+ 
+.. note::
+  A base table is one that contains the primary records to which ancilliary data (e.g. properties, cross references, CV terms, publications, contacts, etc) are associated via linker tables. For example some base tables include: ``feature``, ``organism``, ``stock``, ``library``, etc.).  The ``base_table`` and ``chado_table`` will always be the same when you are mapping a field to data in a column in a base table. If your field maps data to a "linker" table where ancilliary data is stored then the ``chado_table`` will be the linker table.
+
+Notice as well that the ``display`` and ``widget`` sections list the name of our TripalEntityWidget and TripalEntityFormatter calsses respectively.  This tells drupal to use our widget and formatter classes by default.
+
+When the site administrator navigates to **Administer > Structure > Tripal Content Types**, clicks on a content type, and then the **manage fields** tab, a link appears at the top titled **Checkfor new fields**.  When that link is clicked, this hook function is called.  
+
+.. note::
+
+  Both hook functions must be properly constructed for the field to be automatically attached to the content type.
+  
+Allowing Manual Attachment of Fields
+------------------------------------
+Not all fields are created equal.  Some field can be added by the site developer to a bundle and some cannot.  When the ``TripalField`` class is implemented for a class the ``$no_ui`` parameter is set to indicate if a field can be added via the web interface or not.  See the :doc:`manual_field_creation` page for more details. But in short the following setting does not allow a field to be added using the web interface
+
+.. code-block::  php
+
+ public static $no_ui = FALSE;
+ 
+The following setting will allow the field to be added:
+
+.. code-block::  php
+
+ public static $no_ui = TRUE;
+
+Next, we must let Drupal know that our field exists.  We do this by adding an entry to the ``$info`` array of in the ``hook_bundle_field_info()`` function described above.  This lets Drupal know about our field. However, because we are not programmatically creating an instance of the field on a content type, but allowing the user to create them we do not need to implement the ``hook_bundle_instance_info()`` function. Instead, we must implement the ``hook_bundle_create_user_field()``.  This function is called when the user attempts to add our new field to a bundle.  One field that comes with Tripal is the ``chado_linker__prop`` field.  Most Chado base tables have an associated property table (e.g. ``organismprop``, ``featureprop``, ``stockprop``, etc). By default, the ``tripal_chado`` module automatically adds this field to all bundles that have existing properties. It adds a new instance for every property type.  However, new properties can be added to bundle, and the site admin may want to add those properties via the user interface rather. Therefore, this field has the ``$no_ui`` set to TRUE and uses the  ``hook_bundle_create_user_field()`` to create the new field instance for the user.
+
+The following code is a snippet from the ``tripal_chado_bundle_create_user_field`` function of the ``tripal_chado`` module. Note that it uses the ``field_create_field`` function and the ``field_create_instance`` functions directly.  The arrays passed to these functions are identical to the ``$info`` arrays of both the ``hook_bundle_field_info`` and ``hook_bundle_instance_info`` functions described above.
+
+.. code-block:: php
+  :linenos:
+  
+  function tripal_chado_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,
+    );
+    $chado_table = $bundle->data_table;
+    $chado_type_table = $bundle->type_linker_table;
+    $chado_type_column = $bundle->type_column;
+    $chado_type_id = $bundle->type_id;
+    $chado_type_value = $bundle->type_value;
+  
+    // 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'] == 'chado_linker__prop') {
+      $table_name = $chado_table . 'prop';
+  
+      if (chado_table_exists($table_name)) {
+        $schema = chado_get_schema($table_name);
+        $pkey = $schema['primary key'][0];
+        $field_name = $new_field['field_name'];
+        $field_type = 'chado_linker__prop';
+  
+        // 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_chado_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' => TRUE,
+            'base_table' => $chado_table,
+            'chado_table' => $table_name,
+            'chado_column' => $pkey,
+            'term_vocabulary' => '',
+            'term_accession' => '',
+            'term_name' => ''
+          ),
+          'widget' => array(
+            'type' => 'chado_linker__prop_widget',
+            'settings' => array(
+              'display_label' => 1,
+            ),
+          ),
+          'display' => array(
+            'default' => array(
+              'label' => 'inline',
+              'type' => 'chado_linker__prop_formatter',
+              'settings' => array(),
+            ),
+          ),
+        ));
+      }
+      else {
+        drupal_set_message('Cannot add a property field to this entity. Chado does not support properties for this data type.', 'error');
+      }
+    }
+  }
+
+
+
+.. note::
+  
+  It is possible to have a field that is both programmtically attached to some content types but is also allowed to be attached to another content type by the site admin using the web interface. To do this, programmatically add the field to the content types using the ``hook_bundle_instance_info`` function and also implement the ``hook_bundle_create_user_field`` function to support manual adding.
+  
+ 

BIN
docs/dev_guide/custom_field/custom_formatter.pager.1.png


+ 208 - 0
docs/dev_guide/custom_field/custom_formatter.rst

@@ -0,0 +1,208 @@
+Creating a Custom Formatter
+===========================
+The third component of a field is the formatter.  Thus far we have introduced how to create a field class and a widget class for a field.  The field class is responsible for describing the field, loading data into it, and providing search support.  The widget class provided a Drupal form for online editing of the field.  Finally, the formatter is responsible for display of the field on a Tripal site.  
+ 
+.. note::
+  This guide assumes you already have your formatter class file created. For more information, see :doc:`manual_field_creation` or, :doc:`tripal_field_generator`. 
+  
+The formatter class is the simplest of all the Tripal field classes.  Here we will again use the **obi__organism** field that comes with the ``tripal_chado`` module.  
+
+The view() function.
+~~~~~~~~~~~~~~~~~~~~
+In most cases the only function you need to implement is the ``view()`` function. This function is called whenever your field needs to be displayed on a page. The following code is from the ``obi__organism_formatter.inc`` class file.  
+
+.. code-block:: php
+  :linenos:
+
+  public function view(&$element, $entity_type, $entity, $langcode, $items, $display) {
+  
+    if ($items[0]['value']) {
+      $content = $items[0]['value']['rdfs:label'];
+      if (array_key_exists('entity', $items[0]['value'])) {
+        list($entity_type, $entity_id) = explode(':', $items[0]['value']['entity']);
+        $content = l(strip_tags($items[0]['value']['rdfs:label']), 'bio_data/' . $entity_id);
+      }
+
+      // The cardinality of this field is 1 so we don't have to
+      // iterate through the items array, as there will never be more than 1.
+      $element[0] = array(
+        '#type' => 'markup',
+        '#markup' => $content,
+      );
+    }
+  }
+  
+In the code above the input arguments have the following meaning:
+  - ``$element`` is the first argument. It is an array into which you should set the contents to be displayed.  
+  - ``$entity_type`` will always have the value ``Tripal Entity``.
+  - ``$entity`` is the entity object which contains all information about the entity including the loaded data values.
+  -  ``$langcode`` is the language. This is used by Drupal to provide translations of data into other spoken languages. By default, Tripal does not use a language, as biological data is generally language agnostic.  Consider for example a gene sequence or a feature coordinate.
+  - ``$items`` is an array containing all of the loaded data for this field.  
+  - ``$display`` is the name of the display such as full page, a teaser, etc. Currently, Tripal does not distinguish between displays.
+  
+The purpose of the ``view()`` function is to iterate through the values in the ``$items`` array, and format them into an appropriate display for viewing.  Here you must remember the structure of the data in the ``$items`` array.  
+ 
+To demonstrate this function, let's look at what we expect in our ``$items`` array. Using the `Citrus sinesis` organism from the User's Guide. We would expect an items array to look like the following:
+ 
+.. code::
+
+  $items = [
+    0 => [
+      "value" => [
+        "rdfs:label" =>  "Citrus sinensis",
+        "rdfs:type" =>  "Organism",
+        "local:abbreviation" =>  "C. sinensis",
+        "TAXRANK:0000005" => "Citrus",
+        "TAXRANK:0000006" => "sinensis",
+        "entity" => "TripalEntity:3",
+      ],
+      "chado-feature__organism_id" => 12,
+    ],    
+  ];
+  
+You may recall that the ``$items`` array structure is the same as that created by the ``load()`` function described in the :doc:`manual_field_creation` page. Note that each key in the ``value`` array is an accession for a controlled vocabulary term.  These accessions are used to unambiguously describe the value. To display the organism on a page we need the element named ``rdfs:label``.  Thus, we set the ``$content`` variable to contain this value as shown on line 4 of the ``view()`` function above.
+
+Because our organisms are also published entities we want to link to their respective pages each time an organism is displayed.  Because the ``value`` array has an element named ``entity`` we know that this item is published.  Lines 5-6 of the ``view()`` function shown above use this information to create a clickable link to the organism page.   Finally, the ``$element`` argument is set to provide content of type ``markup``.  This ``$element`` array is a `Drupal renderable array <https://www.drupal.org/docs/7/api/render-arrays/render-arrays-overview>`_.
+
+Lastly, notice the element named ``chado-feature__organsim_id``.  This element is at the same level as the ``value`` element.  This data is meant to be used internally by the field. It maps this fields values to the appropriate table in Chado where the data is stored.  
+
+.. warning:: 
+
+  You should never show the user any data that is outside of ``value`` element.  Remember that your field can be shown by other viewers, including web services.  By ensuring that data in the ``value`` element is mean to be displayed we ensure that information on the web page, web services, or any othe future form of display is always consistent.
+
+In summary, the following should be observed when processing the ``$items`` array for viewing:
+
+  - A field with only one value (a cardinality of 1) will always have only one element in the ``$items`` array and can use the index 0. This is what has been done in this example code. 
+  - A field with more than one value can have any number of elements in the ``$items`` array.  You should therefore iterate through all of them.
+  - For every index in ``$item`` you should create a matching index in ``$element`` to display the data found in that ``$item``.
+  - If there are no items, then nothing you return will be displayed.
+  - For each element in the ``$items`` array there is a ``value`` key.  Only the data in the ``value`` key should be shown to the user.
+  - Each element in the ``$items`` array may have more than a ``value`` key.  These values are meant to help manage the data. 
+
+.. warning::
+
+  You should never have SQL statments or any API calls that retreive data in the foramter ``view()`` function. The formatter should strictly format data for viewing.
+  
+Creating Pagers
+~~~~~~~~~~~~~~~
+The example shown in the previous section was for a field that will always only contain a single element.  However some fields may contain a large number of elements.  Consider an mRNA and it's relationships to subfeatures: exons, 5' UTRs, 3'UTRs, CDS, etc.).  A large mRNA can have many relationships.  Alternatively, consider the case where a genentic map content type may have a field that lists all of the markers on the map.  Such a list could become extremely long on the page.  In these cases it may be best to only list a few items at a time and to provide a pager to let the user cycle through the items.  An example of a pager added to the bottom of relationships is shown in the example below.
+
+.. image:: custom_formatter.pager.1.png
+
+To create a pager we first need to calculate the number of items we want to display per page and the total number of pages required to display all of the data.  
+
+.. code-block:: php
+  
+  $items_per_page = 10;
+  $total_records = count($items);
+  $total_pages = (int) ($total_records / $items_per_page) + 1;
+  
+Next, we must initialize the pager by calling the ``pager_default_initialize`` function.  We pass it the total number of records, the number of items per page and the index (i.e. ``$pelement``) for this pager on the page.  
+
+.. code-block:: php
+
+  $pelement = 0; 
+  $current_page = pager_default_initialize($total_records, $items_per_page, $pelement);
+  
+The call to ``pager_default_initialize`` will return the current page.  The current page is a numeric number indicating which page the pager is currently showing. The first time the page is loaded this will always be the first page.  Each time the user navigates to other pages by clicking the "next" link or the numeric links then this ``view()`` function is called and the current page is set to the page being viewed. Next, we must theme the pager so that it follows the look-and-feel prescribed for the site. For this we use the Drupal ``theme()`` function.
+
+.. code-block:: php
+
+  $pager = theme('pager', array(
+    'tags' => array(),
+    'element' => $pelement,
+    'parameters' => array(),
+    'quantity' => $total_pages,
+  ));
+  
+By default, all links in the pager cause the page to reload.  We do not want the page to reload, rather we only want to update the contents of the field.  The TripalFieldFormatter class provides a function named ``ajaxifyPager`` to convert a pager into an AJAX pager:
+
+.. code-block:: php
+
+  $pager = $this->ajaxifyPager($pager, $entity);
+  
+Now that we have a pager, it has been setup for AJAX and we know the current page that the user is viewing we can now display only the items from the ``$items`` array that are appropriate for the page being viewed. A common way to provide multiple items on a page is within a table. When we set the ``$element`` array we need to be sure to provide both the content and the pager:
+
+.. code-block:: php
+
+    $element[0] = array(
+      '#type' => 'markup',
+      '#markup' => $content . $pager,
+    );
+    
+The settingsForm() Funtion.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Sometimes you may want to provide some control to the site developer for the formatter.  For example, the ``sbo__relationship_formater`` allows the site developer to customize the title that appears above the table that houses relationships and the text the appears if there are no relationships.  By default the title is "Relationships" and the empty text indicates there are no relationships. Both are a bit too generic.  The ``settingsForm()`` function allows you to provide a Drupal form for the field that appears on the **Administer > Strucutre > Tripal Content Types** on any content type's **manage display** page:
+
+.. image:: custom_formatter.settings.1.png
+
+The form shown in the screenshot above is provided by the ``settingsForm()`` function.  The following code generates this form:
+
+.. code-block:: php
+  :linenos:
+  
+  public function settingsForm($view_mode, $form, &$form_state) {
+
+    $display = $this->instance['display'][$view_mode];
+    $settings = $display['settings'];
+    $element = array();
+    $element['title'] = array(
+      '#type' => 'textfield',
+      '#title' => 'Table Header',
+      '#default_value' => array_key_exists('title', $settings) ? $settings['title'] : 'Relationship',
+    );
+    $element['empty'] = array(
+      '#type' => 'textfield',
+      '#title' => 'Empty text',
+      '#default_value' => array_key_exists('empty', $settings) ? $settings['empty'] : 'There are no relationships',
+    );
+  
+    return $element;
+  }
+  
+The form is typical of any form.  Note, however that the ``#default_value`` is set using the current settings values.
+
+A settings form is useful but it only works when Drupal knows what settings you want for your field.  You must provide the settings names (e.g. "title" and "empty" in this case) when you  attach your field to a given content type (i.e. bundle).  You tell Drupal to attach this field to a content type using the ``hook_bundle_instance_info`` function.  See 
+the :doc:`create_instance` to learn more about this function.  Briefly, the ``display`` section of the info array for the ``sbo__relationship`` field contains the following settings for the ``display``:
+
+.. code-block:: php
+
+    'display' => array(
+      'default' => array(
+        'label' => 'hidden',
+        'type' => 'sbo__relationship_formatter',
+        'settings' => array(
+          'title' => 'Relationships',
+          'empty' => 'There are no relationships'
+        ),
+      ),
+    ),
+
+.. warning::
+
+    In order for the ``settingsForm()`` implemented to be available on the "Manage Display" page, you must also implement ``settingsSummary()`` as described below.
+The settingsSummary() Function.
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The ``settingsSummary()`` function provides a summary of the current settings values for a field on the **manage display** page.  The following shows the same relationship field from the previous section, but with the settings form closed, and a summary of the current values shown:
+
+.. image:: custom_formatter.settings.2.png
+
+An example of the ``settingsSummary()`` function that generates the summary in the image above is as follows:
+
+.. code-block:: php
+  :linenos:
+  
+  public function settingsSummary($view_mode) {
+    $display = $this->instance['display'][$view_mode];
+    $settings = $display['settings'];
+
+    $summary = t('Title: @title<br>Empty: @empty',
+        array(
+          '@title' => $settings['title'],
+          '@empty' => $settings['empty'])
+        );
+
+    return $summary;
+  }
+
+  

BIN
docs/dev_guide/custom_field/custom_formatter.settings.1.png


BIN
docs/dev_guide/custom_field/custom_formatter.settings.2.png


+ 153 - 0
docs/dev_guide/custom_field/custom_widget.rst

@@ -0,0 +1,153 @@
+Creating a Custom Widget
+========================
+
+In Drupal/Tripal terminology, **widget** refers to the form elements for a specific Tripal Field on the "Edit" form of a piece of Tripal Content. For example, the ``obi__organism`` field widget creates the "Organism" drop-down on the edit form of a gene. All fields come with a default widget; however, you can create a custom widget if the default one doesn't meet your needs.
+
+.. note::
+  This guide assumes you already have your widget class file created. For more information, see :doc:`manual_field_creation` or, :doc:`tripal_field_generator`.
+
+.. note::
+	If you are only creating a widget and not the whole field, you still need to follow the expected directory structure. For example, if your widget is going to be named ``obi__organism_fancy`` then your file would be ``[your_module]/includes/TripalField/obi__organism_fancy/obi__organism_fancy_widget.inc``.
+
+The Form
+--------
+
+The form elements of your widget are defined in the ``form()`` method of your widget according to the `Drupal Form API <https://api.drupal.org/api/drupal/developer%21topics%21forms_api_reference.html/7.x>`_. As such the ``$widget`` variable is actually a nested associative array describing what the widget portion of the form should look like. For example, the following is how the ``obi__organism`` widget creates the drop-down.
+
+.. code-block:: php
+
+  /**
+   * @see TripalFieldWidget::form()
+   */
+  public function form(&$widget, &$form, &$form_state, $langcode, $items, $delta, $element) {
+
+    $field_name = $this->field['field_name'];
+    $field_table = $this->instance['settings']['chado_table'];
+    $linker_field = 'chado-' . $field_table . '__organism_id';
+
+    // The value presented to the user via load.
+    // If $items['delta']['value'] is set then we are updating and already have this
+    // information. As such, simply save it again.
+    $widget['value'] = array(
+      '#type' => 'value',
+      '#value' => array_key_exists($delta, $items) ? $items[$delta]['value'] : '',
+    );
+
+    // Pull out the value previously saved to be used as the default.
+    $organism_id = 0;
+    if (count($items) > 0 and array_key_exists($linker_field, $items[0])) {
+      $organism_id = $items[0][$linker_field];
+    }
+
+    // Define a drop-down form element where the options are organisms retrieved using
+    // the Tripal API, the default is what we looked up above, and the title and
+    // description are those set when defining the field.
+    $widget[$linker_field] = array(
+      '#type' => 'select',
+      '#title' => $element['#title'],
+      '#description' => $element['#description'],
+      '#options' => chado_get_organism_select_options(FALSE),
+      '#default_value' => $organism_id,
+      '#required' => $element['#required'],
+      '#delta' => $delta,
+    );
+
+  }
+
+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. Convention is to store the value of the field as a hidden ``value`` form element as is shown in the above example.
+
+.. note::
+	For more information on how to use the Drupal Form API, check out the `official Drupal Documentation <https://www.drupal.org/docs/7/api/form-api>`_.
+
+.. note::
+	The current item is saved in ``$items[$delta]`` as an array where the keys will match those set by the field ``load()`` function.
+
+
+Validation
+----------
+
+The ``validate()`` function of your widget allows you to confirm that the values entered by the user are valid. It is recommended to consider each form element you created above and consider what is required for that element to be entered "correctly". For example, for an organism drop-down, the organism chosen must exist in our chado database (since this is a ``ChadoFieldWidget``). Luckily this doesn't need to be validated since Drupal ensures only elements in our select list are chosen.
+
+.. warning::
+	The ``value`` key of this field must be set in the ``$form_state['values']`` array to a **TRUE** value (e.g. a string or non-zero integer) anytime data is entered by the user.
+
+.. note::
+	For more information on how to validate your data, see the official `Drupal Form Validation Documentation <https://www.drupal.org/docs/7/creating-custom-modules/validating-the-data>`_
+
+Saving the Data
+---------------
+
+The Drupal Storage Backend handles saving of your widget data. As such, **you do not and should not insert, update or delete the data yourself**. It should happen automatically, assuming you've followed the conventions of the specific storage backend.
+
+Chado Fields utilize the chado storage backend to save your data. Thus to ensure your data is saved, you set the columns of your chado table to the values you want them set via the ``$form_state['values']`` array using the ``chado-[table]__[column]`` convention. This should be done at the end of the validation function above, if the data submitted is valid.
+
+For our ``obi__organism`` example, the drop-down returns the chado organism_id of the record chosen by the user. We would like to save that as the organism_id of the chado table the field references, which the following code specifies.
+
+.. code-block:: php
+
+  /**
+   * @see TripalFieldWidget::validate()
+   */
+  public function validate($element, $form, &$form_state, $langcode, $delta) {
+
+    $field_name = $this->field['field_name'];
+    $field_table = $this->instance['settings']['chado_table'];
+    $linker_field = 'chado-' . $field_table . '__organism_id';
+
+    //...
+    // Validate your data here
+    //...
+
+    // In this case, if you have an organism_id, then your user selected this field.
+    $organism_id = $form_state['values'][$field_name]['und'][0][$linker_field];
+    if ($organism_id > 0) {
+      $form_state['values'][$field_name]['und'][0]['value'] = $organism_id;
+      // This is where we tell the storage backend what we want to save.
+      // Specifically, that we want to save $organism_id to $field_table.organism_id
+      $form_state['values'][$field_name]['und'][$delta][$linker_field] = $organism_id;
+    }
+  }
+
+But what do you do if the record you want to link to via foreign key constraint doesn't yet exist? Luckily the Chado Storage API has a solution for this as well. Consider the example of the ``sbo__relationship_widget``. When this widget is on the create form for a given content type, we will first need to create the base record before we can create a relationship to it. This is done by setting the values you do know (e.g. ``chado-feature__type_id`` and ``chado-feature__object_id``) but nnot setting the column mapping to the base record. The Chado Storage API will then fill it in automatically once the base record is created.
+
+.. code-block:: php
+
+  /**
+   * @see TripalFieldWidget::validate()
+   */
+  public function validate($element, $form, &$form_state, $langcode, $delta) {
+
+    $field_name = $this->field['field_name'];
+    $field_table = $this->instance['settings']['chado_table'];
+    $linker_field = 'chado-' . $field_table . '__organism_id';
+
+    //...
+    // Validate your data here
+    //...
+
+    //...
+    // Determine the subject_id, object_id and type_id based on user input.
+    // User input is found in $form_state['values'].
+    //...
+
+    // If we have all the keys then set the columns as in the obi__organism ex.
+    if ($subject_id && $object_id && $type_id) {
+      // Set all chado fields to their values.
+    }
+    // Otherwise, maybe we are creating the entity...
+    // The storage API should handle this case and automagically add the key in // once the chado record is created... so all we need to do is set the
+    // other columns.
+    elseif ($subject_name && $object_id && $type_id) {
+      $form_state['values'][$field_name][$langcode][$delta]['value'] = 'value must be set but is not used';
+      $form_state['values'][$field_name][$langcode][$delta]['chado-' . $field_table . '__' . $object_id_key] = $object_id;
+      $form_state['values'][$field_name][$langcode][$delta]['chado-' . $field_table . '__type_id'] = $type_id;
+      // Notice that the subject_id is not set here.
+    }
+    // Otherwise, we don't have a value to insert so leave them blank.
+    else {
+      // Set all chado fields to empty string.
+    }
+
+Drupal typically does not provide a submit hook for fields because, as mentioned above, saving should be done by the storage backend. However, the TripalField provides a ``TripalFieldWidget::submit()`` to allow for behind-the-scenes actions to occur. This function should never be used for updates, deletes or inserts for the Chado table associated with the field as these actions should be handled by the storage backend.
+
+However, it is permissible to perform inserts, updates or deletions within Chado using this function.  Those operations can be performed if needed but on other tables not directly associated with the field. An example is the ``chado.feature_synonym`` table.  The ``chado_linker__synonym`` field allows the user to provide a brand new synonynm and it must add it to the chado.synonym table prior to the record in the chado.feature_synonym table.

+ 513 - 0
docs/dev_guide/custom_field/manual_field_creation.rst

@@ -0,0 +1,513 @@
+Manual Field Creation
+======================
+To show how a TripalField works we will break down a class implementation section by section.  Here we will use the **obi__organism** field that comes with Tripal and which extends the ChadoField class.  The ChadoField class is almost identical to the TripalField class except that it provides a few extra settings for working with Chado tables.   To create your own class you need to create a new class that implements the necessary functions.
+
+.. note::
+  Creation of your first field may not seem easy!  The following document is a lot to think about and consider. Therefore, when you write your first field, don't try to do everything at once. Take it one piece at a time.  The variables and functions described here are in order with the most critical components described first.  Take it at an even pace.
+
+
+Directory Structure for Fields
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Before we create our class we must first create a proper directory structure.  Tripal expects that all new Tripal field classes are located inside of a custom module in the following directory structure:
+
+.. code-block:: bash
+
+  /sites/all/modules/[your_module]/includes/TripalField/[field_name]/[field_name].inc
+  /sites/all/modules/[your_module]/includes/TripalField/[field_name]/[field_name]_widget.inc
+  /sites/all/modules/[your_module]/includes/TripalField/[field_name]/[field_name]_formatter.inc
+
+
+In the directories above the token [your_module] can be substituted with the name of your module and [field_name] is the name of your field.  You can name your field whatever you like, but you must use this name consistently in other locations throughout the modules.  Because all fields are defined by vocabulary terms, it is custom to name your fields with the vocabulary **short name** followed by two underscores followed by the **term name**, hence:  obi__organism.  Here the ChadoField implementation goes in the [field_name].inc file, the ChadoFieldWidget in the [field_name]_widget.inc file and the ChadoFieldFormatter in the [field_name]_formatter.inc.   All new fields must implement all three classes.   in the case of our obi__organism field the directory structure is as follows:
+
+.. code-block:: bash
+
+  /sites/all/modules/tripal/tripal_chado/includes/TripalField/obi__organism/obi__organism.inc
+  /sites/all/modules/tripal/tripal_chado/includes/TripalField/obi__organism/obi__organism_widget.inc
+  /sites/all/modules/tripal/tripal_chado/includes/TripalField/obi__organism/obi__organism_formatter.inc
+
+Anatomy of the ChadoField Class
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The following describes a ChadoField class from top to bottom. The code for the obi__organism field is shown in order that it appears in the class with descriptions provided for the meaning of each piece of code.  To write your own class, duplicate the variables and function and customize accordingly.  First, let's look at the definition of the class.  The following line defines the class and indicates that it extends the ChadoField class:
+
+.. code-block:: php
+
+  
+
+  class obi__organism extends ChadoField {
+
+.. note::
+
+  In the line above, the class is named obi__organism. This must be the same name as the directory in which the field is located. Otherwise, Tripal won't be able to find the field.
+
+Static Member Variables
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+Next, the TripalField/ChadoField class has a section of public static variables. These are variables that you can customize to describe your field to Tripal. Here you will provide the default label that appears for the field, and a description for the field:
+
+.. code-block:: php
+
+  
+
+  // The default label for this field.
+  public static $default_label = 'Organism';
+
+  // The default description for this field.
+  public static $description = 'The organism to which this resource is associated.';
+
+As described in the section titled Tripal Data Structures, fields that are attached to Bundles are "instances" of a field. Every field instance can be customized differently on each bundle.  The following section of code allows your field to provide custom settings.  Here we want to "hard-code" the term that defines this field using the $default_instance_settings variable:
+
+
+.. code-block:: php
+
+  
+  // 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. shcema, SO, GO, PATO, etc.).
+    'term_vocabulary' => 'OBI',
+    // The name of the term.
+    'term_name' => 'organism',
+    // The unique ID (i.e. accession) of the term.
+    'term_accession' => '0100026',
+    // Set to TRUE if the site admin is allowed to change the term
+    // type. This will create form elements when editing the field instance
+    // to allow the site admin to change the term settings above.
+    'term_fixed' => FALSE,
+    // The format for display of the organism.
+    'field_display_string' => '<i>[organism.genus] [organism.species]</i>',
+  );
+
+Notice in the code above that the elements **term_vocabulary, term_name** and **term_accession** are used to define the vocabulary term that this field maps to.  The term_fixed element allows the term to be changed by the site admin if desired.  These elements are required of all TripalFields classes.  You must always have these elements.  However, the **field_display_string** is a variable unique to this obi__organism field!  Because this field is displaying the organism we want to allow the site-admin to customize how the organism name is constructed and displayed.  Therefore, the **field_display_string** creates this new setting for us.  How this setting is used will be described later.
+
+As you may have noticed, a field requires a widget and a formatter.  This is why there are three classes for every field.  However, Drupal is flexible and allows fields to be edited or displayed by any number of widgets and formatters.  By default, Tripal provides one widget class and one formatter class for every field.  When you write a new field you will need to do the same and create a new ChadoFieldWidget and ChadoFieldFormatter class (or the corresponding non-Chado versions if you don't need Chado).  The following variables in the class indicate what are the default widget and formatter classes (we have not yet created those, but we know their names!):
+
+.. code-block:: php
+
+  
+  // The default widget for this field.
+  public static $default_widget = 'obi__organism_widget';
+
+  // The default formatter for this field.
+  public static $default_formatter = 'obi__organism_formatter';
+
+Drupal allows new instances of fields to be attached to any Bundle.  This is really useful for fields like the built in Image field that Drupal provides.  It can be very handy to attache an instance of an Image field to any content type and viola! your content type now supports images.  However, there are some fields that should never be added via the online Drupal interface.  Our organism field is a good example.  We probably don't want to allow end-users to add an organism field to a Person content type...  In this case we will programmatically control which fields are attached to which Bundles.  We'll show that later.  But for now, let's set the no_ui variable to TRUE to prevent users from adding our new field to any Bundle.
+
+.. code-block:: php
+
+  
+  // 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 = TRUE;
+
+
+Sometimes a field is meant to provide a visualization or some other functionality.  An example of this might be a small search form or link to an analytical service.  In these cases we want the field to show up on the web page but it should not appear anywhere else, such as in Tripal's web service that provides access to all content.   We can set the no_data variable to TRUE and this will allow it to be seen on the site, but not anywhere else.
+
+.. code-block:: php
+
+  
+  // A boolean specifying that the field will not contain any data. This
+  // should exclude the field from web serivces 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;
+
+
+  .. note::
+  Be sure to only set this to TRUE when you are absolutely certain the contents would not be needed in web services.  Tripal was designed so that what appears on the page will always appear in web services.  Aside form the formatting we see on the website, the content should be the same.
+
+Finally, the last item in our Class variables is the **download_formatters**.  Tripal provides an API that allows tools to group entities into data collections.  Data collections are like "baskets" or "shopping carts".   Entities that are in data collections can be downloaded into files.  If your field is compatible with specific file downloaders you can specify those here.  A file downloader is a special TripalFieldDownloader class that "speaks" certain file formats.  Tripal, by default, provides the TripalTabDownloader (for tab-delimited files), the TripalCSVDownloader (for CSV files), a TripalNucFASTADownloader for creating nucleotide FASTA files and a TripalProteinFASTADownloader for protein FASTA files.   If your field is compatible with any of these formatters you can specify them in the following array:
+
+.. .. code-block::
+
+  // Indicates the download formats for this field.  The list must be the
+  // name of a child class of the TripalFieldDownloader.
+  public static $download_formatters = array(
+    'TripalTabDownloader',
+    'TripalCSVDownloader',
+  );
+
+
+If your field is compatible with the TripalTabDownloader, for example, your field will be included as a column in a tab delimited file where each row represents contents for a given entity.
+
+The load() function.
+~~~~~~~~~~~~~~~~~~~~~
+
+The first function we want to implement in our class is the load() function.   This function is responsible for querying the database and populating the field value.  Data that is loaded into the field must be organized in two ways: 1) a value that is visible to the end-users, and 2) values that are visible to Chado for ensuing update/editing of the correct record in Chado when the field is edited.  Our obi__organism field is designed to be used for multiple Bundles therefore the code in our load() function must be able to support any Chado table that has a foreign key relationship with the organism table.
+
+To get started, the load() function receives a single argument. The entity object:
+
+.. code-block:: php
+
+  public function load($entity) {
+
+
+Because this is a ChadoField and the TripalChado module supports this field and maps entities to their "base" record on Chado, we get something extra... we get the record itself
+
+.. code-block:: php
+
+    $record = $entity->chado_record;
+
+Having the record helps tremendously.  Our **obi__organism** field is meant to be attached to genomic feature content types (e.g. genes, mRNA, etc.), germplasm, etc.  Therefore, the entity will be a record of one of those types. In the case of a genomic feature, these come from the **feature** table of Chado.  In the case of a germplam, these records come from the **stock** table of Chado.  Both of these records have an **organism_id** field which is a foreign key to the organism table where we find out details about the organism.
+
+Before we set the values for our field, we need a little bit more information.  Remember that all field instances have settings?   The Tripal Chado module also populates for us the name of the Chado table and the column that this field maps to.  Our obi__organism field can be used for multiple Bundles.  A gene bundle would map to the **feature** table of Chado and a germplasm Bundle would map to the **stock** table.  We need to know what table and column this field is mapping to:  We can get that from the instance object of the class and its settings:
+
+.. code-block:: php
+
+    $settings = $this->instance['settings'];
+    $field_table = $this->instance['settings']['chado_table'];
+    $field_column = $this->instance['settings']['chado_column'];
+
+Next, we want to get this field name and its type.  We obviously know our field name, it is obi__organism.  However, we can get the name programmatically as well.  Drupal maintains an "informational" array about our field.  Inside of that field array we can find lots of interesting information such as our field name and its type (Bundle).  We'll need this when we set our field value.  But rather than hard-code it, let's grab it programmatically from the field name.  It's best to grab it programmatically because there are cases where the field name could change:
+
+.. code-block:: php
+
+    $field_name = $this->field['field_name'];
+    $field_type = $this->field['type'];
+
+
+Now, let's plan how we want our values to appear in our field.  The organism record of Chado v1.3 has a genus, species, abbreviation, infraspecific name, infraspecific type, and a common name.  We want these values exposed to the end user.  But, wait... when we discussed fields in the Tripal Data Structures section we learned about a name field that provides names for entities.  That field only has one value: the name.  Our organism field has multiple values (i.e. genus, species, etc.).   A field can provide more than just one value but values have to be qualified.  We have to provide values in key/value pairs, and the keys must be controlled vocabulary terms.  We must use controlled vocabulary terms because we want our field to be searchable by other Tripal sites.  For example, the ontology term for the word 'genus' comes from the TAXRANK vocabulary.  Fortunately, almost every column of every table in Chado has been mapped to a controlled vocabulary term so we don't need to go hunting for terms.  We can use a Chado API function that Tripal provides for getting the ontology terms associated with every column table in Chado.  The following code shows these functions retrieving the ontology terms for our values from the organism table:
+
+.. code-block:: php
+
+    // Get the terms for each of the keys for the 'values' property.
+    $label_term = 'rdfs:label';
+    $genus_term = tripal_get_chado_semweb_term('organism', 'genus');
+    $species_term = tripal_get_chado_semweb_term('organism', 'species');
+    $infraspecific_name_term = tripal_get_chado_semweb_term('organism', 'infraspecific_name');
+    $infraspecific_type_term = tripal_get_chado_semweb_term('organism', 'type_id');
+
+
+Notice that for our organism fields we can easily get the ontology terms for them using the API function **tripal_get_chado_semweb_term**.  You will also notice a **label_term** variable.  Sometimes a user may want to see the full name of the organism and not pieces of it in various elements.  Therefore, we will provide a label in our list of values that will concatenate the full organism name.  This field is not in our organism table so we hard-code the term 'rdfs:lable' which is a term from the Resource Data Framework Schema vocabulary that defines a label.
+
+Next, let's initialize our field's value to be empty.  When setting a field value we must do so in the entity object that got passed into our load function.  The entity is an object and it stores values using the names of the fields.  The following code sets an empty record for our field:
+
+.. code-block:: php
+
+    // Set some defaults for the empty record.
+    $entity->{$field_name}['und'][0] = array(
+      'value' => array(),
+    );
+
+
+Notice that our field has some sub elements. The first is 'und'.  This element corresponds to the "language" of the text.  Drupal supports mulitple spoken languages and wants to know the language of text we provide.  For Tripal fields we always use 'und' meaning 'undefined'.   The next element is the delta index number.  Field have a cardinality, or in other words they can have multiple values.  For every value we add we increment that index, always starting at zero.  The last element is our 'value' element and it is here where we put our element. You may notice that our **delta** index is hard coded to 0.  This is because an entity can only always have one organism that it is associated with.  We will never have more than one.
+
+Now that we've got some preliminary values and we've initialized our value array we can start adding values!  Before we do though, let's double check that we have a record.  If we don't have a record for this entity, we can't get a value.
+
+.. code-block:: php
+
+    if ($record) {
+
+
+Now if we do have a record we need to get the value  The first step is to actually get our organism record.  For this we will find the record variable to be really handy. It already comes pre-populated with every Chado record that has a foreign-key relationship with our base record.  So, in the case of a gene, the record is stored in the feature table which has an organism_id column which is a foreign key to the organism table.  So, we know then that our record object has an organism_id property and we can get our organism from that. The only exception is the biomaterial table which uses a field named taxon_id:
+
+.. code-block:: php
+
+      if ($field_table == 'biomaterial') {
+        $organism = $record->taxon_id;
+      }
+      else {
+        $organism = $record->organism_id;
+      }
+
+We can easily get all of the values we need from this organism object.   We can now access the values for this organism using the Chado organism table column names (e.g. $organism->genus, $organism->species).
+
+.. code-block:: php 
+
+      $label = tripal_replace_chado_tokens($string, $organism);
+      $entity->{$field_name}['und'][0]['value'] = array(
+        $label_term => $label,
+        $genus_term => $organism->genus,
+        $species_term => $organism->species,
+      );
+      // The infraspecific fields were introduced in Chado v1.3.
+      if (property_exists($organism, 'infraspecific_name')) {
+        $entity->{$field_name}['und'][0]['value'][$infraspecific_type_term] = NULL;
+        $entity->{$field_name}['und'][0]['value'][$infraspecific_name_term] = $organism->infraspecific_name;
+        if ($organism->type_id) {
+          $entity->{$field_name}['und'][0]['value'][$infraspecific_type_term] =  $organism->type_id->name;
+        }
+      }
+
+In the code above we are populating our value array and we're using the controlled vocabulary terms we retrieved earlier as the keys.
+
+Okay, so, we have our values set. However, remember,  our fields must support two types of values: 1) those for end users; and 2) those that allow us to save values in Chado if the field is edited.  If you look at our value array above you will recognize that the entity to which this field is loading data for is for a feature or stock or library, etc.  This field represents the organism for a record from one of those tables.  If someone wants to edit the entity and change the organism  then effectively we need to change the organism_id of that table.  But in our values array we don't have the organism_id we only have data about the organism.  How will Tripal know how to change the organism for an entity if edited?  To do help Tripal out, we have to create special key/value pair to add to our values.  These are values that are not meant to be seen by the end-user.  The organism_id is a good example of such a value.  To create these values we create a key with a special naming scheme: use "chado-" as a prefix, followed by the table name (e.g. feature), followed by two underscores and finally the column name (e.g. organism_id).   The following code shows the creation of this value name:
+
+.. code-block:: php
+
+    // Set the linker field appropriately.
+    if ($field_table == 'biomaterial') {
+      $linker_field = 'chado-biomaterial__taxon_id';
+    }
+    else {
+      $linker_field = 'chado-' . $field_table . '__organism_id';
+    }
+
+If our entity were of type "gene" then our **field_table** is feature.  Therefore, our **linker_field** variable would be **chado-feature__organism_id**.  Next, we need to add this to our value:
+
+.. code-block:: php
+
+
+      $entity->{$field_name}['und'][0][$linker_field] = $organism->organism_id;
+
+Notice, though, that we did not add this value inside the 'value' key like we did above for our end-user, such as the following:
+
+.. code-block:: php
+
+  
+
+  $entity->{$field_name}['und'][0]['value'])
+
+Instead, we put it in at the same level as 'value':
+
+.. code-block:: php
+
+  
+
+  $entity->{$field_name}['und'][0][$linker_field]
+
+We do this because anything in the 'value' element is intended for the end-user.  Anything outside of the 'value' is meant for Tripal.  Adding the organism ID to this field as a Tripal "hidden" value allows Tripal to recognize where these values really came from.   When writing your own fields, you must include any values as "hidden" Tripal values that need to be written to the database table.  A good way to remember if you a value should be visible to the end-user or hidden for Tripal is to ask yourself these questions:
+
+  1.  Does the user need this value?  If yes, put it in the 'value' element.
+  2.  Does Tripal need the value when writing back to the Chado table?  If yes, put it as a hidden element.
+  3.  Does the user need to see the value an will this same value need to be written to the table?  If yes, then you have to put the value in both places.
+
+For our **obi__organism** field it is for entities with records in the **feature, stock, library**, etc. tables. Those tables only have an **organism_id** to represent the organism.  So, that's the database column this field is supporting.  We therefore, need to put that field as a hidden field, and all the others are just helpful to the user and don't get saved in the feature, stock or library tables. So, those go in the values array.
+
+Now, we're at a good stopping point with our field! We can close out our if($record) statement and the function:
+
+.. code-block:: php
+
+
+      }
+   }
+
+elementInfo() function
+~~~~~~~~~~~~~~~~~~~~~~
+The elementInfo() function is necessary to integrate your new field with Drupal Views and Tripal Web Services.  Drupal needs to know what data elements your field provides and Tripal needs to know what vocabulary terms to use for each of the data elements.  Related to vocabulary terms, all fields are assigned an ontology term for the field itself.  Every field has to have an one.   But when a field provides more than just a single data value it must also provide vocabulary terms for any sub elements as well.  Our obi__organism field provides the genus, species, etc. sub elements and, therefore, we need to describe these to Drupal and Tripal.  The elementInfo() function from the obi_organism field is as follows:
+
+.. code-block:: php
+
+  
+
+  /**
+   * @see TripalField::elementInfo()
+   */
+  public function elementInfo() {
+    $field_term = $this->getFieldTermID();
+
+    $genus_term = chado_get_semweb_term('organism', 'genus');
+    $species_term = chado_get_semweb_term('organism', 'species');
+    $infraspecific_name_term = chado_get_semweb_term('organism', 'infraspecific_name');
+    $infraspecific_type_term = chado_get_semweb_term('organism', 'type_id');
+
+    return array(
+      $field_term => array(
+        'operations' => array('eq', 'contains', 'starts'),
+        'sortable' => TRUE,
+        'searchable' => TRUE,
+        'readonly' => FALSE,
+        'type' => 'xs:complexType',
+        'elements' => array(
+          'rdfs:label' => array(
+            'searchable' => TRUE,
+            'name' => 'scientific_name',
+            'operations' => array('eq', 'ne', 'contains', 'starts'),
+            'sortable' => FALSE,
+            'type' => 'xs:string',
+            'readonly' => TRUE,
+            'required' => FALSE,
+          ),
+          $genus_term => array(
+            'searchable' => TRUE,
+            'name' => 'genus',
+            'operations' => array('eq', 'ne', 'contains', 'starts'),
+            'sortable' => TRUE,
+            'readonly' => FALSE,
+            'type' => 'xs:string',
+            'required' => TRUE,
+          ),
+          $species_term => array(
+            'searchable' => TRUE,
+            'name' => 'species',
+            'operations' => array('eq', 'ne', 'contains', 'starts'),
+            'sortable' => TRUE,
+            'readonly' => FALSE,
+            'type' => 'xs:string',
+            'required' => TRUE,
+          ),
+          $infraspecific_name_term => array(
+            'searchable' => TRUE,
+            'name' => 'infraspecies',
+            'operations' => array('eq', 'ne', 'contains', 'starts'),
+            'sortable' => TRUE,
+            'readonly' => FALSE,
+            'type' => 'xs:string',
+            'required' => FALSE,
+          ),
+          $infraspecific_type_term => array(
+            'searchable' => TRUE,
+            'name' => 'infraspecific_type',
+            'operations' => array('eq', 'ne', 'contains', 'starts'),
+            'sortable' => TRUE,
+            'readonly' => FALSE,
+            'type' => 'xs:integer',
+            'required' => FALSE,
+          ),
+          'entity' => array(
+            'searchable' => FALSE,
+          ),
+        ),
+      ),
+    );
+  }
+
+
+The code above generates and returns an associative array that provides metadata about the field and its elements.  The array is structured such that the first-level key is the term for the field.  Details about the field are at the second-level and all sub elements are contained in a 'elements' key.  In the following code the terms for the field and sub elements are retrieved using TripalField class functions and Tripal API calls:
+
+.. code-block:: php
+
+  
+
+    $field_term = $this->getFieldTermID();
+
+    $genus_term = chado_get_semweb_term('organism', 'genus');
+    $species_term = chado_get_semweb_term('organism', 'species');
+    $infraspecific_name_term = chado_get_semweb_term('organism', 'infraspecific_name');
+    $infraspecific_type_term = chado_get_semweb_term('organism', 'type_id');
+
+    return array( $field_term => array(
+      'operations' => array('eq', 'contains', 'starts'),
+      'sortable' => TRUE,
+      'searchable' => TRUE,
+      'readonly' => FALSE,
+      'type' => 'xs:complexType',
+      'elements' => array(
+
+Notice the value for $field_term variable was easily obtained by calling the $this->getFieldTermID function and all of the terms for the elements were obtained using the chado_get_semweb_term function which maps table columns in the Chado database schema to ontology terms.  The operations key indicates which search filter operations are supported for the field as a whole.  For this example these include 'eq' (for equals), 'contains' and 'starts' (for starts with).   The field is sortable and searchable so those values are set to TRUE.   Later, we weill learn how to implement the sorting, searching and filtering that the field will support.  For now we know we want them so we set the values accordingly.  Additionally, the field allows updating so 'readonly' is set to FALSE.   By convention, the 'type' of a field follows the XML data types for simple types (https://www.w3schools.com/xml/schema_simple.asp) and Complex types (https://www.w3schools.com/xml/schema_complex.asp) that have multiple elements.  Because our obi__organism field has subelements  and is not a single value, the field type is 'xs:complexType'.
+
+The array keys just mentioned fully describe our field to Drupal and Tripal.  Next we will define the sub elements in the same way, and these go in the 'elements' key.  First, we will describe the label.  Our obi__oranganism field provides a handly label element that concatenates the genus, species and infraspecific name into one simple string.  Therefore, we need to describe this element in the same way we described the field itself.  In the code below that the key is set to 'rdfs:label' (which is the controlled vocabulary term for a label) and that the child keys are the same as for the field.
+
+.. code-block:: php
+
+  
+
+
+        'elements' => array(
+          'rdfs:label' => array(
+            'searchable' => TRUE,
+            'name' => 'scientific_name',
+            'operations' => array('eq', 'ne', 'contains', 'starts'),
+            'sortable' => FALSE,
+            'type' => 'xs:string',
+            'readonly' => TRUE,
+            'required' => FALSE,
+          ),
+
+Notice that our field will allow searching, provides a variety of search filter options, is sortable and defines the type as 'xs:string'.  The remaing elements follow the same pattern.  As another example, here is the genus element:
+
+.. code-block:: php
+
+  
+
+         $genus_term => array(
+            'searchable' => TRUE,
+            'name' => 'genus',
+            'operations' => array('eq', 'ne', 'contains', 'starts'),
+            'sortable' => TRUE,
+            'readonly' => FALSE,
+            'type' => 'xs:string',
+            'required' => TRUE,
+          ),
+
+The major difference in the code above is that the term is provided by the variable $genus_term.
+
+Finally, our obi__organism field provides an 'entity' element that provides information for a published organism entity.  We do not provide any filtering, searching or sorting of those values.  So the final element appears as:
+
+.. code-block:: php
+
+          'entity' => array(
+            'searchable' => FALSE,
+          ),
+
+In summary,  you will always want to describe your field and every element of your field in the array returned by the elementInfo function.  However, you do not need to provide sorting, filtering or querying for every element.  If your field is read-only and simply provides values you should still describe these elements but you would set the meta data keys appropriately for the behavior of your field.   Also, you only need to describe elements in the values array returned by your load function.  Remember, there may be other key/value pairs (such as those used to help coordinate inserts/updates into Chado) but those do not need to be described here because they are never seen by the end-user.
+
+query() function
+~~~~~~~~~~~~~~~~~~
+
+
+As described above in the elementInfo function section, some fields and elements of fields are searchable.  if the elementInfo array indicates that the field is searchable and has operations (i.e. filters) then we must provide a way for those queries to occur.  This is where the query() function is needed.  The following is example code from the query function of our obi__organism field:
+
+.. code-block:: php
+
+  
+
+  public function query($query, $condition) {
+      $alias = $this->field['field_name'];
+      $operator = $condition['operator'];
+
+      $field_term_id = $this->getFieldTermID();
+      $genus_term = $field_term_id . ',' . chado_get_semweb_term('organism', 'genus');
+      $species_term = $field_term_id . ',' . chado_get_semweb_term('organism', 'species');
+      $infraspecific_name_term = $field_term_id . ',' . chado_get_semweb_term('organism', 'infraspecific_name');
+      $infraspecific_type_term = $field_term_id . ',' . chado_get_semweb_term('organism', 'type_id');
+
+      // Join to the organism table for this field.
+      $this->queryJoinOnce($query, 'organism', $alias, "base.organism_id = $alias.organism_id");
+
+      // If the column is the field name then we're during a search on the full
+      // scientific name.
+      if ($condition['column'] == $field_term_id or
+          $condition['column'] == $field_term_id . ',rdfs:label') {
+        if (chado_get_version() <= 1.3) {
+          $query->where("CONCAT($alias.genus, ' ', $alias.species) $operator :full_name",  array(':full_name' => $condition['value']));
+        }
+        else {
+          $this->queryJoinOnce($query, 'cvterm', $alias . '_cvterm', 'base.infraspecific_type = ' . $alias . '_cvterm.type_id', 'LEFT OUTER');
+          $query->where("CONCAT($alias.genus, ' ', $alias.species, ' ', " . $alias . "'_cvterm.name', ' ', $alias.infraspecific_name) $operator :full_name",  array(':full_name' => $condition['value']));
+        }
+      }
+
+      // If the column is a subfield.
+      if ($condition['column'] == $species_term) {
+        $query->condition("$alias.species", $condition['value'], $operator);
+      }
+      if ($condition['column'] == $genus_term) {
+        $query->condition("$alias.genus", $condition['value'], $operator);
+      }
+
+      if ($condition['column'] == $infraspecific_name_term) {
+        $query->condition("$alias.infraspecific_name", $condition['value'], $operator);
+      }
+
+      if ($condition['column'] == $infraspecific_type_term) {
+        $this->queryJoinOnce($query, 'cvterm', 'CVT', "base.type_id = CVT.cvterm_id");
+        $query->condition("CVT.name", $condition['value'], $operator);
+      }
+    }
+
+The code above is how the field tells Drupal and Tripal how to find and filter the records that this field corresponds to.  First, we retreive the field alias and operators:and as with the load and elementInfo functions we get the controlled vocabulary terms for our field and field elements:
+
+
+.. code-block:: php
+
+  
+
+    $alias = $this->field['field_name'];
+    $operator = $condition['operator'];
+
+    $field_term_id = $this->getFieldTermID();
+    $genus_term = $field_term_id . ',' . chado_get_semweb_term('organism', 'genus');
+    $species_term = $field_term_id . ',' . chado_get_semweb_term('organism', 'species');
+    $infraspecific_name_term = $field_term_id . ',' . chado_get_semweb_term('organism', 'infraspecific_name');
+    $infraspecific_type_term = $field_term_id . ',' . chado_get_semweb_term('organism', 'type_id');
+
+Next, our knowledge of Chado is needed.  We know that our obi__organism field will load data from the organism table.  Therefore, our search must occur there.

BIN
docs/dev_guide/custom_field/select_vocab_terms.1.organism.png


BIN
docs/dev_guide/custom_field/select_vocab_terms.2.cds.png


BIN
docs/dev_guide/custom_field/select_vocab_terms.3.go.png


BIN
docs/dev_guide/custom_field/select_vocab_terms.4.edam.png


+ 157 - 0
docs/dev_guide/custom_field/select_vocab_terms.rst

@@ -0,0 +1,157 @@
+Selecting Vocabulary Terms
+==========================
+
+Ontologies: what and why?
+-------------------------
+
+Tripal 3 requires all bundles and fields to be associated with a Controlled Vocabulary (CV). CVs are dictionaries of defined terms (CV terms) that make data machine-accessible, ensuring uniform terms are used across experiments, organisms and websites. Without CVterms, our scientific knowledge might be split by "dialects". Plant biologists might study temperature stress, while animal biologists study heat shock. Each group might benefit from the knowledge of the other, but they use a different vocabulary to describe the same thing, creating challenges for data discovery and exchange. CV terms make this easier not just for people, but especially for machines. Ontologies take this a step further. Where CVs are controlled lists of CVterms, ontologies are a controlled language, that include heirarchical relationships of terms.
+
+Tripal leverages vocabularies to make use of the `Semantic Web <https://en.wikipedia.org/wiki/Semantic_Web>`_. Every bundle and field defined in Tripal will be associated with a CVterm. Therefore, it is important to find community developed terms.  The `EMBL EBI Ontology Lookup Service <http://www.ebi.ac.uk/ols/index>`_ provides an easy location to search for and identify terms.  When choosing terms for new Bundles and Fields, think carefully about the terms you will use to describe your objects. Selecting the proper CV term that best describes the data may be the most challenging part of creating custom Bundles and Fields!
+
+Before you can create a new Bundle or Field  the vocabulary term must be present in your local Tripal site.  You can check if a term exists by using the Tripal lookup service on your local site using the URL path cv/lookup (e.g. http://your-site/cv/lookup).  If the term is not present then you'll need to add it.  You can do so manually by using Tripal's controlled vocabulary admin pages.  For creating new bundles this is all you need to do.  However, when creating Fields you will want to programmatically add the term.  This is important because Fields are meant to be shared. If you create an awesome field that you want to share with others then you need to make sure the terms get added programmatically.   The following sections describe how terms are stored in Chado and how you can add them using Tripal API calls.
+
+Storage of Terms in Chado
+-------------------------
+
+In Chado, CVs are stored by two tables: the **db** and **cv** tables. Chado was designed to store a record for the online database that a vocabulary lives at in the **db** table, and the namespace of a vocabulary in the **cv** table.  For example, the sequence ontology uses the namespace, sequence, which is stored in the **cv** table but uses the short name of SO which is stored in the **db** table.  As we'll see later, sometimes the distinction between what gets stored in the **cv** vs the **db** tables can get a bit fuzzy with some vocabularies. The terms themselves are stored in the cvterm table. The cvterm table has a foreign key to the **cv** table via the **cv_id** field.  Every controlled vocabulary term has an accession. For example the term gene in the Sequence Ontology has an accession number of SO:0000704.  Accession numbers consist of two parts: a vocabulary "short name", followed by a unique identifier separated by a colon.  Within Chado, the accession for any term is stored in the dbxref table.  This table has a foreign key to the **db** table via the **db_id** as well as a foreign key to the cvterm table via the **dbxref_id** field.
+
+Vocabulary Short Names and Namespaces
+-------------------------------------
+
+How can you tell what the **short name** and **namespace** values will be for a vocabulary term that you want to insert into Chado for your custom Bundle or Field? Hint: use the information is in the EMBL-EBI `Ontology Lookup Service <http://www.ebi.ac.uk/ols/index>`_ (OLS).  The following sections provide three examples for different cases.
+
+Case 1:  Ontologies without a defined namespace
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Consider the term for `organism <http://www.ebi.ac.uk/ols/ontologies/obi/terms?iri=http%3A%2F%2Fpurl.obolibrary.org%2Fobo%2FOBI_0100026>`_.
+
+.. figure:: select_vocab_terms.1.organism.png
+
+Notice how the teal box (the **short name**) is OBI, and the orange box contains the **full accession**, OBI:0100026 which includes but the **short name** and the unique term **accession** value.  Unfortunately, the OLS does not indicate the **namespace** terms.   So, as a rule we will use the short name converted to lower case.  Before using this term in a Tripal Bundle or Field you may need to insert this term into Chado.  You can do so in your custom module cude using the **tripal_insert_cvterm** function. The following provides a demonstration:
+
+.. code-block:: php
+
+	
+
+   $term= tripal_insert_cvterm([
+            'id' => 'OBI:0100026',
+            'name' => 'organism',
+            'cv_name' => 'OBI',
+            'definition' => 'A material entity that is an individual living system, such as animal,
+            plant, bacteria or virus, that is capable of replicating or reproducing, growth and maintenance
+                  in the right environment. An organism may be unicellular or made up, like humans, of many
+               billions of cells divided into specialized tissues and organs.',
+        ]);
+
+
+
+Note that in the code above the namespace is provided as the **cv_name** element and the full accessions (including the short name) is provided as the **id** element.  In this case the OBI CV already exists by default in the Tripal database, so we did not need to add the vocabulary record.  If the OBI did not exist we could have added it using the following API calls.  First we insert the "database" record for the ontology.
+
+.. code-block:: php
+
+  
+  tripal_insert_db(array(
+    'name' => 'obi',
+    'description' => 'The Ontology for Biomedical Investigation.',
+    'url' => 'http://obi-ontology.org/page/Main_Page',
+    'urlprefix' => 'http://purl.obolibrary.org/obo/{db}_{accession}',
+  ));
+
+
+
+Notice here that the **name** element is the **namespace** (short name converted to lower case) for the vocabulary.  The url is the web address for the ontology online. The urlprefix is a URL that can be used to construct a link that when clicked will take the user to any term in the vocabulary.  Almost all vocabularies will have a common URL for all terms.  Tripal will automatically substitute the short name into the **{db}** token and the term **accession** in to the **{accession}** token to generate the URL.
+
+Second, we insert the record for the controlled vocabulary.
+​
+
+.. code-block:: php
+
+  
+ 	tripal_insert_cv(
+    	'OBI',
+    	'Ontology for Biomedical Investigation. The Ontology for Biomedical Investigations (OBI) is build in a collaborative, international effort and will serve as a resource for annotating biomedical investigations, including the study design, protocols and instrumentation used, the data generated and the types of analysis performed on the data. This ontology arose from the Functional Genomics Investigation Ontology (FuGO) and will contain both terms that are common to all biomedical investigations, including functional genomics investigations and those that are more domain specific.'
+  );
+
+
+Case 2:  Ontologies with a defined namespace
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Consider the entry for `CDS <https://www.ebi.ac.uk/ols/ontologies/so/terms?iri=http%3A%2F%2Fpurl.obolibrary.org%2Fobo%2FSO_0000316>`_.
+
+.. figure:: select_vocab_terms.2.cds.png
+
+
+Notice that in the Term Info box on the right there is the term **has_obo_namespace** which is defined as the word: sequence.  This is much better than the organism example from the OBI.  We now know the correct namespace for the term! By default, Tripal loads the Sequence Ontology during install.  However, suppose we did not have this term loaded we could do so with the following:
+
+
+.. code-block:: php
+
+  
+    $term= tripal_insert_cvterm([
+            'id' => 'SO:0000316',
+            'name' => 'CDS',
+            'cv_name' => 'sequence',
+            'definition' => 'A contiguous sequence which begins with, and includes, a start codon and ends with, and includes, a stop codon. [ http://www.sequenceontology.org/browser/current_svn/term/SO:ma ].',
+        ]);
+
+Notice in the code above we can properly set the cv_name to sequence.
+
+Case 3: Ontologies with multiple namespaces
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Some ontologies are b into sub-ontologies. This includes the Gene Ontology (GO).  Let's consider the example GO term `cell aggregation <http://www.ebi.ac.uk/ols/ontologies/go/terms?iri=http%3A%2F%2Fpurl.obolibrary.org%2Fobo%2FGO_0098743>`_. Looking at the EBI entry, the teal box is GO, the orange box is GO:0098743, and the has_obo_namespace is biological_process. However, the GO provides two other namespaces:  cellular_component and molecular_function.  Be sure to pay attention to these different namespaces if you ever need to manually insert a term.
+
+.. figure:: select_vocab_terms.3.go.png
+
+
+Case 4: Ontologies with muliptle short names
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The EDAM ontology builds its term accessions using different short names instead of the ontology. Consider the EDAM term for `Sequence <http://www.ebi.ac.uk/ols/ontologies/edam/terms?iri=http%3A%2F%2Fedamontology.org%2Fdata_2044>`_. The teal box is EDAM, the orange box is data:2044, and there is no **namespace**.
+
+.. figure:: select_vocab_terms.4.edam.png
+
+
+For this case, the **namespace** is EDAM, the short name is **data**, and the accession is 2044.  Unfortunately, this breaks the paradigm that Chado expects. Typically the **short name** is the teal box (EDAM).  In order to force Chado to properly handle ontologies like this we are forced to reverse the short name and **namespace** values when creating our record:
+
+
+.. code-block:: php
+
+  
+	$term= tripal_insert_cvterm([
+	  'id' => 'data:2044',
+	  'name' => 'sequence',
+	  'cv_name' => 'EDAM',
+	  'definition' => 'One or more molecular sequences, possibly with associated annotation.',
+	]);
+
+	tripal_insert_db(array(
+	    'name' => 'data',
+	    'description' => 'Bioinformatics operations, data types, formats, identifiers and topics.',
+	    'url' => 'http://edamontology.org/page',
+	    'urlprefix' => 'http://edamontology.org/{db}_{accession}',
+	));
+
+	tripal_insert_cv(
+	    'EDAM',
+	    'EDAM is an ontology of well established, familiar concepts that are prevalent within bioinformatics, including types of data and data identifiers, data formats, operations and topics. EDAM is a simple ontology - essentially a set of terms with synonyms and definitions - organised into an intuitive hierarchy for convenient use by curators, software developers and end-users. EDAM is suitable for large-scale semantic annotations and categorization of diverse bioinformatics resources. EDAM is also suitable for diverse application including for example within workbenches and workflow-management systems, software distributions, and resource registries.'
+	);
+
+
+
+Case 5: You really cant find a term!
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Sometimes a good CVterm just doesn't exist for what you want to describe. If you can't find a CV term, you can insert a term into the "local" CV. This is meant to be used as a last resort. In these cases, before you use a local term, consider contributing the term to an existing CV or ontology. Any terms that are invented for a local site may mean that the data exposed by your site cannot be discovered by other sites or tools.  In this case, the accession will not be numeric,  but is the same as the term name.
+
+.. code-block:: php
+
+	
+	$term= tripal_insert_cvterm([
+	   'id' => 'local:shame_on_you',
+	   'name' => 'shame_on_you',
+	   'cv_name' => 'local',
+	   'definition' => 'You should really find a good CVterm.',
+	]);
+
+Notice in the above code the **short name** and **namespace** are both "local" as this is a local term on the site.

BIN
docs/dev_guide/custom_field/tripal_field_generator.TFG.png


+ 9 - 0
docs/dev_guide/custom_field/tripal_field_generator.rst

@@ -0,0 +1,9 @@
+Easier Field Creation: Tripal Field Generator
+==============================================
+
+
+The Staton Lab has created a `specialized tool <https://github.com/statonlab/fields_generator>`_ for automatically generating a ChadoField or TripalField and populating them with the basic controlled vocabulary and database information.  Instructions are available at https://github.com/statonlab/fields_generator.
+
+.. figure:: tripal_field_generator.TFG.png
+   :align: center
+   :scale: 50%

+ 11 - 0
docs/dev_guide/custom_modules.rst

@@ -0,0 +1,11 @@
+Creating Custom Modules
+========================
+
+.. note::
+
+	This section is under construction
+
+
+The Tripal API, in conjunction with the Drupal API allow developers to create their own modules that can "plug-in" with Tripal. Developers may create their own modules if they desire new or different functionality. A custom module can also be desired to house a sites "customizations." The Tripal API provides an interface for module developers to interact with the Chado database as well as the other Tripal core functions such as jobs management, materialized views, in-house vocabularies, external database cross-references, properties, etc. An understanding of the Drupal API and Drupal module development is required.
+
+Basic instructions for creating custom Drupal module can be found here:  https://www.drupal.org/docs/7/creating-custom-modules.   Before you can create custom modules that interact with Tripal, you should review those instructions and have practiced creating at least a simple module.   Tripal follows Drupal module development rules and API.  

BIN
docs/dev_guide/custom_web_services.1_hydra_console.png


BIN
docs/dev_guide/custom_web_services.2.png


BIN
docs/dev_guide/custom_web_services.3.png


BIN
docs/dev_guide/custom_web_services.4.png


+ 347 - 0
docs/dev_guide/custom_web_services.rst

@@ -0,0 +1,347 @@
+Creating Custom Web Services
+==============================
+
+
+Introduction
+-------------
+
+New in Tripal v3 are `RESTful web services <https://en.wikipedia.org/wiki/Representational_state_transfer>`_.  These web-services are designed to allow end-users to access data programmatically using any programming language of their choice.  Web services in Tripal v3 are provided by the **tripal_ws** module.  By default the module provides the "content" service.  This service provides access via a RESTful interface to all of the content published on a Tripal site.  It is meant to respond immediately as the site admin makes changes to published data and is expected to always provide access to the same data displayed on the site (nothing more and nothing less). 
+
+
+Tripal v3 has been redesigned from Tripal v2 to be fully organized around controlled vocabularies.  All data made available via Tripal is expected to be described by the terms of a controlled vocabulary or ontology.  For example, all content types in Tripal are assigned a controlled vocabulary term that describes what the content is.  Additionally, each field of data attached to a content type also is described using a controlled vocabulary term.  If a field provides more than just a single data value (i.e. it provides a list or nested structured array of data of key/value pairs) then each of the keys for those pairs must also be a controlled vocabulary term.  This requirement allows Tripal to fully describe the data it houses to any other Tripal site and any other script or service that can read the web services.  As long as the client application understands the vocabulary term it will understand the meaning of the data.  Using controlled vocabulary terms to describe all data allows a Tripal site to participate in the `Semantic Web <https://en.wikipedia.org/wiki/Semantic_Web>`_. 
+
+Finally, the Tripal RESTful services are meant to be discoverable.  In some cases, when a web services is designed, the only way to understand the structure of it and the operations that it provides are for a programmer to read online documentation for the service before she can write the client application.  However, to better support automatic data discovery without human intervention by a client the Tripal web services have been designed to be discoverable.  To this end, Tripal uses the  `Hyrda Core Vocabulary <https://www.hydra-cg.com/spec/latest/core/>`_ specification.  It fully describes all of the services, their operations, and the resulting value.  A client application that understands the Hydra language can therefore learn  to use the web service without human intervention.  However, in practice, its a good idea to continue to provide online documentation for humans.  And this User's Guide :doc:`provides those instructions </user_guide/web_services>` for the default Tripal content service.  
+
+This documentation provides instructions to construct your own custom web services and making that service available through Tripal's existing web service infrastructure.  This will enable your web service to be discoverable and to provide a consistent experience to end-users.  Before proceeding with the following instructions, please review the **Structure of a Web Service Response** section on the User's Guide :doc:`Web Services page </user_guide/web_services>`.
+
+Getting Started
+----------------
+Before creation of your new web services you must first consider the following in your design:
+
+1.  What functionality will your service provide.  This will dictate the URL paths that you will need and what operations (Create, Read, Update, Delete) your service will support.
+2.  What controlled vocabularies do you need to use to describe the data that your service may provide.
+3.  What human-readable label can you give to your service.
+4.  What one-word describes the type of service you are creating.
+5.  What human-readable description should you provide to users of your service.
+
+When you are ready to begin construction of your web service, you must first create a custom Drupal module.  The assumption is that you already know how to create new Drupal modules, or you have access to an existing one into which you can add your new web services.   Prepare your custom module by creating the following directory structure:
+
+  .. code-block:: bash
+
+	[module_name]/includes/TripalWebService
+
+Where [module_name] is the name of your custom module.  It is inside of this directory that you will place the code for your new web services.  Tripal is designed to recognize this directory, discover the web services described inside of it and to automatically make them available!  You need only program the service and Tripal does the rest.  
+
+
+**Note:** When selecting a version number it is best practice to start with 0.1.  The first number represents the  "major" number and the second number is the "minor" number.  When minor bug fixes or changes to the service occur the minor number should be incremented. When major changes occur that affect the structure of the web service or it's operations then the major number should be incremented.    The service can be named whatever you like.   This class file should be named according to this schema with a **.inc** extension. 
+
+For this tutorial, suppose we wanted to create a web service that allowed someone to interact with the Tripal Job queue.  We could name our new class the **TripalJobService_v0.1** class and we would create this class in the file: 
+
+.. code-block:: bash
+
+	[module_name]/includes/TripalWebService/TripalJobService_v0_1.inc
+
+Within this file we will implement our class with the following structure:
+
+
+.. code-block:: php
+
+	
+	class TripalJobService_v0_1 extends TripalWebService {
+
+	  /**
+	   * The human-readable label for this web service.
+	   */
+	  public static $label = 'Jobs';
+
+	  /**
+	   * A bit of text to describe what this service provides.
+	   */
+	  public static $description = 'Provides interaction with the Tripal Job Queue';
+
+	  /**
+	   * A machine-readable type for this service. This name must be unique
+	   * among all Tripal web services and is used to form the URL to access
+	   * this service.
+	   */
+	  public static $type = 'jobs';
+
+	  /**
+	   * Implements the constructor
+	   */
+	  public function __construct($base_path) {
+	    parent::__construct($base_path);
+	  }
+	}
+
+
+This is all we need for Tripal to recognize our new service!  Notice that the class implementation extends the TripalWebSerivce class and it sets a few static variables that defines the label, description and name for this service.  Finally, this class defines the constructor which simply calls the parent class constructor. Be sure to always call the parent constructor when you implement your own service.  We can now use the `Hydra console <http://www.markus-lanthaler.com/hydra/console/>`_ to see our service.  Note, that the hydra console must be able to have access to your site. For this tutorial, the Tripal site is temporarily hosted on the local machine, and hence Hydra has been installed locally.    To see if your new service shows up, enter the URL for your site into the Hydra console and you should see it appear:
+
+.. image:: custom_web_services.1_hydra_console.png
+
+Notice in the above screen shot that our **jobs** service is now present in the **Response** section, and in the **Documentation** section!   The **Response** section shows the JSON array returned by Tripal and the **Documentation** section displays the information about our service.
+
+Documenting the Services
+-------------------------
+
+Our service appears in the Hydra console but if we try to use Hydra to perform a **GET** operation on our Jobs service there will be no interesting response and no documentation.  Try this by clicking on the link in the **Response** section for our Jobs service, and selecting the GET operation.
+
+.. image:: custom_web_services.2.png
+
+
+
+You will see that our service provides nothing:
+
+.. image:: custom_web_services.3.png
+
+
+Before we create our service we should have planned the design of our service. Suppose for now we just wanted to provide read-only access to job information.  Our design for the web service is quite simple and consists of the these resources (URLs and operations):
+
+
+TABLE!
+
+
+
+Before we begin implementation of our web service we must first document these resources.  To do this we must add a new function to our TripalJobService_v0_1 class named **getDocumentation**.  The following code shows this initial implementation of that function in our class:
+
+
+.. code-block:: php
+
+	 /**
+	     * @see TripalWebService::getDocumentation()
+	     */
+	    public function getDocumentation() {
+	        return parent::getDocumentation();
+	    }
+
+Notice currently all this function does is call the parent getDocumentation function.  Now, the first thing we need to document is our web service classes.  A web service class (not to be confused with the PHP class) simply refers to a resource.  A resource is any distinct URL within web services.  So, according to our design above we have two resources and hence two classes:  a jobs collection resource and a job resource.  Tripal must describe all of the classes (i.e. resources) using the Hydra method.  This makes the web service discoverable.   For example, with the content web service, Tripal provides a resource for each content type.  The Gene content type that Tripal provides is  described using the Hydra method in a JSON array with the following:
+
+
+.. code-block:: json
+
+	{
+	  "@id": "http://www.sequenceontology.org/browser/current_svn/term/SO:0000704",
+	  "@type": "hydra:Class",
+	  "hydra:title": "Gene",
+	  "hydra:description": "A region (or regions) that includes all of the sequence elements necessary to encode a functional transcript. A gene may include regulatory regions, transcribed regions and\/or other functional sequence regions. [SO:immuno_workshop]",
+	  "subClassOf": "hydra:Resource",
+	  "supportedOperation": [
+	    {
+	      "@id": "_:gene_retrieve",
+	      "@type": "hydra:Operation",
+	      "method": "GET",
+	      "label": "Retrieves the Gene resource.",
+	      "description": null,
+	      "statusCodes": [],
+	      "expects": null,
+	      "returns": "http://www.sequenceontology.org/browser/current_svn/term/SO:0000704"
+	     }
+	  ]
+	}
+
+In the above array, notice the @id is URL that represents a unique identifier for the class.   The @type will always be 'hydra:Class' because we are documenting a resource.  Then there is information about the class defined using the 'hydra:title' and 'hydra:description'.  The 'subclassOf' is always set to 'hydra:Resource'.  Next is the list of supported operations for this resource.  Remember, in our design we only want to support the GET operation for our Jobs service, so just like in the example above, the method we will support is GET.  The key/value pairs for the GET method are described using Hydra terms.  
+
+For our services we need to provide the information to Tripal so that it can generate these Hydra JSON arrays that document our service.  Tripal provides some easy API functions to help with this.  The firt is the **addDoc** member function.  This function will take as input the class details, operations and properties necessary to generate the documentation for our class.  First, lets use this function to document our Jobs Collection resource.  Below is sample code that will do this for us.
+
+
+.. code-block:: php
+
+	
+
+ 	public function getDocumentation() {
+        $term = tripal_get_term_details('local', 'computational_jobs');
+        $details = array(
+            'id' => $term['url'],
+            'title' => $term['name'],
+            'description' => $term['definition'],
+        );
+        $operations = array(
+            'GET' => array(
+                'label' => 'Computational Jobs',
+                'description' => 'Retrieves the list of computational jobs that have been submitted on this site.',
+                'returns' => $term['url'],
+                'type' => '_:computational_jobs_retrieve',
+            ),
+        );
+        $properties = array(
+        );
+        $this->addDocClass($details, $operations, $properties);
+        return parent::getDocumentation();
+    
+
+
+In the code above we add the documentation for our Job Collection class. There are three different arrays, one for the class details, one for the operations that the class supports and the third for properties. For now, the properties array is left empty. We'll come back to that later.  All classes must use a controlled vocabulary term.  Notice that the term used for this class is a term local to the database named 'computational_jobs'.   Normally when creating a class we would try to use a term from a published controlled vocabulary.  A large number of these vocabularies can be searched using `the EBI Ontology Lookup Service <https://www.ebi.ac.uk/ols/index>`_.  Unfortunately, an appropriate term could not be found in a published vocabulary, so we had to create a local term.  We can use Tripal's API functions to easily add new terms.  The following code should be placed in the install() function of your module to ensure the term is available:
+
+
+.. code-block:: php
+
+    $term = tripal_insert_cvterm(array(
+            'id' => 'local:computational_job',
+            'name' => 'Computational Job',
+            'cv_name' => 'local',
+            'definition' => 'A computational job that executes a specific task.',
+        ));
+        $term = tripal_insert_cvterm(array(
+            'id' => 'local:computational_jobs',
+            'name' => 'Computational Jobs',
+            'cv_name' => 'local',
+            'definition' => 'A set of computational jobs where each job executes a specific task.',
+        ));
+
+You'll notice in the code above that the @id of the Class is the URL of the term.  Using the **tripal_get_term_details** function we can get the URL for the term.  The URL serves as the unique identifier for this term.  We simply set the title and description for the class using the term details.  For the operation, we can specify any of the HTTP protocols (e.g. GET, PUT, PUSH, DELETE and PATCH).  Here we are currently only supporting read-only operations so we only need to provide a 'GET' operation.    Our jobs collection resource is now documented!
+
+
+Implementing a Collection Resource
+--------------------------------------
+
+Now that our job collection resource is documented we can implement the resource using the **handleRequest** function.  We currently only support two paths for our web services as indicated in our design table above. Those include the default path where our job collection resource is found and an additional path with the job ID appended where individual job resources are found.  First, we will implement the Job Collections resource:
+
+
+.. code-block:: php
+
+    /**
+     * @see TripalWebService::handleRequest()
+     */
+    public function handleRequest() {
+
+        // Get the content type.
+        $job_id = (count($this->path) > 0) ? $this->path[0] : '';
+
+        // If we have a content type then list all of the entities that belong
+        // to it.
+        if (!$job_id) {
+            $this->doJobsList();
+        }
+    }
+
+in the code above need to determine if the resource is a job collection or a job resource.  To do that we can check to see if a job_id was provided.  The TripalWebService class provides as a member element the full URL path broken into an array of elements.  Because our job_id would always be in the first element of the path (after our base path for the service) we can use **$this->path[0]** to look for a job_id.  If one is not provided then we can execute a function called **doJobsList** and the code for that is as follows:
+
+
+.. code-block:: php
+
+	
+
+    /**
+     * Generates the job collection resource.
+     */
+
+    private function doJobsList() {
+        // If the user has specified a limit or page number then use those to
+        // get the specific jobs.
+        $limit = isset($this->params['limit']) ? $this->params['limit'] : '25';
+        $page = isset($this->params['page']) ? $this->params['page'] : 0;
+
+        // Get the list of jobs for the given page, and the total number.
+        $offset = $page * $limit;
+        $jobs = tripal_get_jobs($offset, $limit);
+        $num_records = tripal_get_jobs_count();
+
+        // Set the current resource to be a new TripalWebServiceCollection resource,
+        // and pass in the current service path, and set the pager.
+        $service_path = $this->getServicePath();
+        $this->resource = new TripalWebServiceCollection($service_path, $this->params);
+        $this->resource->setType('local:computational_jobs');
+        $this->resource->initPager($num_records, $limit, $page);
+
+        // Now add the jobs as members
+        foreach ($jobs as $job) {
+            $member = new TripalWebServiceResource($service_path);
+            $member->setID($job->job_id);
+            $member->setType('local:computational_job');
+            $member->addProperty('schema:ItemPage', url('admin/tripal/tripal_jobs/view/' . $job->job_id, array('absolute' => TRUE)));
+            $this->resource->addMember($member);
+        }
+    }
+
+The first few lines of code above are as follows:  
+
+.. code-block:: php
+
+        $limit = isset($this->params['limit']) ? $this->params['limit'] : '25';
+        $page = isset($this->params['page']) ? $this->params['page'] : 0;
+
+
+
+Remember, that we wanted to allow for paging of our job collection.  We could have hundreds or thousands of jobs over time and we do not want to slow the page load by loading all of those jobs.  Therefore the page and limit parameters that can be added to the URL are available via the params member as a set of key/value pairs.  Next, using Tripal API function calls, we get the list of jobs that the user has requested to see (or the first page by default):
+
+.. code-block:: php
+
+      $offset = $page * $limit;
+        $jobs = tripal_get_jobs($offset, $limit);
+        $num_records = tripal_get_jobs_count();
+
+
+Now that we have our list of jobs to use in the collection we next need to build the resource.  We do this by setting the resource member of our TripalJobService_v0_1 class.  Tripal provides an easy way for constructing a collection via a class named TripalWebServiceCollection.  This class provides the necessary functions to easily create a collection resource that in the end will generate the appropriate JSON for us.  To create a collection we first instantiate a new instance of the TripalWebServiceCollection class and pass it the URL path that it corresponds to (in this case our base service path for the service).  We assign this new object to the resource member of our class.
+
+.. code-block:: php
+
+        $service_path = $this->getServicePath();
+        $this->resource = new TripalWebServiceCollection($service_path, $this->params);
+
+Next we need to indicate what type of collection this is.  Remember the controlled vocabulary terms we created previously?  We need to use those again to set the type.  Our term for a job collection is: local:computational_jobs.  So, we need to use this to set the type:
+
+.. code-block:: php
+
+        $this->resource->setType('local:computational_jobs');
+
+Now, because we have instantiated a TripalWebServiceCollection object it can handle creation of the pager for us. We just need to tell it how many total records there are, the page and number of records per page (i.e. limit):
+
+.. code-block:: php
+
+        $this->resource->initPager($num_records, $limit, $page);
+
+Lastly, we need to add our "members" of the collection.  These are the jobs from our query.  The following for loop iterates through alll of our jobs and creates new member objects that are added to our collection:
+
+.. code-block:: php
+
+	
+
+        foreach ($jobs as $job) {
+            $member = new TripalWebServiceResource($service_path);
+            $member->setID($job->job_id);
+            $member->setType('local:computational_job');
+            $member->addProperty('schema:name', $job->job_name);
+            $member->addProperty('schema:ItemPage', url('admin/tripal/tripal_jobs/view/' . $job->job_id, array('absolute' => TRUE)));
+            $this->resource->addMember($member);
+        }
+
+Notice in the code above that each job is an instance of the class called TripalWebServiceResource.  We use this class because each element of the collection is a reference to a resource and we reference the ID and the type.   In the code above we create the new member resource, we set it's type to be the vocabulary term 'local:computational_job' and eventually, use the addMember function of our TripalWebServiceCollection to add it to the collection. 
+
+Also in the code above is a new function named addProperty.   We want to add some additional information about the job to help the end-user understand what each job is and how to get to it.  Here we add two properties, one that is the job name and another that is the page URL for the job on our Tripal site.  With these properties the client can quickly see the title and can go see the job on the Tripal site by following the given URL.  Note, a resource always has two identifying pieces of information: the ID and the Type.  So, everything else is added as a property of the resource.   Also notice that the first argument when using the addProperty function is a controlled vocabulary term.  Here we've used the terms **schema:name** and **schema:ItemPage**.  These terms are from the Schema.org vocabulary and the define what these properties are: a name and an item's page.
+
+Now that we have what looks like enough code to handle the job collection resource, we can return to Hydra to test if our new resource is working.  The screenshot below shows the results:
+
+.. image:: custom_web_services.4.png
+
+
+It's clear that our resource is working!  However, there are some issues.  The URL for the ItemPage does not show as clickable, and we're missing descriptions of the properties in the Documentation column on the right. We'll fix that issue a bit later.  For now, this looks good.
+
+
+
+Simplifying the Property Keys
+-----------------------------
+
+.. note::
+
+	The rest of the guide is under construction
+
+
+Implementing a Resource
+------------------------
+ 
+Documenting Properties
+------------------------
+
+More on Parameters
+------------------------
+
+Implementing Other Operations
+------------------------------
+
+Controlling Access to Resources
+---------------------------------
+
+
+ 
+
+Debugging and Troubleshooting

+ 57 - 0
docs/dev_guide/data_structures.rst

@@ -0,0 +1,57 @@
+Tripal Data Structures
+=======================
+
+This page explains the relationships between Entity types, Bundles (content type), Entities and Fields. These are data structures provided by Drupal that Tripal v3 uses to expose biological content through your Drupal site, and to provide flexibility in site customization.  It is important to understand these terms and their relationships before developing new modules or custom functionlaity.   A quick summary of these Drupal data structures are as follows:
+
+* Entity:  a discrete data record.  Entities are most commonly are seen as "pages" on a Drupal web site.
+* Field:  an "atomic" piece of ancillary data that describes, defines or expands the entity.  Common fields include the name of an entity, a "body" of descriptive text, etc.
+* Bundle:  a content type.  An entity must always have a content type.  Drupal provides several content types by default:  Basic Page and Article.  Tripal provides biological bundles (e.g. genes, organisms, etc).
+* Entity Type:  despite the confusing name, an entity type is simply a group of bundles that are somehow related.  Drupal provides a "Node" Entity type that includes the Page and Article bundles.  All of the Tripal bundles (content types) belong to the TripalEntity Entity type.
+
+
+The following figure describes the heirarchical relationship between Drupal Entity types (e.g. Node) in comparison with TripalEntity entity types (e.g. Chromosome, Germplasm, etc.).
+
+
+.. figure:: /_images/dev_guide/data_structures/Terminology-Diagram.png
+   :scale: 100 %
+   :alt: Entity terminology guide
+
+
+Furthermore, fields are "attached" to a Bundle and hold unique values for each Entity. The following figure describes this relationship for a Gene Bundle that has several fields attached: name, description and organism.  Note that in this figure the Entity and each of the Fields are defined using a controlled vocabulary term.  As a result, bundles and fields can be described using the `Semantic Web <https://en.wikipedia.org/wiki/Semantic_Web>`_ concepts of a "subject", "predicate" and "object".  We will discuss more on controlled vocabularies a bit later in the Handbook.
+
+
+
+.. figure:: /_images/dev_guide/data_structures/Tripal-Semantic-web.png
+   :scale: 50 %
+   :alt: Semantic web
+
+
+
+Bundles (Content Types)
+-----------------------
+
+Bundles are types of content in a Drupal site.  By default, Drupal provides the Basic Page and Article content types, and Drupal allows a site developer to create new content types on-the-fly using the administrative interface--no programming required.  Tripal also provides several Content Type by default. During installation of Tripal the Organism, Gene, Project, Analysis and other content types are created automatically.  The site developer can then create new content types for different biological data--again, without any programming required.
+
+In order to to assist with data exchange and use of common data formats, Tripal Bundles are defined using a controlled vocabulary term (cvterm). For example, a "Gene" Bundle is defined using the Sequence Ontology term for gene whose term accession is: SO:0000704. This mapping allows Tripal to compare content across Tripal sites, and expose data to computational tools that understand these vocabularies. You can create as many Tripal Content Types as you would like through Administration > Structure > Tripal Content Types provided you can define it using a controlled vocabulary term.
+
+By default, Tripal uses Chado as its primary data storage back-end.  When a bundle is created, the Tripal Chado module allows you to map a Bundle to a table in Chado.  Thus, any content type desired can be define as well as how it is stored in Chado--all using the administrative interface.
+
+Entity
+------
+
+An entity is a discrete data record.  Entities are most commonly seen as "pages" on a Drupal web site and are instances of a Bundle (i.e content type). When data is published on a Tripal site such as organisms, genes, germplasm, maps, etc., each record is represented by a single entity with an entity ID as its only attribute.   All other information that the entity provides is made available via Fields.
+
+Fields
+------
+
+A field is a reusable "data container" that is attached to a Bundle. Programmatically, each field provides one or more primitive data types, with validators and widgets for editing and formatters for display. Each field independently manages the data to which it assigned.  Just like with Bundles, Fields are also described using controlled vocabulary terms.  For example, a gene Bundle has a field attached that provides the name of the gene.   This field only provides the name and nothing more.  Tripal uses the `schema:name <http://schema.org/name>`_ vocabulary term to describe the field.
+
+Field Instances
+---------------
+
+Fields describe "atomic" units of data that are associated with an entity.  For example, a "name" is an atomic unit of data about a Gene or Organism entity. Fields can be reused for multiple Bundles. For example, gene, mRNA, genetic markers and variants all have name data.  Despite that all of these Bundles provides a "name", we only need one field to describe that this data is a "name".  However, we may want to customize a field specific to each bundle.  Therefore, an Instance of a field is attached to a bundle, and field instances can then be customized differently.  The most important customization is the one that defines the Chado table from which the data for a field is retrieved.   Despite that field instances are attached to bundles, they become visible with Entities.  When an entity is loaded for display, Drupal examines all of the fields that are attached to the entity's bundle, and then populates the fields instances with data specific to the entity being loaded.
+
+Entity Types
+------------
+
+An entity type is simply a group of Bundles that have some similarity.  For examples Drupal provides a Node entity type. The Node entity type contains the Basic Page and Article Bundles.  Tripal v2 expanded the Node entity type when creating new content.  Tripal v3, however, uses an a new entity type named TripalEntity that provides the Organism, Gene, Analysis, etc. content types.  Using these new entity types provides a a more responsive solution then the Node entity type, is more flexible, and supports the new ontology-driven approach of Tripal v3.

BIN
docs/dev_guide/exporting_field_settings.1.assign_term.png


BIN
docs/dev_guide/exporting_field_settings.2.png


BIN
docs/dev_guide/exporting_field_settings.3.png


BIN
docs/dev_guide/exporting_field_settings.4.png


BIN
docs/dev_guide/exporting_field_settings.5.png


BIN
docs/dev_guide/exporting_field_settings.6.png


BIN
docs/dev_guide/exporting_field_settings.7.png


+ 122 - 0
docs/dev_guide/exporting_field_settings.rst

@@ -0,0 +1,122 @@
+Exporting Field Settings With Drupal Features
+================================================
+
+This guide will demonstrate using the `Drupal Features <https://www.drupal.org/docs/7/modules/features>`_ to export and import Tripal Bundle Field settings.
+
+Why Drupal Features?
+---------------------
+
+Consider a case where we have a development site where we configure a bundle (let's say the vanilla Analysis bundle) to have a custom set of Tripal Panes, with fields carefully reorganized into the panes.  In particular, we attach a Drupal File field to it, so we can host FASTA files easily.
+
+Once we've configured the field settings, how do we get them to the live site?  One option is to open our field configuration admin UI on both sites, and copy the details one at a time.  This method is `time consuming and error prone <https://www.drupal.org/docs/7/modules/features/features-moving-site-configuration-to-code>`_, although it is relatively safe: we aren't liable to accidentally break our database this way.  Alternatively, trying to transfer via writing database exports is dangerous, and liable to accidentally break our site.
+
+Is there a better way?  By exporting the field configuration as a feature!
+
+.. note::
+
+  Drupal 8 has a `feature designed to handle development deployment <https://www.phase2technology.com/blog/drupal-8-configuration-management>`_: Configuration Management!  Drupal Features remains relevant for sharing field configurations across sites, so this guide may remain useful when Tripal moves to Drupal 8.
+
+.. warning::
+
+  Note that I'm going to talk about features a lot in this post.  **In the context of the Feature module, features are exported configurations within your site**.  It's important that we don't confuse this with Chado features, which are genes, mRNAs, polypeptides, etc.
+
+Version control
+~~~~~~~~~~~~~~~~
+
+Because features get exported as their own module, this means you can treat them as such.  You can initialize a git repo, store them on GitHub, make discrete versions, and in general benefit from version control for something which otherwise would only be done via the UI.
+
+How many Features?
+~~~~~~~~~~~~~~~~~~~
+
+The features documentation links to a great article about `how to manage multiple features <http://kerasai.com/blog/2014/04/08/organizing-features-configuration-managment>`_.  The suggestion that each bundle be its own module is particularly helpful for Tripal, since we have so many bundles and its a reasonable, discrete way to manage and deploy configuration.  This means that mRNA and gene should be separate feature modules even though they are both ``chado.feature`` content types.
+
+A Hazard: Mapping bundle IDs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+The main hurdle to overcome when mapping features is converting the field machine names across site.  This is because each field instance is specific to the bundle it's attached to, and bundle machine-names are from the bundle ID.  We can't assume bundle IDs are consistent across sites!
+
+So what do we do?  Interestingly, roles have a similar problem, and a guide is `available <https://www.drupal.org/docs/7/modules/features/exportables-and-user-role-ids-in-features>`_ for dealing with them.
+
+The general strategy is:
+
+-   remove the exported id value from the ``features.inc`` file
+-   use hook alter to fetch the ID on the target deployment setup
+
+In our example below, the bundle ID's match on our site.  For default Tripal bundles, this should generally be the case.
+
+Drupal Features Tutorial
+-------------------------
+
+
+In this tutorial, we'll add a FASTA file field to the Analysis bundle, and export the configuration.
+
+Configuring the bundle fields
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To begin, let's quickly configure a bundle.  Navigate to the structure of your site and select Analysis (**Admin ->Structure -> Tripal Content -> Analysis -> Manage Fields**).  Scroll to the bottom and add a new field of type File, with a machine name of ``field_fasta_file‎``, and click **Save**. Be sure to change the **Allowed extensions** parameter to accept ``.fasta``, otherwise, it will only allow `.txt` files to be uploaded.  You may also want to increase the file size limit, as the default 2MB can be too small for many FASTA files.
+
+We now have to pick a CV term for our field.  Because we are providing a FASTA file field, we can use a term such as FASTA `(SWO:0000142 <https://www.ebi.ac.uk/ols/ontologies/ero/terms?iri=http%3A%2F%2Fwww.ebi.ac.uk%2Fefo%2Fswo%2FSWO_0000142>`_.  Please see  :ref:`adding_a_cvterm` for help loading a term.  Once the term is in our DB, we can assign it to this field.
+
+
+.. image:: ./exporting_field_settings.1.assign_term.png
+
+
+Now lets configure our field display.  Click the **Manage Display Tab** at the top, and create and enable a "Files" Tripal Pane, placing our new field in the Pane.
+
+
+.. image:: ./exporting_field_settings.2.png
+
+
+You can verify your new field is enabled and working by creating a new Analysis and inspecting the "FASTA file" field.
+
+.. image:: ./exporting_field_settings.3.png
+
+
+Exporting the bundle field displays
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Once we are happy with our bundle field configuration, we can export the display settings using the Drupal Features module.
+
+First, we enable the Features module using drush: ``drush pm-enable features -y``.  This adds a Features area under **Admin -> Structure**.  Navigate there and choose **Create Feature**.
+
+The field information we're looking for is in **Field Bases**, **Field Group**, and **Field Instances**.  We can search for FASTA to find the field base and instance, and "files" (the name of our group) to find the field group.
+
+.. note::
+
+  Both **Field Bases** and **Field Instances** will contain the machine name of the field you want to export. **Field Bases** contains the site-wide information for a field and **Field Instances** contains the bundle-specific (i.e. Tripal Content Type) settings.
+
+  **Field Group** will contain the machine name of the Tripal Pane and allows you to export the grouping settings you set on the **Manage Display Tab**.
+  
+I've also specified a custom path to keep all my Tripal features together under advanced options.
+
+
+.. image:: ./exporting_field_settings.4.png
+
+If we download and unzip our feature module, we can see it includes all the trappings of a Drupal module.
+
+.. image:: ./exporting_field_settings.5.png
+
+
+.. warning::
+
+	As you can see, it makes the assumption that ``bio_data_2``, the bundle ID for Analysis on our source site, is the correct bundle to configure fields for.  However, Tripal makes no guarantee that will hold true on our target site.  One solution would be to manually relabel ``bio_data_x`` to the correct bundle ID.  On a smaller scale, this is a reasonable solution.  If you aren't sure what your bundle ID is, look in the URL when configuring the fields for it:  my constructed URL for example was ``admin/structure/bio_data/manage/bio_data_2/fields``.
+
+  In our case, the site we want to import to has the same Analysis bundle ID, so no further action is necessary.
+
+
+Importing the feature configuration
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Go to our target site, all we need to do is download and unpack the ``.tar`` file we generated and enable the module (assuming the bundle ID issue is addressed).  I downloaded my file to ``/var/www/html/sites/all/modules/custom/analysis_configuration.tar``, decompressed it (``tar -xvf analysis_configuration.tar``), and enabled it (``drush pm-enable tripal_configuration``).
+
+The field should now appear when you go to create a new analysis on your target site.  To check for yourself, create a new Analysis with dummy information: you'll be able to upload a file for the new file field.
+
+Unfortunately, the field still gets imported **disabled** due to Tripal preference, so we have to go to the display settings on our target site and enable the tripal pane/field.
+
+
+.. image:: ./exporting_field_settings.6.png
+
+
+Drag the disabled Tripal pane/field group out of the disabled area, click save, and re-visit your newly created Analysis.  The files pane and uploaded FASTA file will now appear.
+
+.. image:: ./exporting_field_settings.7.png

+ 31 - 0
docs/dev_guide/introduction.rst

@@ -0,0 +1,31 @@
+
+Introduction to the Tripal API
+==============================
+
+Tripal provides an Application Programming Interfaces (API) that allows developers to interact with and customize Tripal. Using the API, developers can customize the way data is presented or create custom modules that provide new or different functionality. These custom modules can in turn be shared with the Tripal community. The Tripal API is documented online at http://api.tripal.info/api/tripal/3.x (COMING SOON). This document provides examples and best practices for using the Tripal API.
+
+Requirements
+-------------------------------
+
+In order to use the Tripal API the developer should have the following skills:
+
+Common skills:
+
+* Knowledge of PHP.
+* Knowledge of relational databases and SQL.
+
+To create custom modules:
+
+* Familiarity with Drupal API
+* Familiarity with Drupal module development
+
+To use Chado for data storage:
+
+* Knowledge of Chado and relationships between tables (at least tables where data of interest is stored).
+* An idea how data is stored in Chado (or a willingness to ask questions)
+
+Things that will make your Tripal development experience postive, fun and rewarding:
+
+* A desire to write code that can be re-used and shared by other groups
+* A desire to share your work with others to support other the Tripal community members.
+* A willingness to ask for help on the Tripal Github issue queue if you get stuck, find bugs or desire new features.

+ 55 - 0
docs/dev_guide/rtd.rst

@@ -0,0 +1,55 @@
+Using ReadTheDocs
+=================
+
+.. note::
+
+  For Tripal's own ReadTheDocs guidelines: :ref:`tripal_rtd`.
+
+
+What is ReadTheDocs?
+--------------------
+
+Documentation is important. It tells users how to use our product, and developers how to read our code.  We recommend hosting documentation for your custom module somewhere easily accessible, like ReadTheDocs.
+
+`ReadTheDocs <https://readthedocs.org/>`_ (RTD) uses the `Sphinx <http://www.sphinx-doc.org/en/master/>`_ framework to build a website in a Continuous Integration setup. Your RTD-compatible documentation is added directly to your module and when code changes are pushed to GitHub, the documentation website as defined by sphinx is built, and the "live" documentation website is updated.
+
+Benefits to housing documentation inside of your module code are:
+
+- Code changes can include documentation updates **in the same pull request**.
+- Changes to the documentation is **subject to review, just like code changes**.
+- Documentation changes are under **version control**.
+
+How do I add ReadTheDocs to my project?
+---------------------------------------
+Below is a quick overview of steps for integrating your module's documentation with RTD:
+
+- Set up your ReadTheDocs account and import your project.
+- Install Sphinx.
+- Create a ``docs`` folder at the root of your project and navigate into it.
+- Run the quickstart command: ``sphinx-quickstart``.
+  - This creates necessary site configuration files (``conf.py``) as well as the make script to build your site.
+- Write your documentation (we're using reStructuredText (RST) format):
+  - Create an ``index.rst`` as the home page.
+  - Link other RST documents in your ``index.rst``.
+- Once the guide is on your master branch on GitHub, ReadTheDocs will handle the rest!
+
+For a detailed walkthrough, please see the `official ReadTheDocs getting started guide <https://docs.readthedocs.io/en/latest/getting_started.html>`_.
+
+For RTD integration we recommend using reStructuredText (RST) to write your documentation. It might seem a little more complicated than markdown (and it is), but it's also more powerful.  The choice is yours for which format to use.
+
+ReadTheDocs also provides **versioning** tools, allowing you to post releases of the documentation so users can go back and find older documentation with almost no effort on your part.
+
+What should my documentation include?
+-------------------------------------
+
+We suggest that your module include the following sections:
+
+- Overview
+- Installation and Setup Guide
+- User's Manual
+
+The Overview section should describe what features your module offers.
+
+The Installation and setup section should guide a site administrator through installing and setting up your module.  Any site-wide settings that need to be configured, environmental variables set, or anything else not handled by the automated Drupal install should be covered.
+
+The User's guide should walk through the day-to-day usage of your module.  This may include using custom importers, dashboards, or simply summaries of the new content this module provides.

+ 31 - 0
docs/dev_guide/tutorials.rst

@@ -0,0 +1,31 @@
+Video Tutorials
+=================
+
+.. note::
+
+
+  The following videos are provided by members of the community. While we don't ensure the videos are up to date, we would appreciate being `contacted through the issue queue <https://github.com/tripal/tripal/issues/new/choose>`_ if you find a broken link. Also, if you have your own video and would like to contribute, we would love to hear from you!
+
+
+
+Testing
+~~~~~~~~~
+
+The `Tripal Test Suite module <https://github.com/statonlab/TripalTestSuite>`_ is used by the core Tripal module, and many custom modules, to easily set up PHPUnit and Travis CI.
+
+* `Creating and running Test Suite Tests <https://www.youtube.com/watch?v=hxuiDzRqs9U>`_
+* `Test Suite Factories and DB Transactions <https://www.youtube.com/watch?v=PTJ1Dv8QAag>`_
+* `Test Suite Seeding and Publishing Data <https://www.youtube.com/watch?v=HE2B7YnWYfQ>`_
+* `Test Driven Development live demo <https://www.youtube.com/watch?v=zmYZ_HV3b9s>`_
+
+Developer Tools
+~~~~~~~~~~~~~~~~
+
+* `Tripal 3 - Setting Up A Website From Scratch With TripalDock <https://www.youtube.com/watch?v=5SOfQLypvdE>`_
+
+* `Tripal 3 - TripalDock Commands And Site Structure <https://www.youtube.com/watch?v=g_fmONUgG3s>`_
+
+Other
+~~~~~~~
+
+* `Loading Biomaterials with Tripal Analysis Expression <https://www.youtube.com/watch?v=7YkPp2443qA>`_

+ 26 - 0
docs/extensions.rst

@@ -0,0 +1,26 @@
+Extension Modules
+==================
+
+The below modules are Tripal 3 compatible and grouped roughly by category:
+
+.. toctree::
+   :maxdepth: 2
+
+   extensions/administrative
+   extensions/analysis
+   extensions/data_input
+   extensions/developer
+   extensions/3rd_party
+   extensions/search
+   extensions/visualization
+   extensions/in_development
+
+If you don't see the module you are looking for here, try a `Tripal-specific search on GitHub <https://github.com/search?q=topic%3Atripal>`__.
+
+Informational links for this extension module list:
+
+.. toctree::
+   :maxdepth: 1
+
+   extensions/module_rating
+   extensions/instructions

+ 52 - 0
docs/extensions/3rd_party.rst

@@ -0,0 +1,52 @@
+Third-party Integration
+=======================
+
+The following modules provide integration with external third-party tools. For example, they may allow you to easily embed the tool in Drupal/Tripal pages and/or expose data from the tool on your Tripal site.
+
+BrAPI
+-----
+
+This module provides a Breeding API end point on your Tripal site as well as a user query interface and an auto-query system to integrate external BrAPI end point data into your site dynamically. An administrative interface allows you to adjust the module settings according to the way you use Chado. A couple of hooks are also provided for module developers in order to allow customization/extension of calls.
+
+`Documentation <https://brapi.readthedocs.io/en/latest/>`__
+`Repository <https://github.com/tripal/brapi>`__
+
+Tripal Blast
+------------
+
+.. image:: https://tripal.readthedocs.io/en/7.x-3.x/_images/Tripal-Gold.png
+  :target: https://tripal.readthedocs.io/en/7.x-3.x/extensions/module_rating.html#Gold
+  :alt: Tripal Rating: Gold
+  
+This module provides a basic interface to allow your users to utilize your server's NCBI BLAST+. There is a simple interface allowing users to paste or upload a query sequence and then select from available databases and a tabular results listing with alignment information and multiple download formats (HTML, TSV, GFF3, XML) available.
+
+`Documentation <https://github.com/tripal/tripal_blast/blob/7.x-1.x/README.md>`__
+`Repository <https://github.com/tripal/tripal_blast>`__
+
+Tripal Galaxy
+-------------
+
+.. image:: https://tripal.readthedocs.io/en/7.x-3.x/_images/Tripal-Gold.png
+  :target: https://tripal.readthedocs.io/en/7.x-3.x/extensions/module_rating.html#Gold
+  :alt: Tripal Rating: Gold
+
+This module is for integration of Tripal and the Galaxy Project. It facilitates the creation of forms on your Tripal site that submit jobs to a galaxy instance.
+
+`Documentation <https://tripal-galaxy.readthedocs.io/en/latest/?badge=latest>`__
+`Repository <https://github.com/tripal/tripal_galaxy>`__
+
+Tripal JBrowse
+--------------
+
+This module provides integration between Tripal sites and pre-existing GMOD JBrowse instances. It allows you to create pages on your Tripal site with an embedded JBrowse instance by filling out a simple form.
+
+`Documentation <https://github.com/tripal/tripal_jbrowse/blob/7.x-2.1.x/README.md>`__
+`Repository <https://github.com/tripal/tripal_jbrowse>`__
+
+VCF Filter
+-----------
+
+This modules provides a form interface so users can custom filter existing VCF files and export in a variety of formats. The form simply provides an interface to VCFtools and uses the Tripal Download API to provide the filtered file to the user.
+
+`Documentation <https://github.com/UofS-Pulse-Binfo/vcf_filter/blob/master/README.md>`__
+`Repository <https://github.com/UofS-Pulse-Binfo/vcf_filter>`__

BIN
docs/extensions/Tripal-Bronze.png


ファイルの差分が大きいため隠しています
+ 0 - 0
docs/extensions/Tripal-Bronze.svg


BIN
docs/extensions/Tripal-Gold.png


ファイルの差分が大きいため隠しています
+ 0 - 0
docs/extensions/Tripal-Gold.svg


BIN
docs/extensions/Tripal-Silver.png


ファイルの差分が大きいため隠しています
+ 0 - 0
docs/extensions/Tripal-Silver.svg


+ 33 - 0
docs/extensions/administrative.rst

@@ -0,0 +1,33 @@
+Administrative
+==============
+
+The following modules provide support to Tripal site administrators.
+
+Tripal Alchemist
+-----------------
+
+Tripal Alchemist allows you to transform entities from one type to another.  Define multiple bundles with the same base table and easily convert existing entities to the new type.
+
+`Documentation <https://github.com/statonlab/tripal_alchemist/blob/master/README.md>`__
+`Repository <https://github.com/statonlab/tripal_alchemist>`__
+
+
+Tripal Curator
+-------------------------
+
+Tripal Curator is a Toolbox for curating Chado Properties.  Split properties into multiple child properties, and mass reassign property CVterms.
+
+`Documentation <https://github.com/statonlab/tripal_curator/blob/master/README.md>`__
+`Repository <https://github.com/statonlab/tripal_curator>`__
+
+Tripal HeadQuarters
+-------------------
+
+.. image:: https://tripal.readthedocs.io/en/7.x-3.x/_images/Tripal-Gold.png
+  :target: https://tripal.readthedocs.io/en/7.x-3.x/extensions/module_rating.html#Gold
+  :alt: Tripal Rating: Gold
+
+Tripal HeadQuarters (HQ) provides an administrative layer for Tripal, giving users limited access to content creation forms which must be approved by an admin before they are inserted into the database. Admins can use Chado-specific permissions to define organism or project-specific administrative rights.
+
+`Documentation <https://tripal-hq.readthedocs.io/en/latest/index.html>`__
+`Repository <https://github.com/statonlab/tripal_hq>`__

+ 50 - 0
docs/extensions/analysis.rst

@@ -0,0 +1,50 @@
+Analysis/Annotation
+===================
+
+The following modules provide support for loading annotation or analysis' into Chado and displaying them on Tripal pages.
+
+Tripal Analysis Expression
+--------------------------
+
+A module for loading, annotating, and visualizing NCBI Biomaterials and expression data.
+
+`Documentation <https://github.com/tripal/tripal_analysis_expression/blob/master/README.md>`__
+`Repository <https://github.com/tripal/tripal_analysis_expression>`__
+
+Tripal Analysis Blast
+---------------------
+
+This module extends the Tripal Analysis Module and provides a method for loading XML results from the NCBI blast program. Blast results appear on each feature page.
+
+`Documentation <https://tripal.readthedocs.io/en/latest/user_guide/example_genomics/func_annots/blast.html>`__
+`Repository <https://github.com/tripal/tripal_analysis_blast>`__
+
+Tripal Analysis KEGG
+--------------------
+
+This module provides a method for loading of KEGG ortholog assignments derived from the KEGG Automated Annotation Server (KAAS). KEGG assignments appear on each feature page, and a full KEGG report is available for browsing results once uploaded.
+
+`Repository <https://github.com/tripal/tripal_analysis_kegg>`__
+
+Tripal Analysis Interpro
+------------------------
+
+This module provides a method for loading XML results from the InterProScan program. The module can load InterProScan XML v4 or InterProScan XML v5 generated from the command-line or web-based versions of InterProScan. Additionally, GO terms mapped by InterProScan can optionally be assigned to features.
+
+`Documentation <https://tripal.readthedocs.io/en/latest/user_guide/example_genomics/func_annots/interpro.html>`__
+`Repository <https://github.com/tripal/tripal_analysis_interpro>`__
+
+Tripal CV-Xray
+--------------
+
+Tripal CV-Xray maps content annotations onto controlled vocabulary trees.  The end result is a browseable CV field that lets users explore ontologies and find associated content.
+
+`Documentation <https://github.com/statonlab/tripal_cv_xray/blob/master/README.md>`__
+`Repository <https://github.com/statonlab/tripal_cv_xray>`__
+
+Tripal OrthoQuery
+-----------------
+
+The OrthoQuery module identifies orthologous genes via a Tripal database, standardizes the data for comparative analysis, performs the OrthoFinder analysis through the Tripal Galaxy API, sends the data to the user’s database profile, and provides interactive visualizations. Visualization features focus on facilitating the interrogation of large gene families, examining relationships among families, and allowing direct query of the stored orthogroups and their function. Orthoquery is currently in development.
+
+`Repository <https://gitlab.com/TreeGenes/orthoquery>`__

+ 56 - 0
docs/extensions/data_input.rst

@@ -0,0 +1,56 @@
+Data Loading/Collection
+=======================
+
+The following modules provide interfaces for collection and/or loading of biological data.
+
+Genotype Loader
+----------------
+
+A Drush-based loader for VCF files that follows the genotype storage rules outline by ND genotypes. It has been optimized to handle very large files and supports customization of ontology terms used.
+
+`Documentation <https://genotypes-loader.readthedocs.io/en/latest/>`__
+`Repository <https://github.com/UofS-Pulse-Binfo/genotypes_loader>`__
+
+Mainlab Chado Loader
+---------------------
+
+MCL (Mainlab Chado Loader) is a module that enables users to upload their biological data to Chado database schema. Users are required to transfer their biological data into various types of data template files. MCL, then, uploads these data template files into a chado schema.
+
+`Documentation <https://gitlab.com/mainlabwsu/mcl/blob/master/README.md>`__
+`Repository <https://gitlab.com/mainlabwsu/mcl>`__
+
+Raw Phenotypes
+---------------
+
+This module was designed to aid in collection and further analysis of raw phenotypic data. It supports Excel drag-n-drop uploads with immediate validation and researcher feedback. Additionally, it provides summary charts and download functionality.
+
+`Documentation <https://github.com/UofS-Pulse-Binfo/rawphenotypes/blob/master/README.md>`__
+`Repository <https://github.com/UofS-Pulse-Binfo/rawphenotypes>`__
+
+Tripal BibTeX
+--------------
+
+A BibTEX importer for Tripal Publications. Currently this module only provides a Drush function (``tripal-import-bibtex-pubs``; ``trpimport-bibtex``) for import of BibTEX files.
+
+`Documentation <https://github.com/UofS-Pulse-Binfo/tripal_bibtex/blob/7.x-3.x/README.md>`__
+`Repository <https://github.com/UofS-Pulse-Binfo/tripal_bibtex>`__
+
+Tripal Plant PopGen Submission
+-------------------------------
+
+.. image:: https://tripal.readthedocs.io/en/7.x-3.x/_images/Tripal-Silver.png
+  :target: https://tripal.readthedocs.io/en/7.x-3.x/extensions/module_rating.html#Silver
+  :alt: Tripal Rating: Silver
+
+The Tripal Plant PopGen Submit (TPPS) Module supports a flexible submission interface for genotype, phenotype, environmental, and metadata for population, association, or landscape genetics studies. The portal walks the user through specific questions and collects georeferenced coordinates on plant accessions and also supports ontology standards, including the Minimal Information About a Plant Phenotyping Experiment (MIAPPE) (http://www.miappe.org/) and standard genotyping file formats, such as VCF.
+
+`Documentation <https://tpps.readthedocs.io/en/latest/>`__
+`Repository <https://gitlab.com/TreeGenes/TGDR>`__
+
+Migrate Chado
+-------------
+
+This module is a collection of destination plugins to import biological data to a Chado database using `Drupal Migrate <https://www.drupal.org/project/migrate>`_. The Migrate module provides a flexible framework for migrating content into Drupal from other sources (e.g., when converting a web site to Drupal). Content is imported and rolled back using a bundled web interface (Migrate UI module) or included Drush commands (strongly recommended).
+
+`Documentation <https://www.drupal.org/docs/7/modules/migrate-chado>`__
+`Repository <https://www.drupal.org/project/migrate_chado>`__

+ 53 - 0
docs/extensions/developer.rst

@@ -0,0 +1,53 @@
+
+Developer Tools
+===============
+
+The following modules provide support to both core and extension Tripal developers.
+
+TripalDock
+------------
+
+TripalDock is a command line tool that helps with creating and running Tripal sites using Docker. This tool is designed for developers and is not suitable for production.
+
+`Documentation <https://github.com/statonlab/tripaldock/blob/master/README.md>`__
+`Repository <https://github.com/statonlab/tripaldock>`__
+
+Tripal Download API
+--------------------
+
+This module provides an API for downloading Tripal/Chado data. Since download functionality is often sought after for Tripal sites and Views Data Export is not currently meeting our needs, this module aims to provide an API to aid module and site developers in making efficient, user friendly downloads available.
+
+`Documentation <https://github.com/tripal/trpdownload_api/blob/7.x-1.x/README.md>`__
+`Repository <https://github.com/tripal/trpdownload_api>`__
+
+Tripal D3.js API
+-----------------
+
+Provides d3.js integration for Tripal. It provides an API for developing consistent biological diagrams with a common configuration, as well as, providing some common diagrams such as pie, bar, column and pedigree diagrams.
+
+`Documentation <https://github.com/tripal/tripald3/blob/7.x-1.x/README.md>`__
+`Repository <https://github.com/tripal/tripald3>`__
+
+Tripal Fields Generator
+-----------------------
+
+This is a CLI tool to help automate the generation of Tripal fields.
+
+`Documentation <https://github.com/tripal/fields_generator/blob/master/README.md>`__
+`Repository <https://github.com/tripal/fields_generator>`__
+
+Tripal Rapid Installer
+----------------------
+
+Provides rapid installation of a Tripal site using Drush.
+
+`Documentation <https://github.com/tripal/tripal_install/blob/master/README.md>`__
+`Repository <https://github.com/tripal/tripal_install>`__
+
+Tripal Test Suite
+-----------------
+
+Tripal Test Suite is a composer package that handles common test practices such as bootstrapping Drupal before running the tests, creating test file, creating and managing database seeders (files that seed the database with data for use in testing) and much more.
+
+`Documentation <https://tripaltestsuite.readthedocs.io/en/latest/>`__
+`Repository <https://github.com/tripal/TripalTestSuite>`__

+ 30 - 0
docs/extensions/in_development.rst

@@ -0,0 +1,30 @@
+
+In Development
+==============
+
+The following modules are not yet ready for production or not fully Tripal 3 compatible.
+
+
+TripalMap
+-----------
+
+TripalMap MapViewer module displays map data stored in Chado. MapViewer provides interfaces to view all linkage groups of a map, choose a linkage group and zoom in to a specific region of a linkage group, compare maps that share the same markers and change colors of markers/QTL. The interface can be integrated into Tripal map page and hyperlinked to/from any Tripal page that are displayed in maps (marker, QTL, heritable morphological marker and/or gene). The admin page allows site developers some flexibility in the display pattern.
+
+`Documentation <https://gitlab.com/mainlabwsu/tripal_map/blob/master/README.md>`__
+`Repository <https://gitlab.com/mainlabwsu/tripal_map>`__
+
+Tripal Apollo
+--------------
+
+Tripal Apollo lets you manage user accounts for your JBrowse Apollo instances on your Tripal site.  Provides a form to request apollo access, an Apollo instance content type that connects to Chado Organisms, and an administrative interface for managing requests.
+
+`Documentation <https://tripal-apollo.readthedocs.io/en/latest/>`__
+`Repository <https://github.com/NAL-i5K/tripal_apollo>`__
+
+Tripal Multi-Chado
+------------------
+
+The Multi-Chado module is an extension for Tripal 2.x and 3.x (dev branch under testing) that can be used to install more than one Chado instance across different schemata of your choice and it also enables the use of different PostgreSQL database credentials allowing the administrator to do fine tuning of database accesses. With it you can use a *same Drupal instance* for both a *public* and a *private* Chado instance, have *different releases* or *separated species*, provide a *sandbox*, run *tests* on a blank instance and more (dev-staging-prod, etc.).
+
+`Documentation <http://cgit.drupalcode.org/tripal_mc/plain/README.md?h=7.x-1.x>`__
+`Repository <https://www.drupal.org/project/tripal_mc>`__

+ 33 - 0
docs/extensions/instructions.rst

@@ -0,0 +1,33 @@
+
+Adding your Module to this list!
+==================================
+
+**We would love for you to contribute your own module to this list!** This is done by creating a Pull Request (PR) to `Tripal <https://github.com/tripal/tripal>`__ modify our documentation.
+
+Instructions
+-------------
+
+1. From the current page, click the category in the list above that best fits your module.
+2. Click the "Edit on Github" link at the top of the page.
+3. Add your module using the following template.
+
+.. code:: RST
+
+  Module Name
+  ------------
+
+  This module loads in X, Y, and Z.  It provides admin for A and B, and user area C.
+
+  `Documentation <https://yourmodule.readthedocs.io/en/latest/index.html>`__
+  `Repository <https://github.com/you/yourmodule>`__
+
+4. Rate your module using the :doc:`./module_rating` and mention in your PR description which requirements your module meets.
+
+Guidelines
+------------
+
+- Make sure to follow alphabetical order when choosing where on the category page to add your module.
+- Please write two sentences MAXIMUM about the function of the module.
+- Include links to both the documentation (even if it's your README) and the repository (e.g. Github, Gitlab)
+- If your module doesn't fit well in any of the existing categories, still pick the best one but then feel free to suggest a new category in the PR description.
+- Extension Modules must be publicly available for download

+ 83 - 0
docs/extensions/module_rating.rst

@@ -0,0 +1,83 @@
+
+Tripal Module Rating System
+=============================
+
+This module rating system is meant to aid Tripal Site Administrators in choosing extension modules for their site. It is also meant to guide developers in module best practices and celebrate modules which achieve these goals.
+
+Bronze
+-------
+
+.. image:: Tripal-Bronze.png
+
+- Has a public release.
+- Should install on a Tripal site appropriate for the versions it supports.
+- Defines any custom tables or materialized views in the install file (if applicable).
+- Adds any needed controlled vocabulary terms in the install file (Tripal3).
+- Provides Installation and admin instructions README.md (or `RTD <https://tripal.readthedocs.io/en/latest/dev_guide/rtd.html>`_).
+- Has a license (distributed with module).
+
+Silver
+-------
+
+.. image:: Tripal-Silver.png
+
+- Follows basic Drupal Coding standards; specifically, `code format <https://www.drupal.org/docs/develop/standards/coding-standards>`_ and `API documentation <https://www.drupal.org/docs/develop/standards/api-documentation-and-comment-standards#drupal>`_.
+- Uses Tripal API functions. Specifically, it should use the
+    - Chado Query API for querying chado (if using chado as the storage system). (`API <http://api.tripal.info/api/tripal/tripal_chado%21api%21tripal_chado.query.api.inc/group/tripal_chado_query_api/3.x>`_, :doc:`Tutorial <../dev_guide/chado>`)
+    - Tripal Jobs API for long running processes. (`API  <http://api.tripal.info/api/tripal/tripal%21api%21tripal.jobs.api.inc/group/tripal_jobs_api/3.x>`_)
+    - TripalField class to add data to pages (Tripal3). (:doc:`Tutorial <../dev_guide/custom_field>`)
+- Provides ways to customize the module (e.g. drush options, field/formatter settings, admin UI).
+- Latest releases should follow Drupal naming best practices.
+    - e.g. first release for Drupal 7 should be: ``7.x-1.0``.
+
+Gold
+-----
+
+.. image:: Tripal-Gold.png
+
+- Extensive documentation for the module (similar to Tripal User's Guide). ( `Tutorial <https://tripal.readthedocs.io/en/latest/dev_guide/rtd.html>`_)
+- Unit testing is implemented using PHPUnit with the TripalTestSuite or something similar.
+- Continuous integration is setup (e.g. such as with TravisCI).
+- Imports data via Tripal's importer class (Tripal3) (:doc:`Tutorial <../dev_guide/custom_data_loader>`).
+- Tripal 3 fields are (:doc:`Tutorial <../dev_guide/custom_field/manual_field_creation>`)
+    - Fully compatible with web services.
+    - The elementInfo function is fully implemented.
+    - The query and queryOrder functions fully implemented.
+- Web Services uses Tripal's Web Service Classes (Tripal3). (:doc:`Tutorial <../dev_guide/custom_web_services>`)
+- Code sniffing and testing coverage reports (optional but encouraged).
+- Drupal.org vetted release (optional but encouraged).
+
+Rate your Extension Module!
+-----------------------------
+
+We encourage Tripal module developers to rate their modules. This can be done by :doc:`./instructions`
+
+The following badges are for inclusion on your module README and documentation; however, they are only valid if your module has been included in :doc:`../extensions` with the given rating.
+
+reStructuredText
+
+.. code-block:: RST
+
+    .. image:: https://tripal.readthedocs.io/en/7.x-3.x/_images/Tripal-Bronze.png
+      :target: https://tripal.readthedocs.io/en/7.x-3.x/extensions/module_rating.html#Bronze
+      :alt: Tripal Rating: Bronze
+
+
+Markdown
+
+.. code-block:: MD
+
+    [![Tripal Rating Bronze Status](https://tripal.readthedocs.io/en/7.x-3.x/_images/Tripal-Bronze.png)](https://tripal.readthedocs.io/en/7.x-3.x/extensions/module_rating.html#Bronze)
+
+
+HTML
+
+.. code-block:: html
+
+    <a href='https://tripal.readthedocs.io/en/7.x-3.x/extensions/module_rating.html#Bronze'>
+        <img src='https://tripal.readthedocs.io/en/7.x-3.x/_images/Tripal-Bronze.png' alt='Tripal Rating: Bronze' />
+    </a>
+
+.. note::
+
+   Replace all instances of ``Bronze`` with either ``Silver`` or ``Gold`` for those badges.

+ 42 - 0
docs/extensions/search.rst

@@ -0,0 +1,42 @@
+Searching
+==========
+
+CartograTree
+-------------
+
+CartograTree is a web-based application that allows researchers to identify, filter, compare, and visualize geo-referenced biotic and abiotic data. Its goal is to support numerous multi-disciplinary research endeavors including: phylogenetics, population structure, and association studies.
+
+`Documentation <https://cartogratree.readthedocs.io/en/latest/index.html>`__
+`Repository <https://gitlab.com/TreeGenes/CartograTree>`__
+
+Mainlab Chado Search
+---------------------
+
+Mainlab Chado Search is a module that enables advanced search function for biological data stored in a Tripal/Chado database. By default, a set of search interfaces are provided, such as 'Gene Search' for searching genes and/or transcripts, 'Marker Search' for searching genetic markers, and 'Sequence Search' for searching any sequences stored in the Chado feature table. Searches for other data types, such as QTL, Map, Trait, Stock, Organism are also provided but may require modification to the materialized view to adjust for site-specific data storage.
+
+`Documentation <https://gitlab.com/mainlabwsu/chado_search/blob/master/README.md>`__
+`Repository <https://gitlab.com/mainlabwsu/chado_search>`__
+
+Tripal ElasticSearch
+--------------------
+
+The Tripal ElasticSearch module allows you to easily manage the indexing and display of ElasticSearch on your Tripal website. It also easily enables Cross-Site Querying, allowing you to connect to other Tripal sites and provide additional search results to your users.
+
+`Documentation <https://github.com/tripal/tripal_elasticsearch/blob/master/docs/README.md>`__
+`Repository <https://github.com/tripal/tripal_elasticsearch>`__
+
+Tripal MegaSearch
+---------------------
+
+Tripal MegaSearch is a tool for downloading biological data stored in a Tripal/Chado database. The module was designed to be generic and flexible so it can be used on most Tripal sites. Site administrators may choose from 1) a set of predefined materialized views or 2) chado base tables as the data source to serve data. If neither data source is desired, developers may create their own materialized views and serve them through Tripal MegaSearch via a flexible dynamic query form. This form allows filters to be added dynamically and combined using 'AND/OR' operators. The filters correspond to the underlying data source columns so the user can filter data on each column.
+
+`Documentation <https://gitlab.com/mainlabwsu/tripal_megasearch/blob/master/README.md>`__
+`Repository <https://gitlab.com/mainlabwsu/tripal_megasearch>`__
+
+Tripal Sequence Similarity Search
+----------------------------------
+
+This module supports sequence similarity search on a Tripal website through a new dual application option. The Tripal module provides access to the speed increase available through Diamond for BLASTP/BLASTX style searches as well as traditional NCBI BLAST for BLASTN. Both applications are integrated into a single interface that provides file upload or copy/paste sequence support for the query and access to formatted databases for NCBI BLAST or Diamond. The target databases can be customized for the categories of whole genome, gene, protein, and transcriptome/unigene. The administration interface allows the admin user to set what pre-indexed databases are available (which show up in a dropdown menu). The module supports execution of the searches on a remote machine so that the search is not running directly on the limited resources typically associated with web servers.
+
+`Documentation <https://github.com/Ferrisx4/tripal_diamond/blob/master/README.md>`__
+`Repository <https://github.com/Ferrisx4/tripal_diamond>`__

+ 53 - 0
docs/extensions/visualization.rst

@@ -0,0 +1,53 @@
+Visualization/Display
+======================
+
+The following modules provide specialized displays for Tripal content types.
+
+Analyzed Phenotypes
+--------------------
+
+This module provides support and visualization for partially analyzed data stored in a modified GMOD Chado schema. It is meant to support large scale phenotypic data through backwards compatible improvements to the Chado schema including the addition of a project and stock foreign key to the existing phenotype table, optimized queries and well-chosen indexes.
+
+`Documentation <https://analyzedphenotypes.readthedocs.io/en/latest/index.html>`__
+`Repository <https://github.com/UofS-Pulse-Binfo/analyzedphenotypes>`__
+
+CvitEmbed
+----------
+
+This module integrates `CViTjs <https://github.com/LegumeFederation/cvitjs>`__ with Tripal to provide whole-genome visualizations. It creates one page per plot and makes them accessible via the Drupal Navigation menu.
+
+`Documentation <https://github.com/UofS-Pulse-Binfo/cvitembed/blob/master/README.md>`__
+`Repository <https://github.com/UofS-Pulse-Binfo/cvitembed>`__
+
+Mainlab Tripal Data Display
+----------------------------
+
+Mainlab Tripal Data Display contains a set of Drupal/PHP templates that organize and extend the default display of the biological data hosted on a Tripal-enabled site (i.e. http://tripal.info). Supported data type includes organism, marker, QTL, germplasm (stock), map (featuremap), project, heritable phenotypic marker (MTL), environment (ND geolocation), haplotype block, polymorphism, eimage, generic gene (genes created by parsing Genbank files using the Mainlab ```tripal_genbank_parser`` module), feature, and pub. Each of the templates can be turned on/off as desired.
+
+`Documentation <https://gitlab.com/mainlabwsu/mainlab_tripal/blob/master/README.md>`__
+`Repository <https://gitlab.com/mainlabwsu/mainlab_tripal>`__
+
+ND Genotypes
+-------------
+
+This module provides support and visualization of genotypic data stored in a modified GMOD Chado schema. The 3.x branch of this module represents a shift towards support for large scale genotypic datasets through backwards compatible improvements to the Chado schema including a new gathering table for genotypes (genotype_call) modeled after the Chado phenotype table, optimized queries and well-chosen indexes.
+
+`Documentation <https://nd-genotypes.readthedocs.io/en/latest/>`__
+`Repository <https://github.com/UofS-Pulse-Binfo/nd_genotypes>`__
+
+Phylotree
+-------------------
+
+This extension provides a simple file formatter for `Newick <http://evolution.genetics.washington.edu/phylip/newicktree.html>`__ tree files using
+the Javascript library `Phylotree <https://github.com/veg/phylotree.js/tree/master>`__ for display.
+
+`Documentation <https://cgit.drupalcode.org/phylotree/tree/README.md>`__
+`Repository <https://www.drupal.org/project/phylotree>`__
+
+Tripal Fancy Fields
+-------------------
+
+This module provides additional fields for use with Tripal 3. The current version provides a single-series chart field that can be displayed as a pie, donut, or bar chart, as well as, a simple table.
+
+`Documentation <https://github.com/tripal/trpfancy_fields/blob/master/README.md>`__
+`Repository <https://github.com/tripal/trpfancy_fields>`__

+ 16 - 0
docs/index.rst

@@ -0,0 +1,16 @@
+.. Tripal documentation master file, created by
+   sphinx-quickstart on Tue Aug 21 17:25:20 2018.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Welcome to Tripal's documentation!
+==================================
+
+.. toctree::
+   :maxdepth: 4
+   :caption: Contents:
+
+   user_guide
+   dev_guide
+   extensions
+   contributing

+ 36 - 0
docs/make.bat

@@ -0,0 +1,36 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+	set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=.
+set BUILDDIR=_build
+set SPHINXPROJ=Tripal
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+	echo.
+	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+	echo.installed, then set the SPHINXBUILD environment variable to point
+	echo.to the full path of the 'sphinx-build' executable. Alternatively you
+	echo.may add the Sphinx directory to PATH.
+	echo.
+	echo.If you don't have Sphinx installed, grab it from
+	echo.http://sphinx-doc.org/
+	exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
+
+:end
+popd

+ 1 - 1
docs/tripal_doxygen.config

@@ -38,7 +38,7 @@ PROJECT_NAME           = Tripal
 # could be handy for archiving the generated documentation or if some version
 # control system is used.
 
-PROJECT_NUMBER         = "v3.0 (7.x-3.0)"
+PROJECT_NUMBER         = "v3.0 (7.x-3.1)"
 
 # Using the PROJECT_BRIEF tag one can provide an optional one line description
 # for a project that appears at the top of each page and should give viewer a

+ 27 - 0
docs/user_guide.rst

@@ -0,0 +1,27 @@
+Tripal v3 User's Guide
+======================
+
+.. note:: 
+
+  Looking for the `Tripal v2 User's Guide <http://tripal.info/tutorials/v2.x>`_? 
+
+
+.. toctree::
+   :maxdepth: 1
+   :caption: Table of Contents
+   :glob:
+
+   user_guide/what_is_tripal
+   user_guide/install_tripal
+   user_guide/drupal_overview
+   user_guide/learn_chado
+   user_guide/content_types
+   user_guide/example_genomics
+   user_guide/file_management
+   user_guide/mviews
+   user_guide/custom_tables
+   user_guide/searching
+   user_guide/job_management
+   user_guide/web_services
+   user_guide/bulk_loader
+   user_guide/customize_site

BIN
docs/user_guide/bulk_loader.1.png


BIN
docs/user_guide/bulk_loader.10.png


BIN
docs/user_guide/bulk_loader.11.png


BIN
docs/user_guide/bulk_loader.12.png


BIN
docs/user_guide/bulk_loader.13.png


BIN
docs/user_guide/bulk_loader.2.png


BIN
docs/user_guide/bulk_loader.3.png


BIN
docs/user_guide/bulk_loader.4.png


BIN
docs/user_guide/bulk_loader.5.png


この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません