Browse Source

Merge pull request #27 from tripal/page-integration-v2

Create "Tripal JBrowse Page Integration" version 2.x
Lacey-Anne Sanderson 5 years ago
parent
commit
62c6c4d230

+ 22 - 21
composer.lock

@@ -115,27 +115,28 @@
         },
         {
             "name": "guzzlehttp/guzzle",
-            "version": "6.3.3",
+            "version": "6.4.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/guzzle.git",
-                "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba"
+                "reference": "0895c932405407fd3a7368b6910c09a24d26db11"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba",
-                "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0895c932405407fd3a7368b6910c09a24d26db11",
+                "reference": "0895c932405407fd3a7368b6910c09a24d26db11",
                 "shasum": ""
             },
             "require": {
+                "ext-json": "*",
                 "guzzlehttp/promises": "^1.0",
-                "guzzlehttp/psr7": "^1.4",
+                "guzzlehttp/psr7": "^1.6.1",
                 "php": ">=5.5"
             },
             "require-dev": {
                 "ext-curl": "*",
                 "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
-                "psr/log": "^1.0"
+                "psr/log": "^1.1"
             },
             "suggest": {
                 "psr/log": "Required for using the Log middleware"
@@ -147,12 +148,12 @@
                 }
             },
             "autoload": {
-                "files": [
-                    "src/functions_include.php"
-                ],
                 "psr-4": {
                     "GuzzleHttp\\": "src/"
-                }
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
@@ -176,7 +177,7 @@
                 "rest",
                 "web service"
             ],
-            "time": "2018-04-22T15:46:56+00:00"
+            "time": "2019-10-23T15:58:00+00:00"
         },
         {
             "name": "guzzlehttp/promises",
@@ -1754,16 +1755,16 @@
         },
         {
             "name": "symfony/console",
-            "version": "v4.3.4",
+            "version": "v4.3.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "de63799239b3881b8a08f8481b22348f77ed7b36"
+                "reference": "929ddf360d401b958f611d44e726094ab46a7369"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/de63799239b3881b8a08f8481b22348f77ed7b36",
-                "reference": "de63799239b3881b8a08f8481b22348f77ed7b36",
+                "url": "https://api.github.com/repos/symfony/console/zipball/929ddf360d401b958f611d44e726094ab46a7369",
+                "reference": "929ddf360d401b958f611d44e726094ab46a7369",
                 "shasum": ""
             },
             "require": {
@@ -1825,7 +1826,7 @@
             ],
             "description": "Symfony Console Component",
             "homepage": "https://symfony.com",
-            "time": "2019-08-26T08:26:39+00:00"
+            "time": "2019-10-07T12:36:49+00:00"
         },
         {
             "name": "symfony/polyfill-ctype",
@@ -2004,16 +2005,16 @@
         },
         {
             "name": "symfony/service-contracts",
-            "version": "v1.1.6",
+            "version": "v1.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/service-contracts.git",
-                "reference": "ea7263d6b6d5f798b56a45a5b8d686725f2719a3"
+                "reference": "ffcde9615dc5bb4825b9f6aed07716f1f57faae0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ea7263d6b6d5f798b56a45a5b8d686725f2719a3",
-                "reference": "ea7263d6b6d5f798b56a45a5b8d686725f2719a3",
+                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffcde9615dc5bb4825b9f6aed07716f1f57faae0",
+                "reference": "ffcde9615dc5bb4825b9f6aed07716f1f57faae0",
                 "shasum": ""
             },
             "require": {
@@ -2058,7 +2059,7 @@
                 "interoperability",
                 "standards"
             ],
