mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Visualizations framework: PluginFramework class, serving static and template files from plugins
This commit is contained in:
@@ -60,7 +60,7 @@ shed_tool_data_table_conf.xml
|
||||
job_conf.xml
|
||||
data_manager_conf.xml
|
||||
shed_data_manager_conf.xml
|
||||
config/visualizations/*.xml
|
||||
config/*
|
||||
static/welcome.html.*
|
||||
static/welcome.html
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
Custom visualization plugins
|
||||
----------------------------
|
||||
|
||||
Visualizations can be added to your Galaxy instance by creating
|
||||
sub-directories, templates, and static files here.
|
||||
|
||||
Properly configured and written visualizations will be accessible to
|
||||
the user when they click the 'visualizations' icon for a dataset
|
||||
in their history panel.
|
||||
|
||||
The framework must be enabled in your 'universe_wsgi.ini' file by
|
||||
uncommenting (and having a valid path for) the
|
||||
'visualizations_plugin_directory' entry.
|
||||
|
||||
For more information, see http://wiki.galaxyproject.org/VisualizationsRegistry
|
||||
|
||||
|
||||
Sub-directory structure
|
||||
-----------------------
|
||||
|
||||
In general, sub-directories should follow the pattern:
|
||||
|
||||
my_visualization/
|
||||
config/
|
||||
my_visualization.xml
|
||||
static/
|
||||
... any static files the visualization needs (if any)
|
||||
templates/
|
||||
... any Mako templates the visualization needs
|
||||
|
||||
The XML config file for a visualization plugin can be validated on the command
|
||||
line using (from your plugin directory):
|
||||
|
||||
xmllint my_visualization/config/my_visualization.xml --valid --noout
|
||||
-2
@@ -1,5 +1,3 @@
|
||||
<!-- runnable on NIX with xmllint -->
|
||||
|
||||
<!-- each visualization must have a template (all other elements are optional) -->
|
||||
<!ELEMENT visualization (data_sources*,params*,template_root*,template,link_text*,render_location*)>
|
||||
<!-- visualization name (e.g. 'trackster', 'scatterplot', etc.) is required -->
|
||||
@@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE visualization SYSTEM "visualization.dtd">
|
||||
<visualization name="circster">
|
||||
<data_sources>
|
||||
<data_source>
|
||||
<model_class>HistoryDatasetAssociation</model_class>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
|
||||
<to_param param_attr="id">dataset_id</to_param>
|
||||
<to_param assign="hda">hda_ldda</to_param>
|
||||
</data_source>
|
||||
<data_source>
|
||||
<model_class>LibraryDatasetDatasetAssociation</model_class>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
|
||||
<to_param param_attr="id">dataset_id</to_param>
|
||||
<to_param assign="ldda">hda_ldda</to_param>
|
||||
</data_source>
|
||||
</data_sources>
|
||||
<params>
|
||||
<param type="visualization">id</param>
|
||||
<param type="hda_or_ldda">dataset_id</param>
|
||||
<param_modifier type="string" modifies="dataset_id">hda_ldda</param_modifier>
|
||||
<param type="dbkey">dbkey</param>
|
||||
</params>
|
||||
<template>circster.mako</template>
|
||||
<render_location>_top</render_location>
|
||||
</visualization>
|
||||
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE visualization SYSTEM "visualization.dtd">
|
||||
<visualization name="phyloviz">
|
||||
<data_sources>
|
||||
<data_source>
|
||||
<model_class>HistoryDatasetAssociation</model_class>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">data.Newick</test>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">data.Nexus</test>
|
||||
<to_param param_attr="id">dataset_id</to_param>
|
||||
</data_source>
|
||||
</data_sources>
|
||||
<params>
|
||||
<param type="dataset" var_name_in_template="hda" required="true">dataset_id</param>
|
||||
<param type="integer" default="0">tree_index</param>
|
||||
</params>
|
||||
<template>phyloviz.mako</template>
|
||||
<render_location>_top</render_location>
|
||||
</visualization>
|
||||
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE visualization SYSTEM "visualization.dtd">
|
||||
<visualization name="scatterplot">
|
||||
<data_sources>
|
||||
<data_source>
|
||||
<model_class>HistoryDatasetAssociation</model_class>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">tabular.Tabular</test>
|
||||
<to_param param_attr="id">dataset_id</to_param>
|
||||
</data_source>
|
||||
</data_sources>
|
||||
<params>
|
||||
<param type="dataset" var_name_in_template="hda" required="true">dataset_id</param>
|
||||
</params>
|
||||
<template>scatterplot.mako</template>
|
||||
</visualization>
|
||||
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE visualization SYSTEM "visualization.dtd">
|
||||
<visualization name="sweepster">
|
||||
<data_sources>
|
||||
<data_source>
|
||||
<model_class>HistoryDatasetAssociation</model_class>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
|
||||
<to_param param_attr="id">dataset_id</to_param>
|
||||
<to_param assign="hda">hda_ldda</to_param>
|
||||
</data_source>
|
||||
<data_source>
|
||||
<model_class>LibraryDatasetDatasetAssociation</model_class>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
|
||||
<to_param param_attr="id">dataset_id</to_param>
|
||||
<to_param assign="ldda">hda_ldda</to_param>
|
||||
</data_source>
|
||||
</data_sources>
|
||||
<params>
|
||||
<param type="visualization" var_name_in_template="viz">visualization</param>
|
||||
<param type="hda_or_ldda" var_name_in_template="dataset">dataset_id</param>
|
||||
<param_modifier type="string" modifies="dataset_id">hda_ldda</param_modifier>
|
||||
</params>
|
||||
<template>sweepster.mako</template>
|
||||
<render_location>_top</render_location>
|
||||
</visualization>
|
||||
@@ -1,29 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE visualization SYSTEM "visualization.dtd">
|
||||
<visualization name="trackster">
|
||||
<!--not tested yet -->
|
||||
<data_sources>
|
||||
<data_source>
|
||||
<model_class>HistoryDatasetAssociation</model_class>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
|
||||
<to_param param_attr="id">dataset_id</to_param>
|
||||
<to_param assign="hda">hda_ldda</to_param>
|
||||
<to_param param_attr="dbkey">dbkey</to_param>
|
||||
</data_source>
|
||||
<data_source>
|
||||
<model_class>LibraryDatasetDatasetAssociation</model_class>
|
||||
<test type="isinstance" test_attr="datatype" result_type="datatype">data.Data</test>
|
||||
<to_param param_attr="id">dataset_id</to_param>
|
||||
<to_param assign="ldda">hda_ldda</to_param>
|
||||
</data_source>
|
||||
</data_sources>
|
||||
<params>
|
||||
<param type="visualization">id</param>
|
||||
<param type="dataset">dataset_id</param>
|
||||
<param type="genome_region">genome_region</param>
|
||||
<param type="dbkey">dbkey</param>
|
||||
</params>
|
||||
<template_root>tracks</template_root>
|
||||
<template>browser.mako</template>
|
||||
<render_location>_top</render_location>
|
||||
</visualization>
|
||||
+2
-4
@@ -123,10 +123,8 @@ class UniverseApplication( object ):
|
||||
# Load genome indexer tool.
|
||||
load_genome_index_tools( self.toolbox )
|
||||
# visualizations registry: associates resources with visualizations, controls how to render
|
||||
self.visualizations_registry = None
|
||||
if self.config.visualizations_config_directory:
|
||||
self.visualizations_registry = VisualizationsRegistry( self.config.root,
|
||||
self.config.visualizations_config_directory )
|
||||
self.visualizations_registry = VisualizationsRegistry.from_config(
|
||||
self.config.visualizations_plugins_directory, self.config )
|
||||
# Load security policy.
|
||||
self.security_agent = self.model.security_agent
|
||||
self.host_security_agent = galaxy.security.HostAgent( model=self.security_agent.model, permitted_actions=self.security_agent.permitted_actions )
|
||||
|
||||
@@ -291,8 +291,10 @@ class Configuration( object ):
|
||||
self.fluent_log = string_as_bool( kwargs.get( 'fluent_log', False ) )
|
||||
self.fluent_host = kwargs.get( 'fluent_host', 'localhost' )
|
||||
self.fluent_port = int( kwargs.get( 'fluent_port', 24224 ) )
|
||||
# visualization registries config directory
|
||||
self.visualizations_config_directory = kwargs.get( 'visualizations_config_directory', None )
|
||||
# PLUGINS:
|
||||
self.plugin_frameworks = []
|
||||
# visualization framework
|
||||
self.visualizations_plugins_directory = kwargs.get( 'visualizations_plugins_directory', None )
|
||||
|
||||
@property
|
||||
def sentry_dsn_public( self ):
|
||||
|
||||
@@ -12,6 +12,8 @@ from galaxy import util
|
||||
import galaxy.model
|
||||
from galaxy.web import url_for
|
||||
|
||||
from galaxy.web.base import pluginframework
|
||||
|
||||
import logging
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
@@ -28,17 +30,16 @@ some confused vocabulary in docs, var names
|
||||
tests:
|
||||
anding, grouping, not
|
||||
has_dataprovider
|
||||
user is admin
|
||||
data_sources:
|
||||
lists of
|
||||
add description element to visualization.
|
||||
|
||||
TESTS to add:
|
||||
has dataprovider
|
||||
user is admin
|
||||
user_pref for ordering/ex/inclusion of particular visualizations
|
||||
"""
|
||||
|
||||
# ------------------------------------------------------------------- the registry
|
||||
class VisualizationsRegistry( object ):
|
||||
class VisualizationsRegistry( pluginframework.PluginFramework ):
|
||||
"""
|
||||
Main responsibilities are:
|
||||
- testing if an object has a visualization that can be applied to it
|
||||
@@ -47,6 +48,7 @@ class VisualizationsRegistry( object ):
|
||||
- validating and parsing params into resources (based on a context)
|
||||
used in the visualization template
|
||||
"""
|
||||
#: any built in visualizations that have their own render method in ctrls/visualization
|
||||
# these should be handled somewhat differently - and be passed onto their resp. methods in ctrl.visualization
|
||||
#TODO: change/remove if/when they can be updated to use this system
|
||||
BUILT_IN_VISUALIZATIONS = [
|
||||
@@ -55,57 +57,37 @@ class VisualizationsRegistry( object ):
|
||||
'sweepster',
|
||||
'phyloviz'
|
||||
]
|
||||
# where to search for visualiztion templates (relative to templates/webapps/galaxy)
|
||||
#: where to search for visualiztion templates (relative to templates/webapps/galaxy)
|
||||
# this can be overridden individually in the config entries
|
||||
TEMPLATE_ROOT = 'visualization'
|
||||
#: directories under plugin_directory that aren't plugins
|
||||
non_plugin_directories = [ 'bler' ]
|
||||
|
||||
def __str__( self ):
|
||||
listings_keys_str = ','.join( self.listings.keys() ) if self.listings else ''
|
||||
return 'VisualizationsRegistry(%s)' %( listings_keys_str )
|
||||
return 'VisualizationsRegistry(%s)' %( self.plugin_directory )
|
||||
|
||||
def __init__( self, galaxy_root, configuration_filepath ):
|
||||
# load the registry from the xml files located in configuration_filepath using the given parser
|
||||
configuration_filepath = os.path.join( galaxy_root, configuration_filepath )
|
||||
self.configuration_filepath = self.check_conf_filepath( configuration_filepath )
|
||||
self.move_sample_conf_files()
|
||||
self.load()
|
||||
def __init__( self, registry_filepath, template_cache_dir ):
|
||||
super( VisualizationsRegistry, self ).__init__( registry_filepath, template_cache_dir )
|
||||
|
||||
# what to use to parse query strings into resources/vars for the template
|
||||
self.resource_parser = ResourceParser()
|
||||
log.debug( '%s loaded', str( self ) )
|
||||
|
||||
def check_conf_filepath( self, configuration_filepath ):
|
||||
def load_configuration( self ):
|
||||
"""
|
||||
Checks for the existence of the given filepath.
|
||||
:param configurarion_filepath: full filepath to the visualization config directory
|
||||
:raises IOError: if the given directory doesn't exist
|
||||
"""
|
||||
if not os.path.exists( configuration_filepath ):
|
||||
raise IOError( 'visualization configuration directory (%s) not found' %( configuration_filepath ) )
|
||||
return configuration_filepath
|
||||
Builds the registry by parsing the `config/*.xml` files for every plugin
|
||||
in ``get_plugin_directories`` and stores the results in ``self.listings``.
|
||||
|
||||
def move_sample_conf_files( self ):
|
||||
..note::
|
||||
This could be used to re-load a new configuration without restarting
|
||||
the instance.
|
||||
"""
|
||||
Copies any `*.xml.sample` files in `configuration_filepath` to
|
||||
`.xml` files of the same names if no file with that name already exists.
|
||||
try:
|
||||
self.listings = VisualizationsConfigParser.parse( self.get_plugin_directories() )
|
||||
|
||||
:returns: a list of the files moved
|
||||
"""
|
||||
files_moved = []
|
||||
for sample_file in glob.glob( os.path.join( self.configuration_filepath, '*.sample' ) ):
|
||||
new_name = os.path.splitext( sample_file )[0]
|
||||
if not os.path.exists( new_name ):
|
||||
shutil.copy2( sample_file, new_name )
|
||||
files_moved.append( new_name )
|
||||
|
||||
def load( self ):
|
||||
"""
|
||||
Builds the registry by parsing the xml in `self.configuration_filepath`
|
||||
and stores the results in `self.listings`.
|
||||
|
||||
Provided as separate method from `__init__` in order to re-load a
|
||||
new configuration without restarting the instance.
|
||||
"""
|
||||
self.listings = VisualizationsConfigParser.parse( self.configuration_filepath )
|
||||
except Exception, exc:
|
||||
log.exception( 'Error parsing visualizations plugins %s', self.plugin_directory )
|
||||
raise
|
||||
|
||||
def get_visualization( self, trans, visualization_name, target_object ):
|
||||
"""
|
||||
@@ -283,11 +265,11 @@ class VisualizationsConfigParser( object ):
|
||||
VALID_RENDER_LOCATIONS = [ 'galaxy_main', '_top', '_blank' ]
|
||||
|
||||
@classmethod
|
||||
def parse( cls, config_dir, debug=True ):
|
||||
def parse( cls, plugin_directories, debug=False ):
|
||||
"""
|
||||
Static class interface.
|
||||
"""
|
||||
return cls( debug ).parse_files( config_dir )
|
||||
return cls( debug ).parse_plugins( plugin_directories )
|
||||
|
||||
def __init__( self, debug=False ):
|
||||
self.debug = debug
|
||||
@@ -297,33 +279,45 @@ class VisualizationsConfigParser( object ):
|
||||
self.param_parser = ParamParser()
|
||||
self.param_modifier_parser = ParamModifierParser()
|
||||
|
||||
def parse_files( self, config_dir ):
|
||||
def parse_plugins( self, plugin_directories ):
|
||||
"""
|
||||
Parse each XML file in `config_dir` for visualizations config data.
|
||||
Parses the config files for each plugin sub-dir in `base_path`.
|
||||
|
||||
:param plugin_directories: a list of paths to enabled plugins.
|
||||
|
||||
If an error occurs while parsing a visualizations entry, it is skipped.
|
||||
:returns: registry data in dictionary form
|
||||
"""
|
||||
returned = {}
|
||||
try:
|
||||
for xml_filepath in glob.glob( os.path.join( config_dir, '*.xml' ) ):
|
||||
try:
|
||||
visualization_name, visualization = self.parse_file( xml_filepath )
|
||||
# skip vis' with parsing errors - don't shutdown the startup
|
||||
except ParsingException, parse_exc:
|
||||
log.error( 'Skipped visualization config "%s" due to parsing errors: %s',
|
||||
xml_filepath, str( parse_exc ), exc_info=self.debug )
|
||||
for plugin_path in plugin_directories:
|
||||
returned.update( self.parse_plugin( plugin_path ) )
|
||||
return returned
|
||||
|
||||
if visualization:
|
||||
returned[ visualization_name ] = visualization
|
||||
log.debug( 'Visualization config loaded for: %s', visualization_name )
|
||||
def parse_plugin( self, plugin_path ):
|
||||
"""
|
||||
Parses any XML files in ``<plugin_path>/config``.
|
||||
|
||||
except Exception, exc:
|
||||
log.error( 'Error parsing visualizations configuration directory %s: %s',
|
||||
config_dir, str( exc ), exc_info=( not self.debug ) )
|
||||
#TODO: change when this framework is on by default
|
||||
if self.debug:
|
||||
raise
|
||||
If an error occurs while parsing a visualizations entry, it is skipped.
|
||||
:returns: registry data in dictionary form
|
||||
..note::
|
||||
assumes config files are in a 'config' sub-dir of each plugin
|
||||
"""
|
||||
returned = {}
|
||||
|
||||
plugin_config_path = os.path.join( plugin_path, 'config' )
|
||||
if not os.path.isdir( plugin_config_path ):
|
||||
return returned
|
||||
|
||||
for xml_filepath in glob.glob( os.path.join( plugin_config_path, '*.xml' ) ):
|
||||
try:
|
||||
visualization_name, visualization = self.parse_file( xml_filepath )
|
||||
# skip vis' with parsing errors - don't shutdown the startup
|
||||
except ParsingException, parse_exc:
|
||||
log.error( 'Skipped visualization config "%s" due to parsing errors: %s',
|
||||
xml_filepath, str( parse_exc ), exc_info=self.debug )
|
||||
|
||||
if visualization:
|
||||
returned[ visualization_name ] = visualization
|
||||
log.debug( 'Visualization config loaded for: %s', visualization_name )
|
||||
|
||||
return returned
|
||||
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
"""
|
||||
Base class for plugins - frameworks or systems that may:
|
||||
* serve static content
|
||||
* serve templated html
|
||||
* have some configuration at startup
|
||||
"""
|
||||
|
||||
import os.path
|
||||
import glob
|
||||
import sys
|
||||
|
||||
import pkg_resources
|
||||
pkg_resources.require( 'MarkupSafe' )
|
||||
pkg_resources.require( 'Mako' )
|
||||
import mako
|
||||
|
||||
|
||||
# ============================================================================= exceptions
|
||||
class PluginFrameworkException( Exception ):
|
||||
"""Base exception for plugin frameworks.
|
||||
"""
|
||||
pass
|
||||
class PluginFrameworkConfigException( PluginFrameworkException ):
|
||||
"""Exception for plugin framework configuration errors.
|
||||
"""
|
||||
pass
|
||||
class PluginFrameworkStaticException( PluginFrameworkException ):
|
||||
"""Exception for plugin framework static directory set up errors.
|
||||
"""
|
||||
pass
|
||||
class PluginFrameworkTemplateException( PluginFrameworkException ):
|
||||
"""Exception for plugin framework template directory
|
||||
and template rendering errors.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
# ============================================================================= base
|
||||
class PluginFramework( object ):
|
||||
"""
|
||||
Plugins are files/directories living outside the Galaxy ``lib`` directory
|
||||
that serve static files (css, js, images, etc.), use and serve mako templates,
|
||||
and have some configuration to control the rendering.
|
||||
|
||||
A plugin framework sets up all the above components.
|
||||
"""
|
||||
#: does the class need a config file(s) to be parsed?
|
||||
has_config = True
|
||||
#: does the class need static files served?
|
||||
serves_static = True
|
||||
#: does the class need template files served?
|
||||
serves_templates = True
|
||||
#TODO: allow plugin mako inheritance from existing ``/templates`` files
|
||||
#uses_galaxy_templates = True
|
||||
#TODO: possibly better as instance var (or a combo)
|
||||
#: the directories in ``plugin_directory`` with basenames listed here will
|
||||
#: be ignored for config, static, and templates
|
||||
non_plugin_directories = []
|
||||
|
||||
# ------------------------------------------------------------------------- setup
|
||||
@classmethod
|
||||
def from_config( cls, config_plugin_directory, config ):
|
||||
"""
|
||||
Set up the framework based on data from some config object by:
|
||||
* constructing it's absolute plugin_directory filepath
|
||||
* getting a template_cache
|
||||
* and appending itself to the config object's ``plugin_frameworks`` list
|
||||
|
||||
.. note::
|
||||
precondition: config obj should have attributes:
|
||||
root, template_cache, and (list) plugin_frameworks
|
||||
"""
|
||||
# currently called from (base) app.py - defined here to allow override if needed
|
||||
if not config_plugin_directory:
|
||||
return None
|
||||
try:
|
||||
full_plugin_filepath = os.path.join( config.root, config_plugin_directory )
|
||||
template_cache = config.template_cache if cls.serves_static else None
|
||||
plugin = cls( full_plugin_filepath, template_cache )
|
||||
|
||||
config.plugin_frameworks.append( plugin )
|
||||
return plugin
|
||||
|
||||
except PluginFrameworkException, plugin_exc:
|
||||
log.exception( "Error loading framework %s. Skipping...", cls.__class__.__name__ )
|
||||
return None
|
||||
|
||||
def __str__( self ):
|
||||
return '%s(%s)' %( self.__class__.__name__, self.plugin_directory )
|
||||
|
||||
def __init__( self, plugin_directory, template_cache_dir=None, debug=False ):
|
||||
if not os.path.isdir( plugin_directory ):
|
||||
raise PluginFrameworkException( 'Framework plugin directory not found: %s, %s'
|
||||
%( self.__class__.__name__, plugin_directory ) )
|
||||
# absolute (?) path
|
||||
self.plugin_directory = plugin_directory
|
||||
self.name = os.path.basename( self.plugin_directory )
|
||||
|
||||
if self.has_config:
|
||||
self.load_configuration()
|
||||
# set_up_static_urls will be called during the static middleware creation (if serves_static)
|
||||
if self.serves_templates:
|
||||
self.set_up_templates( template_cache_dir )
|
||||
|
||||
def get_plugin_directories( self ):
|
||||
"""
|
||||
Return the plugin directory paths for this plugin.
|
||||
|
||||
Gets any directories within ``plugin_directory`` that are directories
|
||||
themselves and whose ``basename`` is not in ``plugin_directory``.
|
||||
"""
|
||||
# could instead explicitly list on/off in master config file
|
||||
for plugin_path in glob.glob( os.path.join( self.plugin_directory, '*' ) ):
|
||||
if not os.path.isdir( plugin_path ):
|
||||
continue
|
||||
|
||||
if os.path.basename( plugin_path ) in self.non_plugin_directories:
|
||||
continue
|
||||
|
||||
yield plugin_path
|
||||
|
||||
# ------------------------------------------------------------------------- config
|
||||
def load_configuration( self ):
|
||||
"""
|
||||
Override to load some framework/plugin specifc configuration.
|
||||
"""
|
||||
# Abstract method
|
||||
return True
|
||||
|
||||
# ------------------------------------------------------------------------- serving static files
|
||||
def get_static_urls_and_paths( self ):
|
||||
"""
|
||||
For each plugin, return a 2-tuple where the first element is a url path
|
||||
to the plugin's static files and the second is a filesystem path to those
|
||||
same files.
|
||||
|
||||
Meant to be passed to a Static url map.
|
||||
"""
|
||||
url_and_paths = []
|
||||
# called during the static middleware creation (buildapp.py, wrap_in_static)
|
||||
|
||||
# NOTE: this only searches for static dirs two levels deep (i.e. <plugin_directory>/<plugin-name>/static)
|
||||
for plugin_path in self.get_plugin_directories():
|
||||
# that path is a plugin, search for subdirs named static in THAT dir
|
||||
plugin_static_path = os.path.join( plugin_path, 'static' )
|
||||
if not os.path.isdir( plugin_static_path ):
|
||||
continue
|
||||
|
||||
# build a url for that static subdir and create a Static urlmap entry for it
|
||||
plugin_name = os.path.splitext( os.path.basename( plugin_path ) )[0]
|
||||
plugin_url = self.name + '/' + plugin_name + '/static'
|
||||
url_and_paths.append( ( plugin_url, plugin_static_path ) )
|
||||
|
||||
return url_and_paths
|
||||
|
||||
# ------------------------------------------------------------------------- templates
|
||||
def set_up_templates( self, template_cache_dir ):
|
||||
"""
|
||||
Add a ``template_lookup`` attribute to the framework that can be passed
|
||||
to the mako renderer to find templates.
|
||||
"""
|
||||
if not template_cache_dir:
|
||||
raise PluginFrameworkTemplateException( 'Plugins that serve templates require a template_cache_dir' )
|
||||
self.template_lookup = self._create_mako_template_lookup( template_cache_dir, self._get_template_paths() )
|
||||
return self.template_lookup
|
||||
|
||||
def _get_template_paths( self ):
|
||||
"""
|
||||
Get the paths that will be searched for templates.
|
||||
"""
|
||||
return [ self.plugin_directory ]
|
||||
|
||||
def _create_mako_template_lookup( self, cache_dir, paths, collection_size=500, output_encoding='utf-8' ):
|
||||
"""
|
||||
Create a ``TemplateLookup`` with defaults.
|
||||
"""
|
||||
return mako.lookup.TemplateLookup(
|
||||
directories = paths,
|
||||
module_directory = cache_dir,
|
||||
collection_size = collection_size,
|
||||
output_encoding = output_encoding )
|
||||
|
||||
#TODO: do we want to remove trans and app from the plugin template context?
|
||||
def fill_template( self, trans, template_filename, **kwargs ):
|
||||
"""
|
||||
Pass control over to trans and render the ``template_filename``.
|
||||
"""
|
||||
# defined here to be overridden
|
||||
return trans.fill_template( template_filename, template_lookup=self.template_lookup, **kwargs )
|
||||
|
||||
def fill_template_with_plugin_imports( self, trans, template_filename, **kwargs ):
|
||||
"""
|
||||
Returns a rendered plugin template but allows importing modules from inside
|
||||
the plugin directory within the template.
|
||||
|
||||
..example:: I.e. given this layout for a plugin:
|
||||
bler/
|
||||
template/
|
||||
bler.mako
|
||||
static/
|
||||
conifg/
|
||||
my_script.py
|
||||
this version of `fill_template` allows `bler.mako` to call `import my_script`.
|
||||
"""
|
||||
try:
|
||||
plugin_base_path = os.path.split( os.path.dirname( template_filename ) )[0]
|
||||
plugin_path = os.path.join( self.plugin_directory, plugin_base_path )
|
||||
sys.path.append( plugin_path )
|
||||
filled_template = self.fill_template( trans, template_filename, **kwargs )
|
||||
|
||||
finally:
|
||||
sys.path.remove( plugin_path )
|
||||
|
||||
return filled_template
|
||||
|
||||
#TODO: could add plugin template helpers here
|
||||
@@ -993,10 +993,13 @@ class GalaxyWebTransaction( base.DefaultWebTransaction ):
|
||||
searchList=[kwargs, self.template_context, dict(caller=self, t=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app)] )
|
||||
return str( template )
|
||||
|
||||
def fill_template_mako( self, filename, **kwargs ):
|
||||
template = self.webapp.mako_template_lookup.get_template( filename )
|
||||
def fill_template_mako( self, filename, template_lookup=None, **kwargs ):
|
||||
template_lookup = template_lookup or self.webapp.mako_template_lookup
|
||||
template = template_lookup.get_template( filename )
|
||||
template.output_encoding = 'utf-8'
|
||||
data = dict( caller=self, t=self, trans=self, h=helpers, util=util, request=self.request, response=self.response, app=self.app )
|
||||
|
||||
data = dict( caller=self, t=self, trans=self, h=helpers, util=util,
|
||||
request=self.request, response=self.response, app=self.app )
|
||||
data.update( self.template_context )
|
||||
data.update( kwargs )
|
||||
return template.render( **data )
|
||||
|
||||
@@ -12,9 +12,11 @@ from paste.httpheaders import ETAG
|
||||
from paste.urlparser import StaticURLParser
|
||||
|
||||
class CacheableStaticURLParser( StaticURLParser ):
|
||||
|
||||
def __init__( self, directory, cache_seconds=None ):
|
||||
StaticURLParser.__init__( self, directory )
|
||||
self.cache_seconds = cache_seconds
|
||||
|
||||
def __call__( self, environ, start_response ):
|
||||
path_info = environ.get('PATH_INFO', '')
|
||||
if not path_info:
|
||||
|
||||
@@ -2,22 +2,26 @@
|
||||
Provides factory methods to assemble the Galaxy web application
|
||||
"""
|
||||
|
||||
import logging, atexit
|
||||
import os, os.path
|
||||
import sys, warnings
|
||||
|
||||
from galaxy.util import asbool
|
||||
import sys
|
||||
import os
|
||||
import os.path
|
||||
import atexit
|
||||
import warnings
|
||||
import glob
|
||||
|
||||
from paste import httpexceptions
|
||||
import pkg_resources
|
||||
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
from galaxy import util
|
||||
import galaxy.model
|
||||
import galaxy.model.mapping
|
||||
import galaxy.datatypes.registry
|
||||
import galaxy.web.framework
|
||||
from galaxy import util
|
||||
from galaxy.util import asbool
|
||||
|
||||
import logging
|
||||
log = logging.getLogger( __name__ )
|
||||
|
||||
|
||||
class GalaxyWebApplication( galaxy.web.framework.WebApplication ):
|
||||
pass
|
||||
@@ -181,7 +185,7 @@ def app_factory( global_conf, **kwargs ):
|
||||
if kwargs.get( 'middleware', True ):
|
||||
webapp = wrap_in_middleware( webapp, global_conf, **kwargs )
|
||||
if asbool( kwargs.get( 'static_enabled', True ) ):
|
||||
webapp = wrap_in_static( webapp, global_conf, **kwargs )
|
||||
webapp = wrap_in_static( webapp, global_conf, plugin_frameworks=app.config.plugin_frameworks, **kwargs )
|
||||
if asbool(kwargs.get('pack_scripts', False)):
|
||||
pack_scripts()
|
||||
# Close any pooled database connections before forking
|
||||
@@ -323,7 +327,7 @@ def wrap_in_middleware( app, global_conf, **local_conf ):
|
||||
log.debug( "Enabling 'Request ID' middleware" )
|
||||
return app
|
||||
|
||||
def wrap_in_static( app, global_conf, **local_conf ):
|
||||
def wrap_in_static( app, global_conf, plugin_frameworks=None, **local_conf ):
|
||||
from paste.urlmap import URLMap
|
||||
from galaxy.web.framework.middleware.static import CacheableStaticURLParser as Static
|
||||
urlmap = URLMap()
|
||||
@@ -343,6 +347,16 @@ def wrap_in_static( app, global_conf, **local_conf ):
|
||||
urlmap["/static/style"] = Static( conf.get( "static_style_dir" ), cache_time )
|
||||
urlmap["/favicon.ico"] = Static( conf.get( "static_favicon_dir" ), cache_time )
|
||||
urlmap["/robots.txt"] = Static( conf.get( "static_robots_txt", 'static/robots.txt'), cache_time )
|
||||
|
||||
# wrap any static dirs for plugins
|
||||
plugin_frameworks = plugin_frameworks or []
|
||||
for static_serving_framework in ( framework for framework in plugin_frameworks if framework.serves_static ):
|
||||
# invert control to each plugin for finding their own static dirs
|
||||
for plugin_url, plugin_static_path in static_serving_framework.get_static_urls_and_paths():
|
||||
plugin_url = '/plugins/' + plugin_url
|
||||
urlmap[( plugin_url )] = Static( plugin_static_path, cache_time )
|
||||
log.debug( 'added url, path to static middleware: %s, %s', plugin_url, plugin_static_path )
|
||||
|
||||
# URL mapper becomes the root webapp
|
||||
return urlmap
|
||||
|
||||
|
||||
@@ -708,7 +708,7 @@ class VisualizationController( BaseUIController, SharableMixin, UsesAnnotations,
|
||||
# validate name vs. registry
|
||||
registry = trans.app.visualizations_registry
|
||||
if not registry:
|
||||
raise HTTPNotFound( 'No visualization registry (possibly disabled in universe_wsgi.ini)')
|
||||
raise HTTPNotFound( 'No visualization registry (possibly disabled in universe_wsgi.ini)' )
|
||||
if visualization_name not in registry.listings:
|
||||
raise HTTPNotFound( 'Unknown or invalid visualization: ' + visualization_name )
|
||||
# or redirect to list?
|
||||
@@ -722,16 +722,15 @@ class VisualizationController( BaseUIController, SharableMixin, UsesAnnotations,
|
||||
resources = registry.query_dict_to_resources( trans, self, visualization_name, kwargs )
|
||||
|
||||
# look up template and render
|
||||
template_root = registry_listing.get( 'template_root', registry.TEMPLATE_ROOT )
|
||||
template = registry_listing[ 'template' ]
|
||||
template_path = os.path.join( template_root, template )
|
||||
template_path = registry_listing[ 'template' ]
|
||||
returned = registry.fill_template( trans, template_path,
|
||||
visualization_name=visualization_name, query_args=kwargs,
|
||||
embedded=embedded, shared_vars={}, **resources )
|
||||
#NOTE: passing *unparsed* kwargs as query_args
|
||||
#NOTE: shared_vars is a dictionary for shared data in the template
|
||||
# this feels hacky to me but it's what mako recommends:
|
||||
# http://docs.makotemplates.org/en/latest/runtime.html
|
||||
#TODO: embedded
|
||||
returned = trans.fill_template( template_path, visualization_name=visualization_name,
|
||||
embedded=embedded, query_args=kwargs, shared_vars={}, **resources )
|
||||
|
||||
except Exception, exception:
|
||||
log.exception( 'error rendering visualization (%s): %s', visualization_name, str( exception ) )
|
||||
|
||||
@@ -616,7 +616,7 @@ function create_scatterplot_action_fn( url, params ){
|
||||
title : "Scatterplot",
|
||||
type : "url",
|
||||
content : url + '/scatterplot?' + $.param(params),
|
||||
center : true
|
||||
location : 'center'
|
||||
});
|
||||
|
||||
//TODO: this needs to go away
|
||||
@@ -699,4 +699,4 @@ function create_trackster_action_fn(vis_url, dataset_params, dbkey) {
|
||||
//==============================================================================
|
||||
//return {
|
||||
// HDAView : HDAView,
|
||||
//};});
|
||||
//};});
|
||||
|
||||
@@ -174,10 +174,8 @@ paste.app_factory = galaxy.web.buildapp:app_factory
|
||||
# Galaxy.
|
||||
#datatypes_config_file = datatypes_conf.xml
|
||||
|
||||
# Visualizations config directory, where to look for individual visualization
|
||||
# xml configuration files. Those files define how visualizations apply to
|
||||
# particular data and how to pass them the necessary parameters
|
||||
#visualizations_config_directory = config/visualizations
|
||||
# Visualizations config directory: where to look for individual visualization plugins.
|
||||
#visualizations_plugins_directory = config/plugins/visualizations
|
||||
|
||||
# Each job is given a unique empty directory as its current working directory.
|
||||
# This option defines in what parent directory those directories will be
|
||||
|
||||
Reference in New Issue
Block a user