-            "time": "2019-08-20T14:44:19+00:00"
+            "time": "2019-09-17T11:12:18+00:00"
         },
         {
             "name": "theseer/tokenizer",

BIN
docs/assets/instancepage.1.addcontent.png


BIN
docs/assets/instancepage.2.addinstance.png


BIN
docs/assets/instancepage.3.createtop.png


BIN
docs/assets/instancepage.4.createbottom.png


BIN
docs/assets/instancepage.embeddedjbrowse.png


BIN
docs/assets/instancepage.excludesetting.png


BIN
docs/assets/instancepage.linkoriginal.png


+ 18 - 23
docs/guide/instance_page.rst

@@ -1,38 +1,33 @@
-JBrowse Page Integration
+Tripal Page Integration
 ========================
 
-This guide will show you **how to create a page within your Tripal site for an existing JBrowse instance**. This ensures a *consistent user experience* by making the menu system of the Tripal site available to the user while browsing. If the user needs more space they can choose the *FullScreen option* to remove the menus.
+Embedded JBrowse Instance
+---------------------------
 
-.. warning::
-
-   This requires you already have a JBrowse instance. Both local (hosted on the same machine as your Tripal site) or external JBrowse instances are supported.
-
-.. note::
+This guide will show you **how to embed a JBrowse instance within your Tripal site for an existing JBrowse instance**. This ensures a *consistent user experience* by making the menu system of the Tripal site available to the user while browsing. If the user needs more space they can choose the *FullScreen option* to remove the menus.
 
-   The Tripal JBrowse Management sub-module provides a user interface to ease setup of multiple JBrowse instances. It is a great option but is not required.
-
-You create a JBrowse Instance page by navigating to **Content > Add Content > JBrowse Instance** on the Administration Toolbar. Then just fill out the form and click save!
-
-.. image:: ../assets/instancepage.1.addcontent.png
+.. warning::
 
-.. image:: ../assets/instancepage.2.addinstance.png
+   This requires you already have a JBrowse instance created through the Tripal JBrowse Management sub-module. For information on how to do this, see the associated Tripal JBrowse Management documentation.
 
-The **title** will become the title of the page and the **description** will be shown above the JBrowse instances. The description is a good place to add any warnings or instructions.
+This is the default functionality of the "Tripal-JBrowse Page Integration" module. If you have already created your JBrowse instances using the "Tripal Jbrowse Management" module then enabling this module automatically creates pages and menu items for those instances!
 
-.. image:: ../assets/instancepage.3.createtop.png
+.. image:: ../assets/instancepage.embeddedjbrowse.png
 
-The **Existing JBrowse URL** is the URL to the JBrowse instance you want to embed. You should be able to put this URL into your browser and access the JBrowse instance directly, even in the case of local instances.
+Links to Original JBrowse
+---------------------------
 
-The **Start Locations** allows you to specify where you want the JBrowse to navigate to for first time users. Keep in mind that JBrowse caches user location and thus all subsequent times a user accesses the instance it will start at their last browsed location.
+This guide will show you how to ensure the links created by this module direct users to the original JBrowse rather than to an embedded JBrowse page. This may be preferable if your theme does not provide much space to content thus causing the embedded JBrowse to be too small.
 
-.. image:: ../assets/instancepage.4.createbottom.png
+By installing the "Tripal-JBrowse Page Integration" module you will already have embedded JBrowse pages for all Tripal JBrowse instances created by the "Tripal JBrowse Management" module. To switch these links and pages to redirect to the original full-screen JBrowse, go to Administration Toolbar > Tripal > Extensions > Tripal JBrowse Management > Page Integration. **Here you simply need to uncheck the "Embed JBrowse in your site" checkbox and click "Save Settings"**
 
-The **Tracks** allows you to set which tracks you want shown by default. You should enter the machine name of the tracks here with multiple tracks separated by comma's.
+.. image:: ../assets/instancepage.linkoriginal.png
 
-.. note::
+Now, all links in the ``yourdrupalsite.com/jbrowse`` listing and menu items will point the original, full screen JBrowse!
 
-    Depending on how your Drupal site is configured, you may be presented with a **Preview** button instead of a **Save** button. In this case, simply click Preview and then on the next page, click Save.
+Exclude Specific JBrowse Instances
+-----------------------------------
 
-    The preview for the JBrowse may not load properly. Do not be concerned as this is not an indication that you have incorrectly configured the page.
+Sometimes you may want to manage a JBrowse Instance using "Tripal JBrowse Management" but not display it to users through "Tripal-JBrowse Page Integration". For example, if you have just begun development and are not ready to release it. To do this, go to Administration Toolbar > Tripal > Extensions > Tripal JBrowse Management > Page Integration. Check the checkbox beside the instance you would like to exclude from menu items, lists and pages and click "Save Settings".
 
-    To disable the preview button, navigate to Structure > Content Types > JBrowse Instance > edit and set the "Submission for settings" > "Preview before submitting" to "Disabled".
+.. image:: ../assets/instancepage.excludesetting.png

+ 0 - 132
tests/tripal_jbrowse/jbrowseInstanceNodeTest.php

@@ -1,132 +0,0 @@
-<?php
-namespace Tests\tripal_jbrowse;
-
-use StatonLab\TripalTestSuite\DBTransaction;
-use StatonLab\TripalTestSuite\TripalTestCase;
-use Faker\Factory;
-
-/**
- * Tests the JBrowse Instance Node Type.
- */
-class jbrowseInstanceNodeTest extends TripalTestCase {
-  // Uncomment to auto start and rollback db transactions per test method.
-  use DBTransaction;
-
-  /**
-   * JBrowse Instance Node Type exists.
-   *
-   * Check that the JBrowse Instance node type exists. It should be created
-   * when the module is installed by the Drupal Node API.
-   */
-  public function testJbrowseInstanceNodeTypeExists() {
-
-    // Get a list of all types available.
-    $types = node_type_get_types();
-
-    // The JBrowse Instance node type must be in the list.
-    $this->assertArrayHasKey('jbrowse_instance', $types, '"JBrowse Instance" node type is not registered with Drupal.');
-
-    // Check that the expected fields exist and are attached to the JBrowse instance node type.
-    // First retrieve all fields for this node type.
-    $fields = field_info_instances('node', 'jbrowse_instance');
-    // Now check that those important to us, exist.
-    $this->assertArrayHasKey('field_jburl', $fields,
-      'The "Existing JBrowse URL" field is not attached to the JBrowse Instance node type.');
-    $this->assertArrayHasKey('field_datadir', $fields,
-      'The "Data Directory" field is not attached to the JBrowse Instance node type.');
-    $this->assertArrayHasKey('field_jbloc', $fields,
-      'The "Start Location" field is not attached to the JBrowse Instance node type.');
-    $this->assertArrayHasKey('field_jbtracks', $fields,
-      'The "Tracks to Display" field is not attached to the JBrowse Instance node type.');
-  }
-
-  /**
-   * Test Creating a JBrowse Instance Node.
-   *
-   * Note: We can't test this by submitting the form via PUT because it requires
-   *  permission to access /node/add/jbrowse_instance; however, we don't yet have a
-   *  way to do this with TripalTestSuite. Furthermore, testing HTTP Requests
-   *  would not give us access to the data added via the test due to database
-   *  transactions.
-   */
-  public function testJBrowseInstanceNodeCreate() {
-    module_load_include('inc', 'node', 'node.pages');
-
-    // Log in the god user.
-    global $user;
-    $user = user_load(1);
-    $node = array('type' => 'jbrowse_instance');
-
-    // Fill in the form.
-    $faker = Factory::create();
-    $form_state = array('values' => array());
-    $form_state['values']['title'] = $faker->words(3, true);
-    $form_state['values']['field_jburl']['und'][0]['url'] = 'https://jbrowse.org/code/JBrowse-1.15.4/';
-    $form_state['values']['field_datadir']['und'][0] = 'sample_data/json/volvox';
-    $form_state['values']['field_jbloc']['und'][0] = 'ctgA:1..11000';
-    $form_state['values']['field_jbtracks']['und'][0] = 'DNA,Genes,volvox-sorted-vcf,volvox_microarray_bw_density,volvox_bb';
-    $form_state['values']['op'] = t('Save');
-
-    // Execute the node creation form.
-    drupal_form_submit('jbrowse_instance_node_form', $form_state, (object) $node);
-
-    // Retrieve any errors.
-    $errors = form_get_errors();
-
-    // Assert that there must not be any.
-    $this->assertEmpty($errors, 'Form submission returned the following errors:'.print_r($errors,TRUE));
-
-    // Check that there is a test jbrowse instance.
-    $result = db_query('SELECT * FROM {node} WHERE title=:name',
-      array(':name' => $form_state['values']['title']));
-    $this->assertEquals(1, $result->rowCount(), 'Unable to select the JBrowse Instance using the name.');
-
-    // log out the god user.
-    $user = drupal_anonymous_user();
-  }
-
-  /**
-   * Update an existing Blast Database Node.
-   */
-  public function testJBrowseInstanceNodeUpdate() {
-    module_load_include('inc', 'node', 'node.pages');
-
-    // Log in the god user.
-    global $user;
-    $user = user_load(1);
-
-    // Create the node in the first place.
-    $seeder = \Tests\DatabaseSeeders\JBrowseInstanceNodeSeeder::seed();
-    $node = $seeder->getNode();
-
-    // Now use the form to edit it :-)
-    // Specifically, we will change the name, url and data directory.
-    $faker = Factory::create();
-    $form_state = array('values' => array());
-    $form_state['values']['title'] = $faker->words(5, true);
-    $form_state['values']['field_jburl']['und'][0]['url'] = 'https://jbrowse.org/code/JBrowse-1.15.4/';
-    $form_state['values']['field_datadir']['und'][0] = 'sample_data/json/modencode';
-    $form_state['values']['op'] = t('Save');
-
-    // Execute the node creation form.
-    drupal_form_submit('jbrowse_instance_node_form', $form_state, $node);
-
-    // Retrieve any errors.
-    $errors = form_get_errors();
-    // Assert that there must not be any.
-    $this->assertEmpty($errors, 'Form submission returned the following errors:'.print_r($errors,TRUE));
-
-    // Check that there is a test jbrowse instance.
-    $result = db_query('SELECT * FROM {node} WHERE title=:name',
-      array(':name' => $form_state['values']['title']));
-    $this->assertEquals(1, $result->rowCount(), 'Unable to select the JBrowse Instance using the name.');
-
-    // log out the god user.
-    $user = drupal_anonymous_user();
-  }
-
-  /**
-   * Test deleting a node.
-   * NOTE: We cannot test this via drupal_form_submit() since it requires a confirmation.
-   */
-}

+ 33 - 0
tests/tripal_jbrowse_page/dotModuleTest.php

@@ -0,0 +1,33 @@
+<?php
+namespace Tests\tripal_jbrowse_page;
+
+use StatonLab\TripalTestSuite\DBTransaction;
+use StatonLab\TripalTestSuite\TripalTestCase;
+
+class dotModuleTest extends TripalTestCase {
+  // Uncomment to auto start and rollback db transactions per test method.
+  use DBTransaction;
+
+  /**
+   * Tests tripal_jbrowse_page_menu().
+   */
+  public function testHookMenu() {
+
+    $menu_items = tripal_jbrowse_page_menu();
+    foreach ($menu_items as $path => $item) {
+
+      // First ensure there are all the menu item keys required.
+      $this->assertArrayHasKey('title', $item);
+      $this->assertArrayHasKey('description', $item);
+      $this->assertArrayHasKey('page callback', $item);
+      $this->assertArrayHasKey('type', $item);
+      $this->assertArrayHasKey('access arguments', $item);
+
+      // Check that all jbrowse instance paths are the correct type.
+      $path_parts = explode('/', $path);
+      if (($path_parts[0] == 'jbrowse') && (sizeof($path_parts) > 1)) {
+        $this->assertEquals(MENU_SUGGESTED_ITEM, $item['type']);
+      }
+    }
+  }
+}

+ 2 - 3
tripal_jbrowse/tripal_jbrowse.info

@@ -1,7 +1,6 @@
-name = Tripal-JBrowse Integration
-description = Integrates existing JBrowse instances into Tripal sites.
+name = JBrowse Nodes DEPRECATED
+description = Creates nodes embedding JBrowse instances.
 core = 7.x
 package = Tripal Extensions
 
-dependencies[] = link
 dependencies[] = tripal

+ 86 - 0
tripal_jbrowse_page/includes/tripal_jbrowse_page.admin.inc

@@ -0,0 +1,86 @@
+<?php
+/**
+ * @file
+ * Administration (settings) for this module.
+ */
+
+/**
+ * Settings Form for page integration.
+ */
+function tripal_jbrowse_page_settings_form($form, $form_state) {
+
+  $form['general'] = [
+    '#type' => 'fieldset',
+    '#title' => 'General Page Integration Settings'
+  ];
+
+  $form['general']['embed'] = [
+    '#type' => 'checkbox',
+    '#title' => 'Embed JBrowse in your Tripal Site',
+    '#description' => 'Check this box if you would like to embed the JBrowse in your Tripal site. Conversely, uncheck it for links to go directly to the full-screen JBrowse.',
+  '#default_value' => variable_get('trpjbrowse_page_embed', 1),
+  ];
+
+  $form['exclude_fieldset'] = [
+    '#type' => 'fieldset',
+    '#title' => 'Exclude JBrowse Instances from Page Integration',
+    '#description' => 'To exclude a JBrowse instance from page integration (i.e. no embedded page will be shown and it won\'t show up in the listing), check the checkbox below.',
+  ];
+
+  // Get all instances as an option to be excluded.
+  $instances = tripal_jbrowse_mgmt_get_instances();
+  $options = [];
+  foreach ($instances as $instance) {
+    $key = $instance->id;
+    $options[$key] = [
+      'genus' => $instance->organism->genus,
+      'species' => $instance->organism->species,
+    ];
+  }
+  // Determine the default value.
+  $default_exclude = variable_get('trpjbrowse_page_exclude', []);
+  if (!is_array($default_exclude)) {
+    $default_exclude = unserialize($default_exclude);
+  }
+  // Now generate the table.
+  $form['exclude_fieldset']['exclude'] = [
+    '#type' => 'tableselect',
+    '#header' => [
+      'genus' => t('Genus'),
+      'species' => t('Species'),
+    ],
+    '#options' => $options,
+    '#empty' => t('No JBrowse Instances available. Please add one through the "List Instances" page.'),
+    '#default_value' => $default_exclude,
+  ];
+
+  $form['submit'] = [
+    '#type' => 'submit',
+    '#value' => 'Save Settings',
+  ];
+
+  return $form;
+}
+
+/**
+ * Settings Form for page integration.
+ */
+function tripal_jbrowse_page_settings_form_submit($form, $form_state) {
+  $values = $form_state['values'];
+
+  // General Settings.
+  variable_set(
+    'trpjbrowse_page_embed',
+    $values['embed']
+  );
+
+  // Exclude Instances.
+  variable_set(
+    'trpjbrowse_page_exclude',
+    serialize($values['exclude'])
+  );
+
+  // Ensure the menu is rebuilt.
+  variable_set('menu_rebuild_needed', TRUE);
+
+}

+ 53 - 0
tripal_jbrowse_page/includes/tripal_jbrowse_page.api.inc

@@ -0,0 +1,53 @@
+<?php
+/**
+ * @file
+ * Contains Application Programmer Interface (API) functions for this module.
+ */
+
+/**
+ * Retrieve the instance id based on the organism.
+ */
+function tripal_jbrowse_page_get_instance_id($conditions, $options) {
+
+  // First retrieve the organism_id.
+  $organism_id = chado_query('SELECT organism_id FROM {organism} WHERE genus=:genus AND species=:species',
+    [
+      ':genus' => $conditions['genus'],
+      ':species' => $conditions['species']
+    ])->fetchField();
+
+  // Then retrieve the instance for that organism.
+  if ($options['load_instance']) {
+    $instances = tripal_jbrowse_mgmt_get_instances(['organism_id' => $organism_id]);
+    return $instances[0];
+  }
+  else {
+    return db_select('tripal_jbrowse_mgmt_instances', 'I')
+      ->fields('I', ['id'])
+      ->condition('organism_id', $organism_id)
+      ->execute()->fetchField();
+  }
+
+}
+
+/**
+ * Retrieve the instance id based on the organism.
+ */
+function tripal_jbrowse_page_is_instance_public($instance_id) {
+  $excluded_instances = variable_get('trpjbrowse_page_exclude', []);
+  if (!is_array($excluded_instances)) {
+    $excluded_instances = unserialize($excluded_instances);
+  }
+
+  if (isset($excluded_instances[$instance_id])) {
+    if ($excluded_instances[$instance_id] === $instance_id) {
+      return FALSE;
+    }
+    else {
+      return TRUE;
+    }
+  }
+  else {
+    return TRUE;
+  }
+}

+ 30 - 0
tripal_jbrowse_page/includes/tripal_jbrowse_page.listing.inc

@@ -0,0 +1,30 @@
+<?php
+/**
+ * @file
+ * Builds the public jbrowse listing.
+ */
+
+/**
+ * Builds the public jbrowse listing.
+ */
+function tripal_jbrowse_page_listing_page() {
+  $settings = tripal_jbrowse_mgmt_get_settings();
+
+  // Retrieve all the instances...
+  $instances = tripal_jbrowse_mgmt_get_instances();
+
+  drupal_add_css(drupal_get_path('module', 'tripal_jbrowse_page') . '/theme/tripal_jbrowse_page.css');
+
+  // Add the URL for each to link to.
+  foreach($instances as $k => $instance) {
+    if (tripal_jbrowse_page_is_instance_public($instance->id)) {
+      $instances[$k]->url = url('jbrowse/'.$instance->organism->genus . '-' . $instance->organism->species, ['absolute' => TRUE]);
+    }
+    else {
+      unset($instances[$k]);
+    }
+  }
+
+  // Use the template to render the page.
+  return theme('jbrowse_instance_public_listing', ['instances' => $instances]);
+}

+ 36 - 0
tripal_jbrowse_page/includes/tripal_jbrowse_page.page.inc

@@ -0,0 +1,36 @@
+<?php
+/**
+ * @file
+ * Builds the Tripal JBrowse page.
+ */
+
+/**
+ * Redirect to the JBrowse Instance.
+ */
+function tripal_jbrowse_page_page($scientific_name) {
+  list($genus, $species) = explode('-', $scientific_name);
+  $instance = tripal_jbrowse_page_get_instance_id([
+    'genus' => $genus,
+    'species' => $species
+  ],
+  ['load_instance' => TRUE]);
+
+  // Determine Query paramters.
+  $query_params = tripal_jbrowse_mgmt_build_http_query($instance);
+  $page_q = drupal_get_query_parameters();
+  foreach ($page_q as $qkey => $qvalue) {
+    $query_params[$qkey] = $qvalue;
+  }
+
+  // Build the URL.
+  $settings = tripal_jbrowse_mgmt_get_settings();
+  $url = url($settings['link'],['query' => $query_params]);
+
+  if (variable_get('trpjbrowse_page_embed', 1)) {
+    drupal_add_css(drupal_get_path('module', 'tripal_jbrowse_page') . '/theme/tripal_jbrowse_page.css');
+    return theme('jbrowse_instance_embedded_page', ['url' => $url]);
+  }
+  else {
+    drupal_goto($url, array('external' => TRUE));
+  }
+}

+ 5 - 0
tripal_jbrowse_page/theme/jbrowse-instance--embedded.tpl.php

@@ -0,0 +1,5 @@
+
+<div id="JBrowseInstance">
+  <iframe src="<?php print $url;?>" width="100%" height="100%">
+  </iframe>
+</div>

+ 32 - 0
tripal_jbrowse_page/theme/jbrowse-instance--public-listing.tpl.php

@@ -0,0 +1,32 @@
+<p>The JBrowse genome browser allows you to visually explore genomes and their associated large-scale datasets. JBrowse is a widely used application that is fast, intuitive, and compatible with most web browsers.</p>
+
+<div class="jbrowse-list">
+
+  <?php foreach ($instances as $instance) { ?>
+
+    <div class="jbrowse-instance">
+      <h3><?php print l($instance->title, $instance->url); ?></h3>
+      <p><?php print $instance->description; ?></p>
+      <span class="jbrowse-launch-link"><?php print l('Launch JBrowse', $instance->url); ?></span>
+    </div>
+
+  <?php }
+    if (empty($instances)) {?>
+
+      <div class="empty-list">
+        <p>There are currently no available JBrowse instances.</p>
+      </div>
+  <?php } ?>
+</div>
+
+
+<div class="jbrowse-admin-message">
+  <?php
+    print tripal_set_message(
+      'You can create or register a JBrowse Instance at '
+      .l('Administration Toolbar > Tripal > Extensions > Tripal JBrowse Management', 'admin/tripal/extension/tripal_jbrowse/management'),
+      TRIPAL_INFO,
+      ['return_html' => TRUE]
+    );
+  ?>
+</div>

+ 23 - 0
tripal_jbrowse_page/theme/tripal_jbrowse_page.css

@@ -0,0 +1,23 @@
+
+/** JBrowse Listing **/
+.jbrowse-list {
+  border-top: 1px solid #d0d0d0;
+  margin-top: 20px;
+}
+.jbrowse-list .jbrowse-instance {
+  border-bottom: 1px solid #d0d0d0;
+  margin-bottom: 20px;
+  padding-bottom: 30px;
+}
+.jbrowse-list .jbrowse-instance .jbrowse-launch-link {
+  float: right;
+}
+.jbrowse-list .empty-list {
+  margin-top: 20px;
+  font-style: italic;
+}
+
+/** Embedded Page **/
+#JBrowseInstance iframe {
+  min-height: 700px;
+}

+ 7 - 0
tripal_jbrowse_page/tripal_jbrowse_page.info

@@ -0,0 +1,7 @@
+name = Tripal-JBrowse Page Integration
+description = Integrates JBrowse instances into Tripal Pages.
+core = 7.x
+package = Tripal Extensions
+
+dependencies[] = tripal
+dependencies[] = tripal_jbrowse_mgmt

+ 78 - 0
tripal_jbrowse_page/tripal_jbrowse_page.module

@@ -0,0 +1,78 @@
+<?php
+
+// API.
+require_once 'includes/tripal_jbrowse_page.api.inc';
+
+/**
+ * Implements hook_menu().
+ */
+function tripal_jbrowse_page_menu() {
+  $items = [];
+
+  // Listing Page.
+  $items['jbrowse'] = [
+    'title' => 'JBrowse',
+    'description' => 'A listing of available JBrowse instances.',
+    'page callback' => 'tripal_jbrowse_page_listing_page',
+    'access arguments' => ['access content'],
+    'file' => 'includes/tripal_jbrowse_page.listing.inc',
+    'type' => MENU_NORMAL_ITEM,
+  ];
+
+  // Pages for each JBrowse Instance.
+  $instances = tripal_jbrowse_mgmt_get_instances();
+  foreach ($instances as $instance) {
+
+    if (tripal_jbrowse_page_is_instance_public($instance->id)) {
+
+      // Create the menu item.
+      $path = 'jbrowse/'.$instance->organism->genus . '-' . $instance->organism->species;
+      $items[$path] = [
+        'title' => $instance->title,
+        'description' => $instance->description,
+        'page callback' => 'tripal_jbrowse_page_page',
+        'page arguments' => [1],
+        'access arguments' => ['access content'],
+        'file' => 'includes/tripal_jbrowse_page.page.inc',
+        'type' => MENU_SUGGESTED_ITEM,
+      ];
+    }
+  }
+
+  // Administration.
+  $admin_path = 'admin/tripal/extension/tripal_jbrowse/management';
+  $items[$admin_path.'/page-integration'] = [
+    'title' => 'Page Integration',
+    'description' => 'A listing of available JBrowse instances.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => ['tripal_jbrowse_page_settings_form'],
+    'access arguments' => ['access content'],
+    'file' => 'includes/tripal_jbrowse_page.admin.inc',
+    'type' => MENU_LOCAL_TASK,
+  ];
+
+  return $items;
+}
+
+/**
+ * Implements hook_theme().
+ */
+function tripal_jbrowse_page_theme($existing, $type, $theme, $path) {
+
+  $items = array(
+    'jbrowse_instance_public_listing' => array(
+      // Don't specify the path in the template name.
+      // Unless you have your template inside a directory within this module.
+      'template' =>  'theme/jbrowse-instance--public-listing',
+      'variables' => array('instances' => []),
+    ),
+    'jbrowse_instance_embedded_page' => array(
+      // Don't specify the path in the template name.
+      // Unless you have your template inside a directory within this module.
+      'template' =>  'theme/jbrowse-instance--embedded',
+      'variables' => array('url' => ''),
+    ),
+  );
+
+  return $items;
+}