galaxy-commits
Threads by month
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
July 2010
- 1 participants
- 87 discussions
galaxy-dist commit e5c40bd9c179: Set a default (database/universe.sqlite) in manage_db.py in case the deprecated database_file is unset
by commits-noreply@bitbucket.org 16 Jul '10
by commits-noreply@bitbucket.org 16 Jul '10
16 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1278099445 14400
# Node ID e5c40bd9c179059344645a9171b7172d74c64a25
# Parent 660a65d7c9294284faaf6bc7b7c3a52d92d8c8d4
Set a default (database/universe.sqlite) in manage_db.py in case the deprecated database_file is unset
--- a/scripts/manage_db.py
+++ b/scripts/manage_db.py
@@ -23,7 +23,7 @@ if cp.has_option( "app:main", "database_
elif cp.has_option( "app:main", "database_file" ):
db_url = "sqlite:///%s?isolation_level=IMMEDIATE" % cp.get( "app:main", "database_file" )
else:
- db_url = None
+ db_url = "sqlite:///./database/universe.sqlite?isolation_level=IMMEDIATE"
dialect_to_egg = {
"sqlite" : "pysqlite>=2",
1
0
galaxy-dist commit 660a65d7c929: Add a few missing options to the sample config.
by commits-noreply@bitbucket.org 16 Jul '10
by commits-noreply@bitbucket.org 16 Jul '10
16 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1278099417 14400
# Node ID 660a65d7c9294284faaf6bc7b7c3a52d92d8c8d4
# Parent b841989e2662b204fcb4c73931ab3a2b08ef35a0
Add a few missing options to the sample config.
--- a/universe_wsgi.ini.sample
+++ b/universe_wsgi.ini.sample
@@ -75,6 +75,12 @@ paste.app_factory = galaxy.web.buildapp:
# [filter:proxy-prefix] section above.
#filter-with = proxy-prefix
+# If proxy-prefix is enabled and you're running more than one Galaxy instance
+# behind one hostname, you will want to set this to the same path as the prefix
+# in the filter above. This value becomes the "path" attribute set in the
+# cookie so the cookies from each instance will not clobber each other.
+#cookie_path = None
+
# -- Database
# By default, Galaxy uses a SQLite database at 'database/universe.sqlite'. You
@@ -93,6 +99,15 @@ paste.app_factory = galaxy.web.buildapp:
# you will want to set this to some positive value (7200 should work).
#database_engine_option_pool_recycle = -1
+# If large database query results are causing memory or response time issues in
+# the Galaxy process, leave the result on the server instead. This option is
+# only available for PostgreSQL and is highly recommended.
+#database_engine_option_server_side_cursors = False
+
+# Create only one connection to the database per thread, to reduce the
+# connection overhead. Recommended when not using SQLite:
+#database_engine_option_strategy = threadlocal
+
# -- Files and directories
# Dataset files are stored in this directory.
1
0
galaxy-dist commit fad6bc5478fa: Add new state colors to blue_colors.ini so that it is synced with the styles, and clean up and add comments to make_style.py.
by commits-noreply@bitbucket.org 16 Jul '10
by commits-noreply@bitbucket.org 16 Jul '10
16 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Greg Von Kuster <greg(a)bx.psu.edu>
# Date 1278087396 14400
# Node ID fad6bc5478fa9a4ac4e9d368bcfae1651528deac
# Parent 8f2e44ed27f6a17586854c8e423d2b7b42ca0ae1
Add new state colors to blue_colors.ini so that it is synced with the styles, and clean up and add comments to make_style.py.
--- a/static/june_2007_style/blue/base.css
+++ b/static/june_2007_style/blue/base.css
@@ -73,8 +73,8 @@ span.toolParameterExpandableCollapsable{
ul.toolParameterExpandableCollapsable{list-style:none;}
ul.manage-table-actions{float:right;margin-top:-2.5em;}
ul.manage-table-actions li{display:block;float:left;margin-left:0.5em;}
-.state-color-new{border-color:#A86030;background:#FFB030;}
-.state-color-upload{border-color:#990099;background:#D090D0;}
+.state-color-new{border-color:#A86030;background:FFB030;}
+.state-color-upload{border-color:#6666AA;background:#CCCCFF;}
.state-color-waiting{border-color:#A86030;background:#E8C060;}
.state-color-queued{border-color:#888888;background:#EEEEEE;}
.state-color-running{border-color:#AAAA66;background:#FFFFCC;}
--- a/static/june_2007_style/blue_colors.ini
+++ b/static/june_2007_style/blue_colors.ini
@@ -33,7 +33,15 @@ table_border=#d8b365
footer_bg=#023858
footer_title_bg=#023858
footer_title_hatch=#000000
-# History
+# History - these are actually Job states (used in the reports webapp), but the same styles are used for Tool states in the community webapp
+history_new_border=#A86030
+history_new_bg=FFB030
+history_upload_border=#990099
+history_upload_bg=#D090D0
+history_waiting_border=#A86030
+history_waiting_bg=#E8C060
+history_deleted_border=#330066
+history_deleted_bg=#3399FF
history_error_border=#AA6666
history_error_bg=#FFCCCC
history_running_border=#AAAA66
--- a/static/june_2007_style/process_css.py
+++ b/static/june_2007_style/process_css.py
@@ -9,9 +9,18 @@ CSS processor for Galaxy style sheets. S
"""
-import sys, string, os.path
+import sys, string, os.path, os
+
+new_path = [ os.path.join( os.getcwd(), '..', '..', "lib" ) ]
+new_path.extend( sys.path[1:] ) # remove scripts/ from the path
+sys.path = new_path
+
+from galaxy import eggs
+import pkg_resources
+from galaxy.util.odict import odict
+
from pyparsing import *
-from odict import odict
+#from odict import odict
try:
import Image
--- a/static/june_2007_style/make_style.py
+++ b/static/june_2007_style/make_style.py
@@ -1,13 +1,6 @@
#!/usr/bin/env python
-#from galaxy import eggs
-#import pkg_resources
-#pkg_resources.require("Cheetah")
-
import sys, string, os.path, tempfile, subprocess
-#from galaxy import eggs
-# import pkg_resources
-# pkg_resources.require( "Cheetah" )
# from Cheetah.Template import Template
from subprocess import Popen, PIPE
@@ -15,7 +8,12 @@ from subprocess import Popen, PIPE
assert sys.version_info[:2] >= ( 2, 4 )
# To create a new style ( this is an example ):
-# python make_style.py blue_colors.ini blue
+# In case you have not yet installed required packages:
+# % sudo easy_install pyparsing
+# % sudo easy_install http://effbot.org/downloads/Imaging-1.1.7.tar.gz
+# When you have the above installed, add whatever new style you want to /static/june_2007_style/blue_colors.ini and then:
+# % cd ~/static/june_2007_style/
+# % python make_style.py blue_colors.ini blue
def run( cmd ):
return Popen( cmd, stdout=PIPE).communicate()[0]
@@ -29,7 +27,8 @@ templates = [ ( "base.css.tmpl", "base.c
( "iphone.css.tmpl", "iphone.css" ),
( "reset.css.tmpl", "reset.css" ),
( "autocomplete_tagging.css.tmpl", "autocomplete_tagging.css") ]
-
+
+# TODO: Are these images still being used? If not, clean this code up!
images = [
( "./gradient.py 9 30 $panel_header_bg_top - $panel_header_bg_bottom 0 0 $panel_header_bg_bottom 1 1", "panel_header_bg.png" ),
( "./gradient.py 9 30 $panel_header_bg_bottom - $panel_header_bg_top 0 0 $panel_header_bg_top 1 1", "panel_header_bg_pressed.png" ),
@@ -49,6 +48,7 @@ images = [
( "./circle.py 12 #FFFFFF #D8B365 none> workflow_circle_drag.png" ),
]
+# TODO: Are these shared_images still being used? If not, clean this code up!
shared_images = [
# Dialog boxes
( "ok_large.png", "done_message_bg", "done_message_icon.png" ),
1
0
galaxy-dist commit 8f2e44ed27f6: Community webapp features and fixes
by commits-noreply@bitbucket.org 16 Jul '10
by commits-noreply@bitbucket.org 16 Jul '10
16 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Greg Von Kuster <greg(a)bx.psu.edu>
# Date 1278081407 14400
# Node ID 8f2e44ed27f6a17586854c8e423d2b7b42ca0ae1
# Parent 692458d15b9211ba7e6569e07b350ef2c3c9757a
Community webapp features and fixes
Fixes:
1. Can no longer upload a tool with a different tool_id than the one being replaced when uploading a new version of an existing tool
2. Fixed a bug in the base Admin controller that threw an exception rather than displaying a user's tool when clicking in grid links
3. Fixed bugs in the ToolListGrid class of the common controller that resulted in incorrect query filters being applied
4. Fixed a bug in the ToolListGrid initial query that did not return rows if tools were not associated with categories
New features:
0. Added stringent security to the templates and methods for performing actions
1. When an admin rejects a tool, they are required to add a reason for the rejection.  The user that uploaded the tool can then make a correction and re-submit it for approval.
2. A new tool menu option is available for viewing the history of a tool.
3. Significantly improved GUI flow
4. An admin is now allowed to purge a tool, which removes records from the database, so should be used only when absolutely necessary.
5. Made the User Description field required when the tool is submitted for approval.
6. Tools can now be downloaded from the admin view as well as the tool view.
--- a/lib/galaxy/webapps/community/security/__init__.py
+++ b/lib/galaxy/webapps/community/security/__init__.py
@@ -6,6 +6,7 @@ from datetime import datetime, timedelta
from galaxy.util.bunch import Bunch
from galaxy.util import listify
from galaxy.model.orm import *
+from galaxy.webapps.community.controllers.common import get_versions
log = logging.getLogger(__name__)
@@ -163,16 +164,83 @@ class CommunityRBACAgent( RBACAgent ):
self.sa_session.refresh( tool )
for category in categories:
self.associate_components( tool=tool, category=category )
- def can_edit_item( self, user, item ):
- # We currently assume the current user can edit the item if they are the owner (i.e., they
- # uploaded the item), and the item is in a NEW state.
- return user and user==item.user and item.is_new()
- def can_upload_new_version( self, user, item, versions ):
+ def can_approve_or_reject( self, user, user_is_admin, cntrller, item ):
+ # The current user can approve or reject the item if the user
+ # is an admin, and the item's state is WAITING.
+ return user and user_is_admin and cntrller=='admin' and item.is_waiting()
+ def can_delete( self, user, user_is_admin, cntrller, item ):
+ # The current user can delete the item if they are an admin or if they uploaded the
+ # item and in either case the item's state is not DELETED.
+ if user and user_is_admin and cntrller == 'admin':
+ can_delete = not item.is_deleted()
+ elif cntrller in [ 'tool' ]:
+ can_delete = user==item.user and not item.is_deleted()
+ else:
+ can_delete = False
+ return can_delete
+ def can_download( self, user, user_is_admin, cntrller, item ):
+ # The current user can download the item if they are an admin or if the
+ # item's state is not one of: NEW, WAITING.
+ if user and user_is_admin and cntrller == 'admin':
+ return True
+ elif cntrller in [ 'tool' ]:
+ can_download = not( item.is_new() or item.is_waiting() )
+ else:
+ can_download = False
+ return can_download
+ def can_edit( self, user, user_is_admin, cntrller, item ):
+ # The current user can edit the item if they are an admin or if they uploaded the item
+ # and the item's state is one of: NEW, REJECTED.
+ if user and user_is_admin and cntrller == 'admin':
+ return True
+ if cntrller in [ 'tool' ]:
+ return user and user==item.user and ( item.is_new() or item.is_rejected() )
+ return False
+ def can_purge( self, user, user_is_admin, cntrller ):
+ # The current user can purge the item if they are an admin.
+ return user and user_is_admin and cntrller == 'admin'
+ def can_upload_new_version( self, user, item ):
+ # The current user can upload a new version as long as the item's state is not NEW or WAITING.
+ if not user:
+ return False
+ versions = get_versions( item )
state_ok = True
for version in versions:
if version.is_new() or version.is_waiting():
state_ok = False
- return user and user==item.user and state_ok
+ break
+ return state_ok
+ def can_view( self, user, user_is_admin, cntrller, item ):
+ # The current user can view the item if they are an admin or if they uploaded the item
+ # or if the item's state is APPROVED.
+ if user and user_is_admin and cntrller == 'admin':
+ return True
+ if cntrller in [ 'tool' ] and item.is_approved():
+ return True
+ return user and user==item.user
+ def get_all_action_permissions( self, user, user_is_admin, cntrller, item ):
+ """Get all permitted actions on item for the current user"""
+ can_edit = self.can_edit( cntrller, user, user_is_admin, item )
+ can_view = self.can_view( cntrller, user, user_is_admin, item )
+ can_upload_new_version = self.can_upload_new_version( user, item )
+ visible_versions = self.get_visible_versions( user, user_is_admin, cntrller, item )
+ can_approve_or_reject = self.can_approve_or_reject( user, user_is_admin, cntrller, item )
+ can_delete = self.can_delete( user, user_is_admin, cntrller, item )
+ return can_edit, can_view, can_upload_new_version, can_delete, visible_versions, can_approve_or_reject
+ def get_visible_versions( self, user, user_is_admin, cntrller, item ):
+ # All previous versions of item can be displayed if the current user is an admin
+ # or they uploaded item. Otherwise, only versions whose state is APPROVED or
+ # ARCHIVED will be displayed.
+ if user and user_is_admin and cntrller == 'admin':
+ visible_versions = get_versions( item )
+ elif cntrller in [ 'tool' ]:
+ visible_versions = []
+ for version in get_versions( item ):
+ if version.is_approved() or version.is_archived() or version.user == user:
+ visible_versions.append( version )
+ else:
+ visible_versions = []
+ return visible_versions
def get_permitted_actions( filter=None ):
'''Utility method to return a subset of RBACAgent's permitted actions'''
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -1251,7 +1251,9 @@ class Admin( object ):
# sort filter instead of this class's.
kwargs[ 'user_id' ] = kwargs[ 'id' ]
kwargs[ 'sort' ] = 'name'
- return self.browse_tools( trans, **kwargs )
+ return trans.response.send_redirect( web.url_for( controller='admin',
+ action='browse_tools',
+ **kwargs ) )
# Render the list view
return self.user_list_grid( trans, **kwargs )
@web.expose
--- a/lib/galaxy/webapps/community/controllers/common.py
+++ b/lib/galaxy/webapps/community/controllers/common.py
@@ -22,14 +22,15 @@ class ToolListGrid( grids.Grid ):
return tool.description
class CategoryColumn( grids.TextColumn ):
def get_value( self, trans, grid, tool ):
+ rval = '<ul>'
if tool.categories:
- rval = '<ul>'
for tca in tool.categories:
rval += '<li><a href="browse_tools?operation=tools_by_category&id=%s&webapp=community">%s</a></li>' \
% ( trans.security.encode_id( tca.category.id ), tca.category.name )
- rval += '</ul>'
- return rval
- return 'not set'
+ else:
+ rval += '<li>not set</li>'
+ rval += '</ul>'
+ return rval
class ToolCategoryColumn( grids.GridColumn ):
def filter( self, trans, user, query, column_filter ):
"""Modify query to filter by category."""
@@ -55,9 +56,9 @@ class ToolListGrid( grids.Grid ):
columns = [
NameColumn( "Name",
key="name",
- link=( lambda item: dict( operation="View Tool", id=item.id, webapp="community" ) ),
+ link=( lambda item: dict( operation="view_tool", id=item.id, webapp="community" ) ),
model_class=model.Tool,
- attach_popup=True
+ attach_popup=False
),
VersionColumn( "Version",
key="version",
@@ -93,12 +94,7 @@ class ToolListGrid( grids.Grid ):
key="free-text-search",
visible=False,
filterable="standard" ) )
- operations = [
- grids.GridOperation( "Download tool",
- condition=( lambda item: not item.deleted ),
- allow_multiple=False,
- url_args=dict( controller="tool", action="download_tool", cntrller="tool", webapp="community" ) )
- ]
+ operations = []
standard_filters = []
default_filter = {}
num_rows_per_page = 50
@@ -108,8 +104,8 @@ class ToolListGrid( grids.Grid ):
return trans.sa_session.query( self.model_class ) \
.join( model.ToolEventAssociation.table ) \
.join( model.Event.table ) \
- .join( model.ToolCategoryAssociation.table ) \
- .join( model.Category.table )
+ .outerjoin( model.ToolCategoryAssociation.table ) \
+ .outerjoin( model.Category.table )
class CategoryListGrid( grids.Grid ):
class NameColumn( grids.TextColumn ):
@@ -171,6 +167,12 @@ class CommonController( BaseController )
message='Select a tool to edit',
status='error' ) )
tool = get_tool( trans, id )
+ can_edit = trans.app.security_agent.can_edit( trans.user, trans.user_is_admin(), cntrller, tool )
+ if not can_edit:
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action='browse_tools',
+ message='You are not allowed to edit this tool',
+ status='error' ) )
if params.get( 'edit_tool_button', False ):
if params.get( 'in_categories', False ):
in_categories = [ trans.sa_session.query( trans.app.model.Category ).get( x ) for x in util.listify( params.in_categories ) ]
@@ -185,7 +187,7 @@ class CommonController( BaseController )
tool.user_description = ''
trans.sa_session.add( tool )
trans.sa_session.flush()
- message="Tool '%s' description and category associations have been saved" % tool.name
+ message = "Tool '%s' description and category associations have been saved" % tool.name
return trans.response.send_redirect( web.url_for( controller='common',
action='edit_tool',
cntrller=cntrller,
@@ -193,31 +195,33 @@ class CommonController( BaseController )
message=message,
status='done' ) )
elif params.get( 'approval_button', False ):
- if params.get( 'in_categories', False ):
- in_categories = [ trans.sa_session.query( trans.app.model.Category ).get( x ) for x in util.listify( params.in_categories ) ]
- trans.app.security_agent.set_entity_category_associations( tools=[ tool ], categories=in_categories )
- else:
- # There must not be any categories associated with the tool
- trans.app.security_agent.set_entity_category_associations( tools=[ tool ], categories=[] )
user_description = util.restore_text( params.get( 'user_description', '' ) )
if user_description:
tool.user_description = user_description
+ if params.get( 'in_categories', False ):
+ in_categories = [ trans.sa_session.query( trans.app.model.Category ).get( x ) for x in util.listify( params.in_categories ) ]
+ trans.app.security_agent.set_entity_category_associations( tools=[ tool ], categories=in_categories )
+ else:
+ # There must not be any categories associated with the tool
+ trans.app.security_agent.set_entity_category_associations( tools=[ tool ], categories=[] )
+ trans.sa_session.add( tool )
+ trans.sa_session.flush()
+ # Move the state from NEW to WAITING
+ event = trans.app.model.Event( state=trans.app.model.Tool.states.WAITING )
+ tea = trans.app.model.ToolEventAssociation( tool, event )
+ trans.sa_session.add_all( ( event, tea ) )
+ trans.sa_session.flush()
+ message = "Tool '%s' has been submitted for approval and can no longer be modified" % ( tool.name )
+ return trans.response.send_redirect( web.url_for( controller='common',
+ action='view_tool',
+ cntrller=cntrller,
+ id=id,
+ message=message,
+ status='done' ) )
else:
- tool.user_description = ''
- trans.sa_session.add( tool )
- trans.sa_session.flush()
- # Move the state from NEW to WAITING
- event = trans.app.model.Event( state=trans.app.model.Tool.states.WAITING )
- tea = trans.app.model.ToolEventAssociation( tool, event )
- trans.sa_session.add_all( ( event, tea ) )
- trans.sa_session.flush()
- message = "Tool '%s' has been submitted for approval and can no longer be modified" % ( tool.name )
- return trans.response.send_redirect( web.url_for( controller='common',
- action='view_tool',
- cntrller=cntrller,
- id=id,
- message=message,
- status='done' ) )
+ # The user_description field is required when submitting for approval
+ message = 'A user description is required prior to approval.'
+ status = 'error'
in_categories = []
out_categories = []
for category in get_categories( trans ):
@@ -225,12 +229,31 @@ class CommonController( BaseController )
in_categories.append( ( category.id, category.name ) )
else:
out_categories.append( ( category.id, category.name ) )
+ if tool.is_rejected():
+ # Include the comments regarding the reason for rejection
+ reason_for_rejection = get_most_recent_event( tool ).comment
+ else:
+ reason_for_rejection = ''
+ can_approve_or_reject = trans.app.security_agent.can_approve_or_reject( trans.user, trans.user_is_admin(), cntrller, tool )
+ can_delete = trans.app.security_agent.can_delete( trans.user, trans.user_is_admin(), cntrller, tool )
+ can_download = trans.app.security_agent.can_download( trans.user, trans.user_is_admin(), cntrller, tool )
+ can_purge = trans.app.security_agent.can_purge( trans.user, trans.user_is_admin(), cntrller )
+ can_upload_new_version = trans.app.security_agent.can_upload_new_version( trans.user, tool )
+ can_view = trans.app.security_agent.can_view( trans.user, trans.user_is_admin(), cntrller, tool )
return trans.fill_template( '/webapps/community/tool/edit_tool.mako',
cntrller=cntrller,
tool=tool,
id=id,
in_categories=in_categories,
out_categories=out_categories,
+ can_approve_or_reject=can_approve_or_reject,
+ can_delete=can_delete,
+ can_download=can_download,
+ can_edit=can_edit,
+ can_purge=can_purge,
+ can_upload_new_version=can_upload_new_version,
+ can_view=can_view,
+ reason_for_rejection=reason_for_rejection,
message=message,
status=status )
@web.expose
@@ -245,15 +268,40 @@ class CommonController( BaseController )
message='Select a tool to view',
status='error' ) )
tool = get_tool( trans, id )
+ can_view = trans.app.security_agent.can_view( trans.user, trans.user_is_admin(), cntrller, tool )
+ if not can_view:
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action='browse_tools',
+ message='You are not allowed to view this tool',
+ status='error' ) )
+ can_approve_or_reject = trans.app.security_agent.can_approve_or_reject( trans.user, trans.user_is_admin(), cntrller, tool )
+ can_delete = trans.app.security_agent.can_delete( trans.user, trans.user_is_admin(), cntrller, tool )
+ can_download = trans.app.security_agent.can_download( trans.user, trans.user_is_admin(), cntrller, tool )
+ can_edit = trans.app.security_agent.can_edit( trans.user, trans.user_is_admin(), cntrller, tool )
+ can_purge = trans.app.security_agent.can_purge( trans.user, trans.user_is_admin(), cntrller )
+ can_upload_new_version = trans.app.security_agent.can_upload_new_version( trans.user, tool )
+ visible_versions = trans.app.security_agent.get_visible_versions( trans.user, trans.user_is_admin(), cntrller, tool )
categories = [ tca.category for tca in tool.categories ]
tool_file_contents = tarfile.open( tool.file_name, 'r' ).getnames()
- versions = get_versions( trans, tool )
+ if tool.is_rejected():
+ # Include the comments regarding the reason for rejection
+ reason_for_rejection = get_most_recent_event( tool ).comment
+ else:
+ reason_for_rejection = ''
return trans.fill_template( '/webapps/community/tool/view_tool.mako',
tool=tool,
tool_file_contents=tool_file_contents,
- versions=versions,
categories=categories,
cntrller=cntrller,
+ can_approve_or_reject=can_approve_or_reject,
+ can_delete=can_delete,
+ can_download=can_download,
+ can_edit=can_edit,
+ can_purge=can_purge,
+ can_upload_new_version=can_upload_new_version,
+ can_view=can_view,
+ visible_versions=visible_versions,
+ reason_for_rejection=reason_for_rejection,
message=message,
status=status )
@web.expose
@@ -267,6 +315,11 @@ class CommonController( BaseController )
status='error'
else:
tool = get_tool( trans, id )
+ if not trans.app.security_agent.can_delete( trans.user, trans.user_is_admin(), cntrller, tool ):
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action='browse_tools',
+ message='You are not allowed to delete this tool',
+ status='error' ) )
# Create a new event
event = trans.model.Event( state=trans.model.Tool.states.DELETED )
# Flush so we can get an event id
@@ -279,13 +332,32 @@ class CommonController( BaseController )
trans.sa_session.add_all( ( tool, tea ) )
trans.sa_session.flush()
# TODO: What if the tool has versions, should they all be deleted?
- message = "Tool '%s' has been marked deleted"
+ message = "Tool '%s' has been marked deleted" % tool.name
status = 'done'
return trans.response.send_redirect( web.url_for( controller=cntrller,
action='browse_tools',
message=message,
status=status ) )
@web.expose
+ def download_tool( self, trans, cntrller, **kwd ):
+ params = util.Params( kwd )
+ id = params.get( 'id', None )
+ if not id:
+ return trans.response.send_redirect( web.url_for( controller='tool',
+ action='browse_tools',
+ message='Select a tool to download',
+ status='error' ) )
+ tool = get_tool( trans, id )
+ if not trans.app.security_agent.can_download( trans.user, trans.user_is_admin(), cntrller, tool ):
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action='browse_tools',
+ message='You are not allowed to download this tool',
+ status='error' ) )
+ trans.response.set_content_type( tool.mimetype )
+ trans.response.headers['Content-Length'] = int( os.stat( tool.file_name ).st_size )
+ trans.response.headers['Content-Disposition'] = 'attachment; filename=%s' % tool.download_file_name
+ return open( tool.file_name )
+ @web.expose
def upload_new_tool_version( self, trans, cntrller, **kwd ):
params = util.Params( kwd )
message = util.restore_text( params.get( 'message', '' ) )
@@ -294,27 +366,69 @@ class CommonController( BaseController )
if not id:
return trans.response.send_redirect( web.url_for( controller=cntrller,
action='browse_tools',
- message='Select a tool to to upload a new version',
+ message='Select a tool to upload a new version',
status='error' ) )
tool = get_tool( trans, id )
+ if not trans.app.security_agent.can_upload_new_version( trans.user, tool ):
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action='browse_tools',
+ message='You are not allowed to upload a new version of this tool',
+ status='error' ) )
return trans.response.send_redirect( web.url_for( controller='upload',
action='upload',
message=message,
status=status,
replace_id=id ) )
+ @web.expose
+ @web.require_login( "view tool history" )
+ def view_tool_history( self, trans, cntrller, **kwd ):
+ params = util.Params( kwd )
+ message = util.restore_text( params.get( 'message', '' ) )
+ status = params.get( 'status', 'done' )
+ id = params.get( 'id', None )
+ if not id:
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action='browse_tools',
+ message='Select a tool to view events',
+ status='error' ) )
+ tool = get_tool( trans, id )
+ can_view = trans.app.security_agent.can_view( trans.user, trans.user_is_admin(), cntrller, tool )
+ if not can_view:
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action='browse_tools',
+ message="You are not allowed to view this tool's history",
+ status='error' ) )
+ can_approve_or_reject = trans.app.security_agent.can_approve_or_reject( trans.user, trans.user_is_admin(), cntrller, tool )
+ can_edit = trans.app.security_agent.can_edit( trans.user, trans.user_is_admin(), cntrller, tool )
+ can_delete = trans.app.security_agent.can_delete( trans.user, trans.user_is_admin(), cntrller, tool )
+ can_download = trans.app.security_agent.can_download( trans.user, trans.user_is_admin(), cntrller, tool )
+ events = [ tea.event for tea in tool.events ]
+ events = [ ( event.state, time_ago( event.update_time ), event.comment ) for event in events ]
+ return trans.fill_template( '/webapps/community/common/view_tool_history.mako',
+ cntrller=cntrller,
+ events=events,
+ tool=tool,
+ can_approve_or_reject=can_approve_or_reject,
+ can_edit=can_edit,
+ can_delete=can_delete,
+ can_download=can_download,
+ can_view=can_view,
+ message=message,
+ status=status )
## ---- Utility methods -------------------------------------------------------
-def get_versions( trans, tool ):
- versions = [tool]
- this_tool = tool
- while tool.newer_version:
- versions.insert( 0, tool.newer_version )
- tool = tool.newer_version
- tool = this_tool
- while tool.older_version:
- versions.append( tool.older_version[0] )
- tool = tool.older_version[0]
+def get_versions( item ):
+ """Get all versions of item"""
+ versions = [item]
+ this_item = item
+ while item.newer_version:
+ versions.insert( 0, item.newer_version )
+ item = item.newer_version
+ item = this_item
+ while item.older_version:
+ versions.append( item.older_version[0] )
+ item = item.older_version[0]
return versions
def get_categories( trans ):
"""Get all categories from the database"""
@@ -322,15 +436,27 @@ def get_categories( trans ):
.filter( trans.model.Category.table.c.deleted==False ) \
.order_by( trans.model.Category.table.c.name ).all()
def get_category( trans, id ):
+ """Get a category from the database"""
return trans.sa_session.query( trans.model.Category ).get( trans.security.decode_id( id ) )
def get_tool( trans, id ):
+ """Get a tool from the database"""
return trans.sa_session.query( trans.model.Tool ).get( trans.app.security.decode_id( id ) )
def get_tools( trans ):
- # Return only the latest version of each tool
+ """Get only the latest version of each tool from the database"""
return trans.sa_session.query( trans.model.Tool ) \
.filter( trans.model.Tool.newer_version_id == None ) \
.order_by( trans.model.Tool.name )
def get_event( trans, id ):
+ """Get an event from the databse"""
return trans.sa_session.query( trans.model.Event ).get( trans.security.decode_id( id ) )
+def get_most_recent_event( item ):
+ """Get the most recent event for item"""
+ if item.events:
+ # Sort the events in ascending order by update_time
+ events = model.sort_by_attr( [ item_event_assoc.event for item_event_assoc in item.events ], 'update_time' )
+ # Get the last event that occurred
+ return events[-1]
+ return None
def get_user( trans, id ):
+ """Get a user from the database"""
return trans.sa_session.query( trans.model.User ).get( trans.security.decode_id( id ) )
--- /dev/null
+++ b/templates/webapps/community/admin/reject_tool.mako
@@ -0,0 +1,70 @@
+<%namespace file="/message.mako" import="render_msg" />
+
+<%!
+ def inherit(context):
+ if context.get('use_panels'):
+ return '/webapps/community/base_panels.mako'
+ else:
+ return '/base.mako'
+%>
+<%inherit file="${inherit(context)}"/>
+
+<%def name="title()">Reject Tool</%def>
+
+<h2>Reject Tool</h2>
+
+<ul class="manage-table-actions">
+ <li><a class="action-button" id="tool-${tool.id}-popup" class="menubutton">Tool Actions</a></li>
+ <div popupmenu="tool-${tool.id}-popup">
+ <a class="action-button" href="${h.url_for( controller='common', action='view_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">View tool</a>
+ <a class="action-button" href="${h.url_for( controller='common', action='view_tool_history', id=trans.security.encode_id( tool.id ), cntrller=cntrller )}">Tool history</a>
+ <a class="action-button" href="${h.url_for( controller='common', action='download_tool', id=trans.security.encode_id( tool.id ), cntrller=cntrller )}">Download tool</a>
+ </div>
+</ul>
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<div class="toolForm">
+ <div class="toolFormTitle">${tool.name}</div>
+ <form name="reject_tool" action="${h.url_for( controller='admin', action='reject_tool', id=trans.security.encode_id( tool.id ) )}" method="post" >
+ <div class="form-row">
+ <label>Tool id:</label>
+ ${tool.tool_id}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Version:</label>
+ ${tool.version}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Description:</label>
+ ${tool.description}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>User description:</label>
+ ${tool.user_description}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Uploaded by:</label>
+ ${tool.user.username}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Reason for rejection</label>
+ <textarea name="comments" rows="5" cols="40"></textarea>
+ <div class="toolParamHelp" style="clear: both;">
+ Required
+ </div>
+ </div>
+ <div class="form-row">
+ <input type="submit" name="reject_button" value="Reject"/>
+ <input type="submit" name="cancel_reject_button" value="Cancel"/>
+ </div>
+ </form>
+ </div>
+</div>
--- a/lib/galaxy/webapps/community/controllers/upload.py
+++ b/lib/galaxy/webapps/community/controllers/upload.py
@@ -33,7 +33,6 @@ class UploadController( BaseController )
message='No categories have been configured in this instance of the Galaxy Community. An administrator needs to create some via the Administrator control panel before anything can be uploaded',
status='error' ) )
if params.get( 'upload_button', False ):
-
url_paste = params.get( 'url', '' ).strip()
file_data = params.get( 'file_data', '' )
if file_data == '' and url_paste == '':
@@ -66,19 +65,24 @@ class UploadController( BaseController )
obj = datatype.create_model_object( meta )
trans.sa_session.add( obj )
if isinstance( obj, trans.app.model.Tool ):
- existing = trans.sa_session.query( trans.app.model.Tool ).filter_by( tool_id = meta.id ).first()
- if existing and replace_id is None:
+ existing = trans.sa_session.query( trans.app.model.Tool ) \
+ .filter_by( tool_id = meta.id ) \
+ .first()
+ if replace_id:
+ replace_version = trans.sa_session.query( trans.app.model.Tool ).get( trans.security.decode_id( replace_id ) )
+ if existing and not replace_id:
raise UploadError( 'A tool with the same ID already exists. If you are trying to update this tool to a new version, please use the upload form on the "Edit Tool" page. Otherwise, please choose a new ID.' )
- elif existing:
- replace_version = trans.sa_session.query( trans.app.model.Tool ).get( trans.security.decode_id( replace_id ) )
+ elif replace_id and not existing:
+ raise UploadError( 'Tool ids must match when uploading a new version of a tool. The new tool id does not match the old tool id (%s). Check the tool XML files.' % str( replace_version.tool_id ) )
+ elif existing and replace_id:
if replace_version.newer_version:
# If the user has picked an old version, switch to the newest version
- replace_version = get_versions( trans, replace_version )[0]
+ replace_version = get_versions( replace_version )[0]
if replace_version.tool_id != meta.id:
- raise UploadError( 'The new tool id (%s) does not match the old tool id (%s). Check the tool XML file' % ( meta.id, replace_version.tool_id ) )
- for old_version in get_versions( trans, replace_version ):
+ raise UploadError( 'Tool ids must match when uploading a new version of a tool. The new tool id (%s) does not match the old tool id (%s). Check the tool XML files.' % ( str( meta.id ), str( replace_version.tool_id ) ) )
+ for old_version in get_versions( replace_version ):
if old_version.version == meta.version:
- raise UploadError( 'The new version (%s) matches an old version. Check your version in the tool XML file' % meta.version )
+ raise UploadError( 'The new version (%s) matches an old version. Check your version in the tool XML file.' % str( meta.version ) )
if old_version.is_new():
raise UploadError( 'There is an existing version of this tool which has not yet been submitted for approval, so either <a href="%s">submit or delete it</a> before uploading a new version.' % url_for( controller='common',
action='view_tool',
@@ -86,7 +90,7 @@ class UploadController( BaseController )
id=trans.security.encode_id( old_version.id ) ) )
if old_version.is_waiting():
raise UploadError( 'There is an existing version of this tool which is waiting for administrative approval, so contact an administrator for help.' )
- # Defer setting the id since the newer version id doesn't exist until the new Tool object is flushed
+ # Defer setting the id since the newer version id doesn't exist until the new Tool object is flushed
if category_ids:
for category_id in category_ids:
category = trans.app.model.Category.get( trans.security.decode_id( category_id ) )
@@ -123,7 +127,7 @@ class UploadController( BaseController )
elif replace_id is not None:
replace_version = trans.sa_session.query( trans.app.model.Tool ).get( int( trans.app.security.decode_id( replace_id ) ) )
old_version = None
- for old_version in get_versions( trans, replace_version ):
+ for old_version in get_versions( replace_version ):
if old_version.is_new():
message = 'There is an existing version of this tool which has not been submitted for approval, so either submit or delete it before uploading a new version.'
break
--- a/templates/webapps/community/category/create_category.mako
+++ b/templates/webapps/community/category/create_category.mako
@@ -19,13 +19,12 @@
<div class="toolFormBody"><form name="create_category_form" id="create_category_form" action="${h.url_for( action='create_category' )}" method="post" ><div class="form-row">
- <input name="webapp" type="hidden" value="${webapp}" size=40"/><label>Name:</label>
- <input name="name" type="textfield" value="" size=40"/>
+ <input name="name" type="textfield" value="${name}" size=40"/></div><div class="form-row"><label>Description:</label>
- <input name="description" type="textfield" value="" size=40"/>
+ <input name="description" type="textfield" value="${description}" size=40"/></div><div class="form-row"><input type="submit" name="create_category_button" value="Save"/>
--- a/templates/webapps/community/tool/edit_tool.mako
+++ b/templates/webapps/community/tool/edit_tool.mako
@@ -1,6 +1,10 @@
<%inherit file="/base.mako"/><%namespace file="/message.mako" import="render_msg" />
+<%
+ from galaxy.web.framework.helpers import time_ago
+%>
+
<%!
def inherit(context):
if context.get('use_panels'):
@@ -17,7 +21,7 @@
$("input:text:first").focus();
})
function confirmSubmit() {
- if ( confirm( "After you have submitted your tool to be published, you will no longer be able to modify it. Click OK to submit it." ) ) {
+ if ( confirm( "Make sure you have filled in the User Description field. After you have submitted your tool to be published, you will no longer be able to modify it. Click OK to submit it." ) ) {
return true;
} else {
return false;
@@ -52,28 +56,57 @@
<%def name="title()">Edit Tool</%def>
-<h2>Edit Tool: ${tool.name} <em>${tool.description}</em></h2>
+<h2>Edit Tool</h2>
+
+${tool.get_state_message()}
+<p/>
+
+<ul class="manage-table-actions">
+ %if can_approve_or_reject:
+ <li><a class="action-button" href="${h.url_for( controller='admin', action='set_tool_state', state=trans.model.Tool.states.APPROVED, id=trans.security.encode_id( tool.id ), cntrller=cntrller )}">Approve</a></li>
+ <li><a class="action-button" href="${h.url_for( controller='admin', action='set_tool_state', state=trans.model.Tool.states.REJECTED, id=trans.security.encode_id( tool.id ), cntrller=cntrller )}">Reject</a></li>
+ %endif
+ <li><a class="action-button" id="tool-${tool.id}-popup" class="menubutton">Tool Actions</a></li>
+ <div popupmenu="tool-${tool.id}-popup">
+ %if can_view:
+ <a class="action-button" href="${h.url_for( controller='common', action='view_tool_history', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Tool history</a>
+ <a class="action-button" href="${h.url_for( controller='common', action='view_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">View tool</a>
+ %endif
+ %if can_download:
+ <a class="action-button" href="${h.url_for( controller='common', action='download_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Download tool</a>
+ %endif
+ %if can_delete:
+ <a class="action-button" href="${h.url_for( controller='common', action='delete_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}" confirm="Are you sure you want to delete this tool?">Delete tool</a>
+ %endif
+ %if can_upload_new_version:
+ <a class="action-button" href="${h.url_for( controller='common', action='upload_new_tool_version', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Upload a new version</a>
+ %endif
+ %if can_purge:
+ <li><a class="action-button" href="${h.url_for( controller='admin', action='purge_tool', id=trans.security.encode_id( tool.id ), cntrller=cntrller )}" confirm="Purging removes records from the database, are you sure you want to purge this tool?">Purge tool</a></li>
+ %endif
+ </div>
+</ul>
%if message:
${render_msg( message, status )}
%endif
-%if cntrller == 'admin' or trans.user == tool.user:
+%if can_edit:
<form id="edit_tool" name="edit_tool" action="${h.url_for( controller='common', action='edit_tool' )}" method="post">
+ %if tool.is_rejected():
+ <div class="toolForm">
+ <div class="toolFormTitle">Reason for rejection</div>
+ <div class="toolFormBody">
+ <div class="form-row">
+ ${reason_for_rejection}
+ <div style="clear: both"></div>
+ </div>
+ </div>
+ </div>
+ <p/>
+ %endif
<div class="toolForm">
- <div class="toolFormTitle">${tool.name}
- %if not tool.deleted:
- <a id="tool-${tool.id}-popup" class="popup-arrow" style="display: none;">▼</a>
- <div popupmenu="tool-${tool.id}-popup">
- <a class="action-button" href="${h.url_for( controller='common', action='view_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">View information</a>
- <a class="action-button" href="${h.url_for( controller='tool', action='download_tool', id=trans.app.security.encode_id( tool.id ) )}">Download tool</a>
- <a class="action-button" href="${h.url_for( controller='common', action='delete_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Delete tool</a>
- %if not tool.is_new() and not tool.is_waiting():
- <a class="action-button" href="${h.url_for( controller='common', action='upload_new_tool_version', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Upload a new version</a>
- %endif
- </div>
- %endif
- </div>
+ <div class="toolFormTitle">${tool.name}</div><div class="toolFormBody"><input type="hidden" name="id" value="${trans.app.security.encode_id( tool.id )}"/><input type="hidden" name="cntrller" value="${cntrller}"/>
@@ -89,11 +122,27 @@
</div><div class="form-row"><label>Description:</label>
+ ${tool.description}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>User Description:</label>
%if tool.user_description:
<div class="form-row-input"><pre><textarea name="user_description" rows="5" cols="35">${tool.user_description}</textarea></pre></div>
%else:
<div class="form-row-input"><textarea name="user_description" rows="5" cols="35"></textarea></div>
%endif
+ <div class="toolParamHelp" style="clear: both;">Required when submitting for approval</div>
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Uploaded by:</label>
+ ${tool.user.username}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Date uploaded:</label>
+ ${time_ago( tool.create_time )}
<div style="clear: both"></div></div></div>
@@ -120,7 +169,7 @@
</div></div><p/>
- %if tool.is_new():
+ %if tool.is_new() or tool.is_rejected():
<div class="toolForm"><div class="toolFormTitle">Get approval for publishing</div><div class="toolFormBody">
--- /dev/null
+++ b/templates/webapps/community/common/view_tool_history.mako
@@ -0,0 +1,92 @@
+<%namespace file="/message.mako" import="render_msg" />
+
+<%
+ if cntrller in [ 'tool' ] and can_edit:
+ menu_label = 'Edit information or submit for approval'
+ else:
+ menu_label = 'Edit information'
+%>
+
+<%!
+ def inherit(context):
+ if context.get('use_panels'):
+ return '/webapps/community/base_panels.mako'
+ else:
+ return '/base.mako'
+%>
+<%inherit file="${inherit(context)}"/>
+
+<h2>Tool history</h2>
+<ul class="manage-table-actions">
+ %if can_approve_or_reject:
+ <li><a class="action-button" href="${h.url_for( controller='admin', action='set_tool_state', state=trans.model.Tool.states.APPROVED, id=trans.security.encode_id( tool.id ), cntrller=cntrller )}">Approve</a></li>
+ <li><a class="action-button" href="${h.url_for( controller='admin', action='set_tool_state', state=trans.model.Tool.states.REJECTED, id=trans.security.encode_id( tool.id ), cntrller=cntrller )}">Reject</a></li>
+ %endif
+ <li><a class="action-button" id="tool-${tool.id}-popup" class="menubutton">Tool Actions</a></li>
+ <div popupmenu="tool-${tool.id}-popup">
+ %if can_edit:
+ <a class="action-button" href="${h.url_for( controller='common', action='edit_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">${menu_label}</a>
+ %endif
+ %if can_view:
+ <a class="action-button" href="${h.url_for( controller='common', action='view_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">View tool</a>
+ %endif
+ %if can_delete:
+ <a class="action-button" href="${h.url_for( controller='common', action='delete_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}" confirm="Are you sure you want to delete this tool?">Delete tool</a>
+ %endif
+ %if can_download:
+ <a class="action-button" href="${h.url_for( controller='common', action='download_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Download tool</a>
+ %endif
+ </div>
+</ul>
+
+%if message:
+ ${render_msg( message, status )}
+%endif
+
+<div class="toolForm">
+ <div class="toolFormTitle">${tool.name}</div>
+ <div class="form-row">
+ <label>Tool id:</label>
+ ${tool.tool_id}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Version:</label>
+ ${tool.version}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Description:</label>
+ ${tool.description}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>User description:</label>
+ ${tool.user_description}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Uploaded by:</label>
+ ${tool.user.username}
+ <div style="clear: both"></div>
+ </div>
+</div>
+<p/>
+<table class="grid">
+ <thead>
+ <tr>
+ <th>State</th>
+ <th>Last Update</th>
+ <th>Comments</th>
+ </tr>
+ </thead>
+ <tbody>
+ %for state, updated, comments in events:
+ <tr class="libraryRow libraryOrFolderRow" id="libraryRow">
+ <td><b><a>${state}</a></b></td>
+ <td><a>${updated}</a></td>
+ <td><a>${comments}</a></td>
+ </tr>
+ %endfor
+ </tbody>
+</table>
--- a/lib/galaxy/webapps/community/model/__init__.py
+++ b/lib/galaxy/webapps/community/model/__init__.py
@@ -164,6 +164,21 @@ class Tool( object ):
return self.state() == self.states.REJECTED
def is_archived( self ):
return self.state() == self.states.ARCHIVED
+ def get_state_message( self ):
+ if self.is_new():
+ return '<font color="red"><b><i>This is an unsubmitted version of this tool</i></b></font>'
+ if self.is_error():
+ return '<font color="red"><b><i>This tool is in an error state</i></b></font>'
+ if self.is_deleted():
+ return '<font color="red"><b><i>This is a deleted version of this tool</i></b></font>'
+ if self.is_waiting():
+ return '<font color="red"><b><i>This version of this tool is awaiting administrative approval</i></b></font>'
+ if self.is_approved():
+ return '<b><i>This is the latest approved version of this tool</i></b>'
+ if self.is_rejected():
+ return '<font color="red"><b><i>This version of this tool has been rejected by an administrator</i></b></font>'
+ if self.is_archived():
+ return '<font color="red"><b><i>This is an archived version of this tool</i></b></font>'
@property
def extension( self ):
# if instantiated via a query, this unmapped property won't exist
@@ -240,7 +255,6 @@ class ToolAnnotationAssociation( object
pass
## ---- Utility methods -------------------------------------------------------
-
def sort_by_attr( seq, attr ):
"""
Sort the sequence of objects by object's attribute
@@ -256,7 +270,6 @@ def sort_by_attr( seq, attr ):
intermed = map( None, map( getattr, seq, ( attr, ) * len( seq ) ), xrange( len( seq ) ), seq )
intermed.sort()
return map( operator.getitem, intermed, ( -1, ) * len( intermed ) )
-
def directory_hash_id( id ):
s = str( id )
l = len( s )
@@ -269,5 +282,3 @@ def directory_hash_id( id ):
padded = padded[:-3]
# Break into chunks of three
return [ padded[i*3:(i+1)*3] for i in range( len( padded ) // 3 ) ]
-
-
--- a/templates/webapps/community/tool/view_tool.mako
+++ b/templates/webapps/community/tool/view_tool.mako
@@ -3,19 +3,11 @@
<%
from galaxy.web.framework.helpers import time_ago
from urllib import quote_plus
-
- menu_label = 'Edit information'
-
- if cntrller in [ 'tool' ]:
- can_edit = trans.app.security_agent.can_edit_item( trans.user, tool )
- if can_edit:
- menu_label = 'Edit information or submit for approval'
- can_upload_new_version = trans.app.security_agent.can_upload_new_version( trans.user, tool, versions )
- visible_versions = []
- for version in versions:
- if version.is_approved() or version.is_archived() or version.user == trans.user:
- visible_versions.append( version )
+ if cntrller in [ 'tool' ] and can_edit:
+ menu_label = 'Edit information or submit for approval'
+ else:
+ menu_label = 'Edit information'
%><%!
@@ -57,127 +49,133 @@
<%def name="title()">View Tool</%def>
-<h2>View Tool: ${tool.name} <em>${tool.description}</em></h2>
+<h2>View Tool</h2>
-%if tool.is_approved():
- <b><i>This is the latest approved version of this tool</i></b>
-%elif tool.is_deleted():
- <font color="red"><b><i>This is a deleted version of this tool</i></b></font>
-%elif tool.is_archived():
- <font color="red"><b><i>This is an archived version of this tool</i></b></font>
-%elif tool.is_new():
- <font color="red"><b><i>This is an unsubmitted version of this tool</i></b></font>
-%elif tool.is_waiting():
- <font color="red"><b><i>This version of this tool is awaiting administrative approval</i></b></font>
-%elif tool.is_rejected():
- <font color="red"><b><i>This version of this tool has been rejected by an administrator</i></b></font>
-%endif
+${tool.get_state_message()}
<p/>
-%if cntrller=='admin' and tool.is_waiting():
- <p>
- <ul class="manage-table-actions">
- <li><a class="action-button" href="${h.url_for( controller='admin', action='set_tool_state', state=trans.model.Tool.states.APPROVED, id=trans.security.encode_id( tool.id ), cntrller=cntrller )}"><span>Approve</span></a></li>
- <li><a class="action-button" href="${h.url_for( controller='admin', action='set_tool_state', state=trans.model.Tool.states.REJECTED, id=trans.security.encode_id( tool.id ), cntrller=cntrller )}"><span>Reject</span></a></li>
- </ul>
- </p>
-%endif
+<ul class="manage-table-actions">
+ %if can_approve_or_reject:
+ <li><a class="action-button" href="${h.url_for( controller='admin', action='set_tool_state', state=trans.model.Tool.states.APPROVED, id=trans.security.encode_id( tool.id ), cntrller=cntrller )}">Approve</a></li>
+ <li><a class="action-button" href="${h.url_for( controller='admin', action='set_tool_state', state=trans.model.Tool.states.REJECTED, id=trans.security.encode_id( tool.id ), cntrller=cntrller )}">Reject</a></li>
+ %endif
+ <li><a class="action-button" id="tool-${tool.id}-popup" class="menubutton">Tool Actions</a></li>
+ <div popupmenu="tool-${tool.id}-popup">
+ %if can_edit:
+ <a class="action-button" href="${h.url_for( controller='common', action='edit_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">${menu_label}</a>
+ %endif
+ <a class="action-button" href="${h.url_for( controller='common', action='view_tool_history', id=trans.security.encode_id( tool.id ), cntrller=cntrller )}">Tool history</a>
+ %if can_download:
+ <a class="action-button" href="${h.url_for( controller='common', action='download_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Download tool</a>
+ %endif
+ %if can_delete:
+ <a class="action-button" href="${h.url_for( controller='common', action='delete_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}" confirm="Are you sure you want to delete this tool?">Delete tool</a>
+ %endif
+ %if can_upload_new_version:
+ <a class="action-button" href="${h.url_for( controller='common', action='upload_new_tool_version', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Upload a new version</a>
+ %endif
+ %if can_purge:
+ <li><a class="action-button" href="${h.url_for( controller='admin', action='purge_tool', id=trans.security.encode_id( tool.id ), cntrller=cntrller )}" confirm="Purging removes records from the database, are you sure you want to purge this tool?">Purge tool</a></li>
+ %endif
+ </div>
+</ul>
%if message:
${render_msg( message, status )}
%endif
-<div class="toolForm">
- <div class="toolFormTitle">${tool.name}
- %if not tool.deleted:
- <a id="tool-${tool.id}-popup" class="popup-arrow" style="display: none;">▼</a>
- <div popupmenu="tool-${tool.id}-popup">
- %if cntrller=='admin' or can_edit:
- <a class="action-button" href="${h.url_for( controller='common', action='edit_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">${menu_label}</a>
- %endif
- <a class="action-button" href="${h.url_for( controller='tool', action='download_tool', id=trans.app.security.encode_id( tool.id ) )}">Download tool</a>
- %if cntrller=='admin' or trans.user==tool.user:
- <a class="action-button" href="${h.url_for( controller='common', action='delete_tool', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Delete tool</a>
- %endif
- %if cntrller=='admin' or can_upload_new_version:
- <a class="action-button" href="${h.url_for( controller='common', action='upload_new_tool_version', id=trans.app.security.encode_id( tool.id ), cntrller=cntrller )}">Upload a new version</a>
- %endif
+%if can_view:
+ %if tool.is_rejected():
+ <div class="toolForm">
+ <div class="toolFormTitle">Reason for rejection</div>
+ <div class="toolFormBody">
+ <div class="form-row">
+ ${reason_for_rejection}
+ <div style="clear: both"></div>
+ </div></div>
- %endif
- </div>
- <div class="toolFormBody">
- <div class="form-row">
- <label>Tool Id:</label>
- ${tool.tool_id}
- <div style="clear: both"></div></div>
- <div class="form-row">
- <label>Version:</label>
- ${tool.version}
- <div style="clear: both"></div>
- </div>
- <div class="form-row">
- <label>Description:</label>
- %if tool.user_description:
- <pre>${tool.user_description}</pre>
- %endif
- <div style="clear: both"></div>
- </div>
- <div class="form-row">
- <label>Uploaded by:</label>
- ${tool.user.username}
- <div style="clear: both"></div>
- </div>
- <div class="form-row">
- <label>Date uploaded:</label>
- ${time_ago( tool.create_time )}
- <div style="clear: both"></div>
- </div>
- <div class="form-row">
- <label>Categories:</label>
- %if categories:
- <ul>
- %for category in categories:
- <li>${category.name}</li>
- %endfor
- </ul>
- %else:
- none set
- %endif
- <div style="clear: both"></div>
- </div>
- %if len( visible_versions ) > 1:
+ <p/>
+ %endif
+ <div class="toolForm">
+ <div class="toolFormTitle">${tool.name}</div>
+ <div class="toolFormBody"><div class="form-row">
- <label>All Versions:</label>
- <ul>
- %for version in visible_versions:
- %if version == tool:
- <li><strong>${version.version} (this version)</strong></li>
- %else:
- <li><a href="${h.url_for( controller='common', action='view_tool', id=trans.app.security.encode_id( version.id ), cntrller=cntrller )}">${version.version}</a></li>
- %endif
- %endfor
- </ul>
+ <label>Tool Id:</label>
+ ${tool.tool_id}
<div style="clear: both"></div></div>
- %endif
- </div>
-</div>
-
-<p/>
-
-<div class="toolForm">
- <div class="toolFormTitle">Tool Contents</div>
- <div class="toolFormBody">
- <div class="form-row">
- <ul class="toolFile">
- <li><a href="${h.url_for( controller='tool', action='download_tool', id=trans.app.security.encode_id( tool.id ) )}">${tool.download_file_name}</a></li>
- <ul class="fileBrowser">
- %for name in tool_file_contents:
- <li><a href="${h.url_for( controller='tool', action='view_tool_file', id=trans.app.security.encode_id( tool.id ), file_name=quote_plus( name ) )}">${name}</a></li>
- %endfor
- </ul>
- </ul>
+ <div class="form-row">
+ <label>Version:</label>
+ ${tool.version}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Description:</label>
+ ${tool.description}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>User Description:</label>
+ %if tool.user_description:
+ <pre>${tool.user_description}</pre>
+ %endif
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Uploaded by:</label>
+ ${tool.user.username}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Date uploaded:</label>
+ ${time_ago( tool.create_time )}
+ <div style="clear: both"></div>
+ </div>
+ <div class="form-row">
+ <label>Categories:</label>
+ %if categories:
+ <ul>
+ %for category in categories:
+ <li>${category.name}</li>
+ %endfor
+ </ul>
+ %else:
+ none set
+ %endif
+ <div style="clear: both"></div>
+ </div>
+ %if len( visible_versions ) > 1:
+ <div class="form-row">
+ <label>All Versions:</label>
+ <ul>
+ %for version in visible_versions:
+ %if version == tool:
+ <li><strong>${version.version} (this version)</strong></li>
+ %else:
+ <li><a href="${h.url_for( controller='common', action='view_tool', id=trans.app.security.encode_id( version.id ), cntrller=cntrller )}">${version.version}</a></li>
+ %endif
+ %endfor
+ </ul>
+ <div style="clear: both"></div>
+ </div>
+ %endif
</div></div>
-</div>
+ <p/>
+ <div class="toolForm">
+ <div class="toolFormTitle">Tool Contents</div>
+ <div class="toolFormBody">
+ <div class="form-row">
+ <ul class="toolFile">
+ <li><a href="${h.url_for( controller='tool', action='download_tool', id=trans.app.security.encode_id( tool.id ) )}">${tool.download_file_name}</a></li>
+ <ul class="fileBrowser">
+ %for name in tool_file_contents:
+ <li><a href="${h.url_for( controller='tool', action='view_tool_file', id=trans.app.security.encode_id( tool.id ), file_name=quote_plus( name ) )}">${name}</a></li>
+ %endfor
+ </ul>
+ </ul>
+ </div>
+ </div>
+ </div>
+%endif
--- a/templates/webapps/community/category/edit_category.mako
+++ b/templates/webapps/community/category/edit_category.mako
@@ -10,7 +10,6 @@
<div class="toolFormBody"><form name="library" action="${h.url_for( controller='admin', action='edit_category' )}" method="post" ><div class="form-row">
- <input name="webapp" type="hidden" value="${webapp}" size=40"/><label>Name:</label><div style="float: left; width: 250px; margin-right: 10px;"><input type="text" name="name" value="${category.name}" size="40"/>
--- a/lib/galaxy/webapps/community/controllers/tool.py
+++ b/lib/galaxy/webapps/community/controllers/tool.py
@@ -97,7 +97,9 @@ class ToolController( BaseController ):
for k, v in kwd.items():
if k.startswith( 'f-' ):
del kwd[ k ]
- return self.browse_tools( trans, **kwd )
+ return trans.response.send_redirect( web.url_for( controller='tool',
+ action='browse_tools',
+ **kwd ) )
# Render the list view
return self.category_list_grid( trans, **kwd )
@web.expose
@@ -107,19 +109,20 @@ class ToolController( BaseController ):
# to take this approach because the "-" character is illegal in HTTP requests.
if 'operation' in kwd:
operation = kwd['operation'].lower()
- if operation == "view tool":
+ if operation == "view_tool":
return trans.response.send_redirect( web.url_for( controller='common',
action='view_tool',
cntrller='tool',
**kwd ) )
- elif operation == "edit tool":
+ elif operation == "edit_tool":
return trans.response.send_redirect( web.url_for( controller='common',
action='edit_tool',
cntrller='tool',
**kwd ) )
elif operation == "download tool":
- return trans.response.send_redirect( web.url_for( controller='tool',
+ return trans.response.send_redirect( web.url_for( controller='common',
action='download_tool',
+ cntrller='tool',
**kwd ) )
elif operation == "tools_by_user":
# Eliminate the current filters if any exist.
@@ -154,20 +157,6 @@ class ToolController( BaseController ):
# Render the list view
return self.tool_list_grid( trans, **kwd )
@web.expose
- def download_tool( self, trans, **kwd ):
- params = util.Params( kwd )
- id = params.get( 'id', None )
- if not id:
- return trans.response.send_redirect( web.url_for( controller='tool',
- action='browse_tools',
- message='Select a tool to download',
- status='error' ) )
- tool = get_tool( trans, id )
- trans.response.set_content_type( tool.mimetype )
- trans.response.headers['Content-Length'] = int( os.stat( tool.file_name ).st_size )
- trans.response.headers['Content-Disposition'] = 'attachment; filename=%s' % tool.download_file_name
- return open( tool.file_name )
- @web.expose
def view_tool_file( self, trans, **kwd ):
params = util.Params( kwd )
id = params.get( 'id', None )
--- a/lib/galaxy/webapps/community/controllers/admin.py
+++ b/lib/galaxy/webapps/community/controllers/admin.py
@@ -399,16 +399,21 @@ class AdminController( BaseController, A
# to take this approach because the "-" character is illegal in HTTP requests.
if 'operation' in kwd:
operation = kwd['operation'].lower()
- if operation == "edit tool":
+ if operation == "edit_tool":
return trans.response.send_redirect( web.url_for( controller='common',
action='edit_tool',
cntrller='admin',
**kwd ) )
- elif operation == "view tool":
+ elif operation == "view_tool":
return trans.response.send_redirect( web.url_for( controller='common',
action='view_tool',
cntrller='admin',
**kwd ) )
+ elif operation == 'tool_history':
+ return trans.response.send_redirect( web.url_for( controller='common',
+ cntrller='admin',
+ action='events',
+ **kwd ) )
elif operation == "tools_by_user":
# Eliminate the current filters if any exist.
for k, v in kwd.items():
@@ -457,7 +462,9 @@ class AdminController( BaseController, A
for k, v in kwd.items():
if k.startswith( 'f-' ):
del kwd[ k ]
- return self.browse_tools( trans, **kwd )
+ return trans.response.send_redirect( web.url_for( controller='admin',
+ action='browse_tools',
+ **kwd ) )
# Render the list view
return self.category_list_grid( trans, **kwd )
@web.expose
@@ -481,16 +488,26 @@ class AdminController( BaseController, A
@web.require_admin
def create_category( self, trans, **kwd ):
params = util.Params( kwd )
- webapp = params.get( 'webapp', 'community' )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
if params.get( 'create_category_button', False ):
name = util.restore_text( params.name )
description = util.restore_text( params.description )
+ error = False
if not name or not description:
- message = "Enter a valid name and a description"
- elif trans.sa_session.query( trans.app.model.Category ).filter( trans.app.model.Category.table.c.name==name ).first():
- message = "A category with that name already exists"
+ message = 'Enter a valid name and a description'
+ error = True
+ elif trans.sa_session.query( trans.app.model.Category ) \
+ .filter( trans.app.model.Category.table.c.name==name ) \
+ .first():
+ message = 'A category with that name already exists'
+ error = True
+ if error:
+ return trans.fill_template( '/webapps/community/category/create_category.mako',
+ name=name,
+ description=description,
+ message=message,
+ status='error' )
else:
# Create the category
category = trans.app.model.Category( name=name, description=description )
@@ -499,26 +516,27 @@ class AdminController( BaseController, A
trans.sa_session.flush()
trans.response.send_redirect( web.url_for( controller='admin',
action='manage_categories',
- webapp=webapp,
message=util.sanitize_text( message ),
status='done' ) )
trans.response.send_redirect( web.url_for( controller='admin',
action='create_category',
- webapp=webapp,
message=util.sanitize_text( message ),
status='error' ) )
+ else:
+ name = ''
+ description = ''
return trans.fill_template( '/webapps/community/category/create_category.mako',
- webapp=webapp,
+ name=name,
+ description=description,
message=message,
status=status )
@web.expose
@web.require_admin
def set_tool_state( self, trans, state, **kwd ):
params = util.Params( kwd )
- webapp = params.get( 'webapp', 'galaxy' )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
- redirect = params.get( 'no_redirect', True )
+ comments = util.restore_text( params.get( 'comments', '' ) )
id = params.get( 'id', None )
if not id:
message = "No tool id received for setting status"
@@ -526,32 +544,130 @@ class AdminController( BaseController, A
else:
tool = get_tool( trans, id )
if state == trans.app.model.Tool.states.APPROVED:
- # If we're approving a tool, all previous versions must be set to archived
- for version in get_versions( trans, tool ):
+ # If we're approving a tool, all previously approved versions must be set to archived
+ for version in get_versions( tool ):
+ # TODO: get latest approved version instead of all versions
if version != tool and version.is_approved():
- self.set_tool_state( trans,
- trans.app.model.Tool.states.ARCHIVED,
- id=trans.security.encode_id( version.id ),
- redirect='False' )
- event = trans.model.Event( state )
- # Flush so we an get an id
- trans.sa_session.add( event )
- trans.sa_session.flush()
- tea = trans.model.ToolEventAssociation( tool, event )
- trans.sa_session.add( tea )
- trans.sa_session.flush()
+ # Create an event with state ARCHIVED for the previously approved version of this tool
+ self.__create_tool_event( trans,
+ version,
+ trans.app.model.Tool.states.ARCHIVED )
+ # Create an event with state APPROVED for this tool
+ self.__create_tool_event( trans, tool, state, comments )
+ elif state == trans.app.model.Tool.states.REJECTED:
+ # If we're rejecting a tool, comments about why are necessary.
+ return trans.fill_template( '/webapps/community/admin/reject_tool.mako',
+ tool=tool,
+ cntrller='admin' )
message = "State of tool '%s' is now %s" % ( tool.name, state )
- if redirect:
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='browse_tools',
+ message=message,
+ status=status ) )
+ @web.expose
+ @web.require_admin
+ def reject_tool( self, trans, **kwd ):
+ params = util.Params( kwd )
+ if params.get( 'cancel_reject_button', False ):
+ # Fix up the keyword dict to include params to view the current tool
+ # since that is the page from which we originated.
+ del kwd[ 'cancel_reject_button' ]
+ del kwd[ 'comments' ]
+ kwd[ 'webapp' ] = 'community'
+ kwd[ 'operation' ] = 'view_tool'
+ message = 'Tool rejection cancelled'
+ status = 'done'
+ return trans.response.send_redirect( web.url_for( controller='admin',
+ action='browse_tools',
+ message=message,
+ status=status,
+ **kwd ) )
+ id = params.get( 'id', None )
+ if not id:
+ return trans.response.send_redirect( web.url_for( controller=cntrller,
+ action='browse_tools',
+ message='No tool id received for rejecting',
+ status='error' ) )
+ tool = get_tool( trans, id )
+ if not trans.app.security_agent.can_approve_or_reject( trans.user, trans.user_is_admin(), 'admin', tool ):
+ return trans.response.send_redirect( web.url_for( controller='admin',
+ action='browse_tools',
+ message='You are not allowed to reject this tool',
+ status='error' ) )
+ # Comments are required when rejecting a tool.
+ comments = util.restore_text( params.get( 'comments', '' ) )
+ if not comments:
+ message = 'The reason for rejection is required when rejecting a tool.'
+ return trans.fill_template( '/webapps/community/admin/reject_tool.mako',
+ tool=tool,
+ cntrller='admin',
+ message=message,
+ status='error' )
+ # Create an event with state REJECTED for this tool
+ self.__create_tool_event( trans, tool, trans.app.model.Tool.states.REJECTED, comments )
+ message = 'The tool "%s" has been rejected.' % tool.name
+ return trans.response.send_redirect( web.url_for( controller='admin',
+ action='browse_tools',
+ operation='tools_by_state',
+ state='rejected',
+ message=message,
+ status='done' ) )
+ def __create_tool_event( self, trans, tool, state, comments='' ):
+ event = trans.model.Event( state, comments )
+ # Flush so we can get an id
+ trans.sa_session.add( event )
+ trans.sa_session.flush()
+ tea = trans.model.ToolEventAssociation( tool, event )
+ trans.sa_session.add( tea )
+ trans.sa_session.flush()
+ @web.expose
+ @web.require_admin
+ def purge_tool( self, trans, **kwd ):
+ # This method completely removes a tool record and all associated foreign key rows
+ # from the database, so it must be used carefully.
+ # This method should only be called for a tool that has previously been deleted.
+ # Purging a deleted tool deletes all of the following from the database:
+ # - ToolCategoryAssociations
+ # - ToolEventAssociations and associated Events
+ # TODO: when we add tagging for tools, we'll have to purge them as well
+ params = util.Params( kwd )
+ id = kwd.get( 'id', None )
+ if not id:
+ message = "No tool ids received for purging"
trans.response.send_redirect( web.url_for( controller='admin',
action='browse_tools',
- webapp=webapp,
- message=message,
- status=status ) )
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ ids = util.listify( id )
+ message = "Purged %d tools: " % len( ids )
+ for tool_id in ids:
+ tool = get_tool( trans, tool_id )
+ message += " %s " % tool.name
+ if not tool.deleted:
+ message = "Tool '%s' has not been deleted, so it cannot be purged." % tool.name
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='browse_tools',
+ message=util.sanitize_text( message ),
+ status='error' ) )
+ # Delete ToolCategoryAssociations
+ for tca in tool.categories:
+ trans.sa_session.delete( tca )
+ # Delete ToolEventAssociations and associated events
+ for tea in tool.events:
+ event = tea.event
+ trans.sa_session.delete( event )
+ trans.sa_session.delete( tea )
+ # Delete the tool
+ trans.sa_session.delete( tool )
+ trans.sa_session.flush()
+ trans.response.send_redirect( web.url_for( controller='admin',
+ action='browse_tools',
+ message=util.sanitize_text( message ),
+ status='done' ) )
@web.expose
@web.require_admin
def edit_category( self, trans, **kwd ):
params = util.Params( kwd )
- webapp = params.get( 'webapp', 'galaxy' )
message = util.restore_text( params.get( 'message', '' ) )
status = params.get( 'status', 'done' )
id = params.get( 'id', None )
@@ -559,7 +675,6 @@ class AdminController( BaseController, A
message = "No category ids received for editing"
trans.response.send_redirect( web.url_for( controller='admin',
action='manage_categories',
- webapp=webapp,
message=message,
status='error' ) )
category = get_category( trans, id )
@@ -582,25 +697,21 @@ class AdminController( BaseController, A
message = "The information has been saved for category '%s'" % ( category.name )
return trans.response.send_redirect( web.url_for( controller='admin',
action='manage_categories',
- webapp=webapp,
message=util.sanitize_text( message ),
status='done' ) )
return trans.fill_template( '/webapps/community/category/edit_category.mako',
category=category,
- webapp=webapp,
message=message,
status=status )
@web.expose
@web.require_admin
def mark_category_deleted( self, trans, **kwd ):
params = util.Params( kwd )
- webapp = params.get( 'webapp', 'galaxy' )
id = kwd.get( 'id', None )
if not id:
message = "No category ids received for deleting"
trans.response.send_redirect( web.url_for( controller='admin',
action='manage_categories',
- webapp=webapp,
message=message,
status='error' ) )
ids = util.listify( id )
@@ -613,20 +724,17 @@ class AdminController( BaseController, A
message += " %s " % category.name
trans.response.send_redirect( web.url_for( controller='admin',
action='manage_categories',
- webapp=webapp,
message=util.sanitize_text( message ),
status='done' ) )
@web.expose
@web.require_admin
def undelete_category( self, trans, **kwd ):
params = util.Params( kwd )
- webapp = params.get( 'webapp', 'galaxy' )
id = kwd.get( 'id', None )
if not id:
message = "No category ids received for undeleting"
trans.response.send_redirect( web.url_for( controller='admin',
action='manage_categories',
- webapp=webapp,
message=message,
status='error' ) )
ids = util.listify( id )
@@ -638,7 +746,6 @@ class AdminController( BaseController, A
message = "Category '%s' has not been deleted, so it cannot be undeleted." % category.name
trans.response.send_redirect( web.url_for( controller='admin',
action='manage_categories',
- webapp=webapp,
message=util.sanitize_text( message ),
status='error' ) )
category.deleted = False
@@ -649,7 +756,6 @@ class AdminController( BaseController, A
message = "Undeleted %d categories: %s" % ( count, undeleted_categories )
trans.response.send_redirect( web.url_for( controller='admin',
action='manage_categories',
- webapp=webapp,
message=util.sanitize_text( message ),
status='done' ) )
@web.expose
@@ -659,13 +765,11 @@ class AdminController( BaseController, A
# Purging a deleted Category deletes all of the following from the database:
# - ToolCategoryAssociations where category_id == Category.id
params = util.Params( kwd )
- webapp = params.get( 'webapp', 'galaxy' )
id = kwd.get( 'id', None )
if not id:
message = "No category ids received for purging"
trans.response.send_redirect( web.url_for( controller='admin',
action='manage_categories',
- webapp=webapp,
message=util.sanitize_text( message ),
status='error' ) )
ids = util.listify( id )
@@ -676,7 +780,6 @@ class AdminController( BaseController, A
message = "Category '%s' has not been deleted, so it cannot be purged." % category.name
trans.response.send_redirect( web.url_for( controller='admin',
action='manage_categories',
- webapp=webapp,
message=util.sanitize_text( message ),
status='error' ) )
# Delete ToolCategoryAssociations
@@ -686,7 +789,6 @@ class AdminController( BaseController, A
message += " %s " % category.name
trans.response.send_redirect( web.url_for( controller='admin',
action='manage_categories',
- webapp=webapp,
message=util.sanitize_text( message ),
status='done' ) )
1
0
galaxy-dist commit 692458d15b92: trackster: Track browsers can now be embedded in Galaxy Pages as multiple browsers can now exist at the same time after refactoring. Display code is now separated from editor code. Some scrolling bugs have been fixed as well.
by commits-noreply@bitbucket.org 16 Jul '10
by commits-noreply@bitbucket.org 16 Jul '10
16 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Kanwei Li <kanwei(a)gmail.com>
# Date 1278029069 14400
# Node ID 692458d15b9211ba7e6569e07b350ef2c3c9757a
# Parent e7678fd94340e969552c8cea825bc2a2cbace68e
trackster: Track browsers can now be embedded in Galaxy Pages as multiple browsers can now exist at the same time after refactoring. Display code is now separated from editor code. Some scrolling bugs have been fixed as well.
--- /dev/null
+++ b/templates/visualization/item_content.mako
@@ -0,0 +1,3 @@
+<%namespace file="/visualization/display.mako" import="*" />
+
+${render_item( item, item_data )}
--- a/templates/visualization/display.mako
+++ b/templates/visualization/display.mako
@@ -1,169 +1,23 @@
<%inherit file="/display_base.mako"/><%def name="javascripts()">
+ <% config = item_data %>
${parent.javascripts()}
- ${h.js( "jquery.event.drag", "jquery.autocomplete", "jquery.mousewheel", "trackster", "ui.core", "ui.sortable" )}
+ ${h.js( "jquery.event.drag", "jquery.autocomplete", "jquery.mousewheel", "trackster" )}
- ## HACK: set config as item_data.
- <% config = item_data %>
-
- ## TODO: Copied from browser.mako -- probably should create JS file for visualization code and include visualization JS above.
<script type="text/javascript">
-
- ## JG: add controller name.
- var data_url = "${h.url_for( controller='/tracks', action='data' )}";
var view;
-
- $(function() {
-
- %if config:
- view = new View( "${config.get('chrom')}", "${config.get('title') | h}", "${config.get('vis_id')}", "${config.get('dbkey')}" );
- %for track in config.get('tracks'):
- view.add_track(
- new ${track["track_type"]}( "${track['name'] | h}", ${track['dataset_id']}, ${track['prefs']} )
- );
- %endfor
- init();
- %else:
- continue_fn = function() {
- view = new View( undefined, $("#new-title").val(), undefined, $("#new-dbkey").val() );
- init();
- hide_modal();
- };
- $.ajax({
- url: "${h.url_for( action='new_browser' )}",
- data: {},
- error: function() { alert( "Couldn't create new browser" ) },
- success: function(form_html) {
- show_modal("New Track Browser", form_html, {
- "Cancel": function() { window.location = "/"; },
- "Continue": function() { $(document).trigger("convert_dbkeys"); continue_fn(); }
- });
- $("#new-title").focus();
- replace_big_select_inputs();
- }
- });
- %endif
-
- // Execute this when everything is ready
- function init() {
- $("ul#sortable-ul").sortable({
- update: function(event, ui) {
- for (var track_id in view.tracks) {
- var track = view.tracks[track_id];
- }
- }
- });
-
- $(document).bind( "redraw", function( e ) {
- view.redraw();
- });
-
- $("#content").bind("mousewheel", function( e, delta ) {
- if (delta > 0) {
- view.zoom_in(e.pageX, $("#viewport-container"));
- } else {
- view.zoom_out();
- }
- e.preventDefault();
- });
-
- $("#content").bind("dblclick", function( e ) {
- view.zoom_in(e.pageX, $("#viewport-container"));
- });
-
- // To let the overview box be draggable
- $("#overview-box").bind("dragstart", function( e ) {
- this.current_x = e.offsetX;
- }).bind("drag", function( e ) {
- var delta = e.offsetX - this.current_x;
- this.current_x = e.offsetX;
-
- var delta_chrom = Math.round(delta / $(document).width() * view.span);
- view.center += delta_chrom;
- view.redraw();
- });
-
- // To adjust the size of the viewport to fit the fixed-height footer
- var refresh = function( e ) {
- $("#viewport-container").height( $(window).height() - 120 );
- $("#nav-container").width( $("#center").width() );
- view.redraw();
- };
- $(window).bind( "resize", function(e) { refresh(e); } );
- $("#right-border").bind( "click", function(e) { refresh(e); } );
- $("#right-border").bind( "dragend", function(e) { refresh(e); } );
- $(window).trigger( "resize" );
-
- $("#viewport-container").bind( "dragstart", function( e ) {
- this.original_low = view.low;
- this.current_height = e.clientY;
- this.current_x = e.offsetX;
- }).bind( "drag", function( e ) {
- var container = $(this);
- var delta = e.offsetX - this.current_x;
- var new_scroll = container.scrollTop() - (e.clientY - this.current_height);
- if ( new_scroll < container.get(0).scrollHeight - container.height() ) {
- container.scrollTop(new_scroll);
- }
- this.current_height = e.clientY;
- this.current_x = e.offsetX;
-
- var delta_chrom = Math.round(delta / $(document).width() * (view.high - view.low));
- view.center -= delta_chrom;
- view.redraw();
- });
-
- ## JG: Removed 'add-track' init code.
-
- ## JG: Removed 'save-button' init code
-
- view.add_label_track( new LabelTrack( $("#top-labeltrack" ) ) );
- view.add_label_track( new LabelTrack( $("#nav-labeltrack" ) ) );
-
- $.ajax({
- ## JG: added controller name
- url: "${h.url_for( controller='/tracks', action='chroms' )}",
- data: { vis_id: view.vis_id },
- dataType: "json",
- success: function ( data ) {
- view.chrom_data = data;
- var chrom_options = '<option value="">Select Chrom/Contig</option>';
- for (i in data) {
- var chrom = data[i]['chrom']
- chrom_options += '<option value="' + chrom + '">' + chrom + '</option>';
- }
- $("#chrom").html(chrom_options);
- $("#chrom").bind( "change", function () {
- view.chrom = $("#chrom").val();
- var found = $.grep(view.chrom_data, function(v, i) {
- return v.chrom === view.chrom;
- })[0];
- view.max_high = found.len;
- view.reset();
- view.redraw(true);
-
- for (var track_id in view.tracks) {
- var track = view.tracks[track_id];
- if (track.init) {
- track.init();
- }
- }
- view.redraw();
- });
- },
- error: function() {
- alert( "Could not load chroms for this dbkey:", view.dbkey );
- }
- });
-
- ## JG: Removed function sidebar_box() and sidebar init code.
-
- $(window).trigger("resize");
- };
-
- });
-
+ // To adjust the size of the viewport to fit the fixed-height footer
+ var refresh = function( e ) {
+ if (view !== undefined) {
+ view.viewport_container.height( $(window).height() - 100 );
+ view.nav_container.width( $("#center").width() );
+ view.redraw();
+ }
+ };
+ $(window).bind( "resize", function(e) { refresh(e); } );
+ $("#right-border").bind( "click dragend", function(e) { refresh(e); } );
+ $(window).trigger( "resize" );
</script></%def>
@@ -171,60 +25,45 @@
<%def name="stylesheets()">
${parent.stylesheets()}
- ${h.css( "history" )}
- <link rel="stylesheet" type="text/css" href="${h.url_for('/static/trackster.css')}" />
-
<style type="text/css">
- ul#sortable-ul {
- list-style: none;
- padding: 0;
- margin: 5px;
+ .nav-container {
+ position: fixed;
+ width: 100%;
+ left: 0;
+ bottom: 0;
}
- ul#sortable-ul li {
- display: block;
- margin: 5px 0;
- background: #eee;
+ .page-body {
+ padding: 0px;
}
+
</style></%def>
+<%def name="render_item_header( item )">
+ ## Don't need to show header
+</%def>
+
<%def name="render_item_links( visualization )">
- ## TODO
+
</%def><%def name="render_item( visualization, config )">
- <br><br>
- ## Copied from center_panel() in browser.mako -- probably need to create visualization_common.mako to render view.
- <div id="content">
- <div id="top-labeltrack"></div>
- <div id="viewport-container" style="overflow-x: hidden; overflow-y: auto;">
- <div id="viewport"></div>
- </div>
- </div>
- <div id="nav-container" style="width:100%;">
- <div id="nav-labeltrack"></div>
- <div id="nav">
- <div id="overview">
- <div id="overview-viewport">
- <div id="overview-box"></div>
- </div>
- </div>
- <div id="nav-controls">
- <form action="#">
- <select id="chrom" name="chrom" style="width: 15em;">
- <option value="">Loading</option>
- </select>
- <input id="low" size="12" />:<input id="high" size="12" />
- <input type="hidden" name="id" value="${config.get('vis_id', '')}" />
- <a href="#" onclick="javascript:view.zoom_in();view.redraw();">
- <img src="${h.url_for('/static/images/fugue/magnifier-zoom.png')}" />
- </a>
- <a href="#" onclick="javascript:view.zoom_out();view.redraw();">
- <img src="${h.url_for('/static/images/fugue/magnifier-zoom-out.png')}" />
- </a>
- </form>
- <div id="debug" style="float: right"></div>
- </div>
- </div>
- </div>
+ <div id="${visualization.id}"></div>
+
+ <script type="text/javascript">
+
+ var data_url = "${h.url_for( controller='/tracks', action='data' )}",
+ reference_url = "${h.url_for( controller='/tracks', action='reference' )}",
+ chrom_url = "${h.url_for( controller='/tracks', action='chroms' )}",
+ view;
+
+ var container_element = $("#${visualization.id}");
+ view = new View( container_element, "${config.get('chrom')}", "${config.get('title') | h}", "${config.get('vis_id')}", "${config.get('dbkey')}" );
+ %for track in config.get('tracks'):
+ view.add_track(
+ new ${track["track_type"]}( "${track['name'] | h}", view, ${track['dataset_id']}, ${track['prefs']} )
+ );
+ %endfor
+
+ </script></%def>
--- a/templates/display_base.mako
+++ b/templates/display_base.mako
@@ -32,7 +32,7 @@
<%def name="javascripts()">
${parent.javascripts()}
- ${h.js( "jquery", "jquery.tipsy", "galaxy.base", "json2", "class", "jquery.jstore", "jquery.autocomplete", "autocomplete_tagging" )}
+ ${h.js( "jquery", "jquery.tipsy", "galaxy.base", "json2", "class", "jquery.jstore", "jquery.autocomplete", "autocomplete_tagging", "trackster" )}
<script type="text/javascript">
@@ -72,6 +72,8 @@
<%def name="stylesheets()">
${parent.stylesheets()}
${h.css( "autocomplete_tagging", "embed_item" )}
+ <link rel="stylesheet" type="text/css" href="${h.url_for('/static/trackster.css')}" />
+
<style type="text/css">
.page-body {
padding: 10px;
--- a/static/scripts/packed/galaxy.base.js
+++ b/static/scripts/packed/galaxy.base.js
@@ -1,1 +1,1 @@
-$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};function ensure_popup_helper(){if($("#popup-helper").length===0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function attach_popupmenu(b,d){var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scrollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return false};$(b).click(c)}function make_popupmenu(c,b){ensure_popup_helper();var a=$("<ul id='"+c.attr("id")
+"-menu'></ul>");$.each(b,function(f,e){if(e){$("<li/>").html(f).click(e).appendTo(a)}else{$("<li class='head'/>").html(f).appendTo(a)}});var d=$("<div class='popmenu-wrapper'>");d.append(a).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(c,d)}function make_popup_menus(){jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("target");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}else{if(e=="_top"){g=window.top}}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));make_popupmenu(a,c);$(this).remove();a.addClass("popup").show()})}function array_length(b){if(b.length){return b.length}var c=0;for(var a in b){c++}return c}function naturalSort(i,g){var n=/(-?[0-9\.]+)/g,j=i.toString().toLowerCase()||"",f=g.toString().toLowerCase()||"",k=String.fromCharCode(0),l=j.replace(n,k+"
$1"+k).split(k),e=f.replace(n,k+"$1"+k).split(k),d=(new Date(j)).getTime(),m=d?(new Date(f)).getTime():null;if(m){if(d<m){return -1}else{if(d>m){return 1}}}for(var h=0,c=Math.max(l.length,e.length);h<c;h++){oFxNcL=parseFloat(l[h])||l[h];oFyNcL=parseFloat(e[h])||e[h];if(oFxNcL<oFyNcL){return -1}else{if(oFxNcL>oFyNcL){return 1}}}return 0}function replace_big_select_inputs(a,b){if(!jQuery().autocomplete){return}if(a===undefined){a=20}if(b===undefined){b=3000}$("select").each(function(){var e=$(this);var h=e.find("option").length;if((h<a)||(h>b)){return}if(e.attr("multiple")==true){return}var l=e.attr("value");var c=$("<input type='text' class='text-and-autocomplete-select'></input>");c.attr("size",40);c.attr("name",e.attr("name"));c.attr("id",e.attr("id"));c.click(function(){var m=$(this).val();$(this).val("Loading...");$(this).showAllInCache();$(this).val(m);$(this).select()});var f=[];var i={};e.children("option").each(function(){var n=$(this).text();var m=$(this).attr("value
");f.push(n);i[n]=m;i[m]=m;if(m==l){c.attr("value",n)}});if(l==""||l=="?"){c.attr("value","Click to Search or Select")}if(e.attr("name")=="dbkey"){f=f.sort(naturalSort)}var g={selectFirst:false,autoFill:false,mustMatch:false,matchContains:true,max:b,minChars:0,hideForLessThanMinChars:false};c.autocomplete(f,g);e.replaceWith(c);var k=function(){var n=c.attr("value");var m=i[n];if(m!==null&&m!==undefined){c.attr("value",m)}else{if(l!=""){c.attr("value",l)}else{c.attr("value","?")}}};c.parents("form").submit(function(){k()});$(document).bind("convert_dbkeys",function(){k()});if(e.attr("refresh_on_change")=="true"){var d=e.attr("refresh_on_change_values");if(d!==undefined){d=d.split(",")}var j=function(){var o=c.attr("value");var n=i[o];if(n!==null&&n!==undefined){refresh=false;if(d!==undefined){for(var m=0;m<d.length;m++){if(n==d[m]){refresh=true;break}}}else{refresh=true}if(refresh){c.attr("value",n);c.parents("form").submit()}}};c.bind("result",j);c.keyup(function(m){if(m.key
Code===13){j()}});c.keydown(function(m){if(m.keyCode===13){return false}})}})}function async_save_text(d,f,e,a,c,h,i,g,b){if(c===undefined){c=30}if(i===undefined){i=4}$("#"+d).live("click",function(){if($("#renaming-active").length>0){return}var l=$("#"+f),k=l.text(),j;if(h){j=$("<textarea></textarea>").attr({rows:i,cols:c}).text(k)}else{j=$("<input type='text'></input>").attr({value:k,size:c})}j.attr("id","renaming-active");j.blur(function(){$(this).remove();l.show();if(b){b(j)}});j.keyup(function(n){if(n.keyCode===27){$(this).trigger("blur")}else{if(n.keyCode===13){var m={};m[a]=$(this).val();$(this).trigger("blur");$.ajax({url:e,data:m,error:function(){alert("Text editing for elt "+f+" failed")},success:function(o){l.text(o);if(b){b(j)}}})}}});if(g){g(j)}l.hide();j.insertAfter(l);j.focus();j.select();return})}function init_history_items(d,a,c){var b=function(){try{var e=$.jStore.store("history_expand_state");if(e){for(var g in e){$("#"+g+" div.historyItemBody").show()}}}c
atch(f){$.jStore.remove("history_expand_state")}if($.browser.mozilla){$("div.historyItemBody").each(function(){if(!$(this).is(":visible")){$(this).find("pre.peek").css("overflow","hidden")}})}d.each(function(){var j=this.id;var h=$(this).children("div.historyItemBody");var i=h.find("pre.peek");$(this).find(".historyItemTitleBar > .historyItemTitle").wrap("<a href='javascript:void(0);'></a>").click(function(){if(h.is(":visible")){if($.browser.mozilla){i.css("overflow","hidden")}h.slideUp("fast");if(!c){var k=$.jStore.store("history_expand_state");if(k){delete k[j];$.jStore.store("history_expand_state",k)}}}else{h.slideDown("fast",function(){if($.browser.mozilla){i.css("overflow","auto")}});if(!c){var k=$.jStore.store("history_expand_state");if(k===undefined){k={}}k[j]=true;$.jStore.store("history_expand_state",k)}}return false})});$("#top-links > a.toggle").click(function(){var h=$.jStore.store("history_expand_state");if(h===undefined){h={}}$("div.historyItemBody:visible").ea
ch(function(){if($.browser.mozilla){$(this).find("pre.peek").css("overflow","hidden")}$(this).slideUp("fast");if(h){delete h[$(this).parent().attr("id")]}});$.jStore.store("history_expand_state",h)}).show()};if(a){b()}else{$.jStore.init("galaxy");$.jStore.engineReady(function(){b()})}}function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}function reset_tool_search(a){var c=$("#galaxy_tools").contents();if(c.length==0){c=$(document)}$(this).removeClass("search_active");c.find(".toolTitle").removeClass("search_match");c.find(".toolSectionBody").hide();c.find(".toolTitle").show();c.find(".toolPanelLabel").show();c.find(".toolSectionWrapper").each(function(){if($(this).attr("id")!="recently_used_wrapper"){$(this).show()}else{if($(this).hasClass("user_pref_visible")){$(this).show()}}});c.find("#search-no-results").hide();c.find("#search-spinner").hide();if(a){var b=c.find("#tool-search-query");b.val("search tools");b.css("font-style","i
talic")}}function GalaxyAsync(a){this.url_dict={};this.log_action=(a===undefined?false:a)}GalaxyAsync.prototype.set_func_url=function(a,b){this.url_dict[a]=b};GalaxyAsync.prototype.set_user_pref=function(a,b){var c=this.url_dict[arguments.callee];if(c===undefined){return false}$.ajax({url:c,data:{pref_name:a,pref_value:b},error:function(){return false},success:function(){return true}})};GalaxyAsync.prototype.log_user_action=function(c,b,d){if(!this.log_action){return}var a=this.url_dict[arguments.callee];if(a===undefined){return false}$.ajax({url:a,data:{action:c,context:b,params:d},error:function(){return false},success:function(){return true}})};$(document).ready(function(){$("a[confirm]").click(function(){return confirm($(this).attr("confirm"))});if($.fn.tipsy){$(".tooltip").tipsy({gravity:"s"})}make_popup_menus();replace_big_select_inputs(20,1500)});
+$.fn.makeAbsolute=function(a){return this.each(function(){var b=$(this);var c=b.position();b.css({position:"absolute",marginLeft:0,marginTop:0,top:c.top,left:c.left,right:$(window).width()-(c.left+b.width())});if(a){b.remove().appendTo("body")}})};function ensure_popup_helper(){if($("#popup-helper").length===0){$("<div id='popup-helper'/>").css({background:"white",opacity:0,zIndex:15000,position:"absolute",top:0,left:0,width:"100%",height:"100%"}).appendTo("body").hide()}}function attach_popupmenu(b,d){var a=function(){d.unbind().hide();$("#popup-helper").unbind("click.popupmenu").hide()};var c=function(g){$("#popup-helper").bind("click.popupmenu",a).show();d.click(a).css({left:0,top:-1000}).show();var f=g.pageX-d.width()/2;f=Math.min(f,$(document).scrollLeft()+$(window).width()-$(d).width()-20);f=Math.max(f,$(document).scrollLeft()+20);d.css({top:g.pageY-5,left:f});return false};$(b).click(c)}function make_popupmenu(c,b){ensure_popup_helper();var a=$("<ul id='"+c.attr("id")
+"-menu'></ul>");$.each(b,function(f,e){if(e){$("<li/>").html(f).click(e).appendTo(a)}else{$("<li class='head'/>").html(f).appendTo(a)}});var d=$("<div class='popmenu-wrapper'>");d.append(a).append("<div class='overlay-border'>").css("position","absolute").appendTo("body").hide();attach_popupmenu(c,d)}function make_popup_menus(){jQuery("div[popupmenu]").each(function(){var c={};$(this).find("a").each(function(){var b=$(this).attr("confirm"),d=$(this).attr("href"),e=$(this).attr("target");c[$(this).text()]=function(){if(!b||confirm(b)){var g=window;if(e=="_parent"){g=window.parent}else{if(e=="_top"){g=window.top}}g.location=d}}});var a=$("#"+$(this).attr("popupmenu"));make_popupmenu(a,c);$(this).remove();a.addClass("popup").show()})}function array_length(b){if(b.length){return b.length}var c=0;for(var a in b){c++}return c}function naturalSort(i,g){var n=/(-?[0-9\.]+)/g,j=i.toString().toLowerCase()||"",f=g.toString().toLowerCase()||"",k=String.fromCharCode(0),l=j.replace(n,k+"
$1"+k).split(k),e=f.replace(n,k+"$1"+k).split(k),d=(new Date(j)).getTime(),m=d?(new Date(f)).getTime():null;if(m){if(d<m){return -1}else{if(d>m){return 1}}}for(var h=0,c=Math.max(l.length,e.length);h<c;h++){oFxNcL=parseFloat(l[h])||l[h];oFyNcL=parseFloat(e[h])||e[h];if(oFxNcL<oFyNcL){return -1}else{if(oFxNcL>oFyNcL){return 1}}}return 0}function replace_big_select_inputs(a,b){if(!jQuery().autocomplete){return}if(a===undefined){a=20}if(b===undefined){b=3000}$("select").each(function(){var e=$(this);var h=e.find("option").length;if((h<a)||(h>b)){return}if(e.attr("multiple")==true){return}if(e.hasClass("no-autocomplete")){return}var l=e.attr("value");var c=$("<input type='text' class='text-and-autocomplete-select'></input>");c.attr("size",40);c.attr("name",e.attr("name"));c.attr("id",e.attr("id"));c.click(function(){var m=$(this).val();$(this).val("Loading...");$(this).showAllInCache();$(this).val(m);$(this).select()});var f=[];var i={};e.children("option").each(function(){var n
=$(this).text();var m=$(this).attr("value");f.push(n);i[n]=m;i[m]=m;if(m==l){c.attr("value",n)}});if(l==""||l=="?"){c.attr("value","Click to Search or Select")}if(e.attr("name")=="dbkey"){f=f.sort(naturalSort)}var g={selectFirst:false,autoFill:false,mustMatch:false,matchContains:true,max:b,minChars:0,hideForLessThanMinChars:false};c.autocomplete(f,g);e.replaceWith(c);var k=function(){var n=c.attr("value");var m=i[n];if(m!==null&&m!==undefined){c.attr("value",m)}else{if(l!=""){c.attr("value",l)}else{c.attr("value","?")}}};c.parents("form").submit(function(){k()});$(document).bind("convert_dbkeys",function(){k()});if(e.attr("refresh_on_change")=="true"){var d=e.attr("refresh_on_change_values");if(d!==undefined){d=d.split(",")}var j=function(){var o=c.attr("value");var n=i[o];if(n!==null&&n!==undefined){refresh=false;if(d!==undefined){for(var m=0;m<d.length;m++){if(n==d[m]){refresh=true;break}}}else{refresh=true}if(refresh){c.attr("value",n);c.parents("form").submit()}}};c.bind
("result",j);c.keyup(function(m){if(m.keyCode===13){j()}});c.keydown(function(m){if(m.keyCode===13){return false}})}})}function async_save_text(d,f,e,a,c,h,i,g,b){if(c===undefined){c=30}if(i===undefined){i=4}$("#"+d).live("click",function(){if($("#renaming-active").length>0){return}var l=$("#"+f),k=l.text(),j;if(h){j=$("<textarea></textarea>").attr({rows:i,cols:c}).text(k)}else{j=$("<input type='text'></input>").attr({value:k,size:c})}j.attr("id","renaming-active");j.blur(function(){$(this).remove();l.show();if(b){b(j)}});j.keyup(function(n){if(n.keyCode===27){$(this).trigger("blur")}else{if(n.keyCode===13){var m={};m[a]=$(this).val();$(this).trigger("blur");$.ajax({url:e,data:m,error:function(){alert("Text editing for elt "+f+" failed")},success:function(o){l.text(o);if(b){b(j)}}})}}});if(g){g(j)}l.hide();j.insertAfter(l);j.focus();j.select();return})}function init_history_items(d,a,c){var b=function(){try{var e=$.jStore.store("history_expand_state");if(e){for(var g in e){$
("#"+g+" div.historyItemBody").show()}}}catch(f){$.jStore.remove("history_expand_state")}if($.browser.mozilla){$("div.historyItemBody").each(function(){if(!$(this).is(":visible")){$(this).find("pre.peek").css("overflow","hidden")}})}d.each(function(){var j=this.id;var h=$(this).children("div.historyItemBody");var i=h.find("pre.peek");$(this).find(".historyItemTitleBar > .historyItemTitle").wrap("<a href='javascript:void(0);'></a>").click(function(){if(h.is(":visible")){if($.browser.mozilla){i.css("overflow","hidden")}h.slideUp("fast");if(!c){var k=$.jStore.store("history_expand_state");if(k){delete k[j];$.jStore.store("history_expand_state",k)}}}else{h.slideDown("fast",function(){if($.browser.mozilla){i.css("overflow","auto")}});if(!c){var k=$.jStore.store("history_expand_state");if(k===undefined){k={}}k[j]=true;$.jStore.store("history_expand_state",k)}}return false})});$("#top-links > a.toggle").click(function(){var h=$.jStore.store("history_expand_state");if(h===undefined)
{h={}}$("div.historyItemBody:visible").each(function(){if($.browser.mozilla){$(this).find("pre.peek").css("overflow","hidden")}$(this).slideUp("fast");if(h){delete h[$(this).parent().attr("id")]}});$.jStore.store("history_expand_state",h)}).show()};if(a){b()}else{$.jStore.init("galaxy");$.jStore.engineReady(function(){b()})}}function commatize(b){b+="";var a=/(\d+)(\d{3})/;while(a.test(b)){b=b.replace(a,"$1,$2")}return b}function reset_tool_search(a){var c=$("#galaxy_tools").contents();if(c.length==0){c=$(document)}$(this).removeClass("search_active");c.find(".toolTitle").removeClass("search_match");c.find(".toolSectionBody").hide();c.find(".toolTitle").show();c.find(".toolPanelLabel").show();c.find(".toolSectionWrapper").each(function(){if($(this).attr("id")!="recently_used_wrapper"){$(this).show()}else{if($(this).hasClass("user_pref_visible")){$(this).show()}}});c.find("#search-no-results").hide();c.find("#search-spinner").hide();if(a){var b=c.find("#tool-search-query");b.
val("search tools");b.css("font-style","italic")}}function GalaxyAsync(a){this.url_dict={};this.log_action=(a===undefined?false:a)}GalaxyAsync.prototype.set_func_url=function(a,b){this.url_dict[a]=b};GalaxyAsync.prototype.set_user_pref=function(a,b){var c=this.url_dict[arguments.callee];if(c===undefined){return false}$.ajax({url:c,data:{pref_name:a,pref_value:b},error:function(){return false},success:function(){return true}})};GalaxyAsync.prototype.log_user_action=function(c,b,d){if(!this.log_action){return}var a=this.url_dict[arguments.callee];if(a===undefined){return false}$.ajax({url:a,data:{action:c,context:b,params:d},error:function(){return false},success:function(){return true}})};$(document).ready(function(){$("a[confirm]").click(function(){return confirm($(this).attr("confirm"))});if($.fn.tipsy){$(".tooltip").tipsy({gravity:"s"})}make_popup_menus();replace_big_select_inputs(20,1500)});
--- a/static/scripts/packed/trackster.js
+++ b/static/scripts/packed/trackster.js
@@ -1,1 +1,1 @@
-var DENSITY=200,FEATURE_LEVELS=10,DATA_ERROR="There was an error in indexing this dataset. ",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=5,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),PX_PER_CHAR=CONTEXT.measureText("A").width,RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src="/static/images/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src="/static/images/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src="/static/images/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND
_INV=CONTEXT.createPattern(right_img_inv,"repeat")};var left_img_inv=new Image();left_img_inv.src="/static/images/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};var Cache=function(a){this.num_elements=a;this.clear()};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c},clear:function(){this.obj_cache={};this.key_ary=[]}});var View=function(b,d,c,a){this.vis_id=c;this.dbkey=a;this.title=d;this.chrom=b;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_high=0;this.track_id_counter=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.reset()};$.extend(View.prototype,{add_track:function(a)
{a.view=this;a.track_id=this.track_id_counter;this.tracks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a.track_id);this.track_id_counter+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){this.has_changes=true;a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[this.tracks.indexOf(a)]},update_options:function(){this.has_changes=true;var b=$("ul#sortable-ul").sortable("toArray");for(var c in b){var e=b[c].split("_li")[0].split("track_")[1];$("#viewport").append($("#track_"+e))}for(var d in view.tracks){var a=view.tracks[d];if(a&&a.update_options){a.update_options(d)}}},reset:function(){this.low=this.max_low;this.high=this.max_high;$(".yaxislabel").remove()},redraw:function(f){var d=this.high-this.low,b=this.low,e=this.high;if(b<this.max_low){b=this.max_low}if(e>this.max_high){e=this.max_high}if(d<this.min_separation){e=b+this.min_separation}this.low=Math.floor(b);this.high=Math.ceil(e);
this.resolution=Math.pow(10,Math.ceil(Math.log((this.high-this.low)/200)/Math.LN10));this.zoom_res=Math.pow(FEATURE_LEVELS,Math.max(0,Math.ceil(Math.log(this.resolution,FEATURE_LEVELS)/Math.log(FEATURE_LEVELS))));$("#overview-box").css({left:(this.low/(this.max_high-this.max_low))*$("#overview-viewport").width(),width:Math.max(12,(this.high-this.low)/(this.max_high-this.max_low)*$("#overview-viewport").width())}).show();$("#low").val(commatize(this.low));$("#high").val(commatize(this.high));if(!f){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c]&&this.tracks[c].enabled){this.tracks[c].draw()}}for(var c=0,a=this.label_tracks.length;c<a;c++){this.label_tracks[c].draw()}}},zoom_in:function(b,c){if(this.max_high===0||this.high-this.low<this.min_separation){return}var d=this.high-this.low,e=d/2+this.low,a=(d/this.zoom_factor)/2;if(b){e=b/c.width()*(this.high-this.low)+this.low}this.low=Math.round(e-a);this.high=Math.round(e+a);this.redraw()},zoom_out:function(){if(this
.max_high===0){return}var b=this.high-this.low,c=b/2+this.low,a=(b*this.zoom_factor)/2;this.low=Math.round(c-a);this.high=Math.round(c+a);this.redraw()}});var Track=function(a,b){this.name=a;this.parent_element=b;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.header_div=$("<div class='track-header'>").text(this.name);this.content_div=$("<div class='track-content'>");this.container_div=$("<div />").addClass("track").append(this.header_div).append(this.content_div);this.parent_element.append(this.container_div)},init_each:function(c,b){var a=this;a.enabled=false;a.data_queue={};a.tile_cache.clear();a.data_cache.clear();if(!a.content_div.text()){a.content_div.text(DATA_LOADING)}a.container_div.removeClass("nodata error pending");if(a.view.chrom){$.getJSON(data_url,c,function(d){if(!d||d==="error"||d.kind==="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR);if(d.message){var f=a.view.tracks.indexOf(a);var e=$("<a href='javascript
:void(0);'></a>").attr("id",f+"_error");e.text("Click to view error");$("#"+f+"_error").live("click",function(){show_modal("Trackster Error","<pre>"+d.message+"</pre>",{Close:hide_modal})});a.content_div.append(e)}}else{if(d==="no converter"){a.container_div.addClass("error");a.content_div.text(DATA_NOCONVERTER)}else{if(d.data!==undefined&&(d.data===null||d.data.length===0)){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(d==="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.enabled=true;b(d);a.draw()}}}}})}else{a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}}});var TiledTrack=function(){this.left_offset=200};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var i=this.view.low,e=this.view.high,f=e-i,d=this.view.resolution;var k=$("<div style='position: relative;'></div>"),l=this
.content_div.width()/f,h;this.content_div.children(":first").remove();this.content_div.append(k),this.max_height=0;var a=Math.floor(i/d/DENSITY);while((a*DENSITY*d)<e){var j=this.content_div.width()+"_"+l+"_"+a;var c=this.tile_cache.get(j);if(c){var g=a*DENSITY*d;var b=(g-i)*l;if(this.left_offset){b-=this.left_offset}c.css({left:b});k.append(c);this.max_height=Math.max(this.max_height,c.height());this.content_div.css("height",this.max_height+"px")}else{this.delayed_draw(this,j,i,e,a,d,k,l)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_element){c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)}});var LabelTrack=function(a){Track.call(this,null,a);this.track_type="LabelTrack";this.hidden=true;this.container_div.addClass("label-track")};$.extend(LabelTrack.prototype,Track.prototype,{draw:
function(){var c=this.view,d=c.high-c.low,g=Math.floor(Math.pow(10,Math.floor(Math.log(d)/Math.log(10)))),a=Math.floor(c.low/g)*g,e=this.content_div.width(),b=$("<div style='position: relative; height: 1.3em;'></div>");while(a<c.high){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+commatize(a)+"</div>").css({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_div.append(b)}});var ReferenceTrack=function(){this.track_type="ReferenceTrack";Track.call(this,null,$("#top-labeltrack"));TiledTrack.call(this);this.hidden=true;this.height_px=12;this.container_div.addClass("reference-track");this.dummy_canvas=$("<canvas></canvas>").get(0).getContext("2d");this.data_queue={};this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE)};$.extend(ReferenceTrack.prototype,TiledTrack.prototype,{get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({
url:reference_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dbkey:this.view.dbkey},success:function(g){c.data_cache.set(e,g);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(f,b,j,n){var g=b*DENSITY*f,d=DENSITY*f,e=$("<canvas class='tile'></canvas>"),m=e.get(0).getContext("2d"),i=f+"_"+b;if(n>PX_PER_CHAR){if(this.data_cache.get(i)===undefined){this.get_data(f,b);return}var l=this.data_cache.get(i);if(l===null){return}e.get(0).width=Math.ceil(d*n+this.left_offset);e.get(0).height=this.height_px;e.css({position:"absolute",top:0,left:(g-this.view.low)*n+this.left_offset});for(var h=0,k=l.length;h<k;h++){var a=Math.round(h*n);m.fillText(l[h],a+this.left_offset,10)}j.append(e);return e}}});var LineTrack=function(c,a,b){this.track_type="LineTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.dataset_id=a;this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LI
NE);this.prefs={min_value:undefined,max_value:undefined,mode:"Line"};if(b.min_value!==undefined){this.prefs.min_value=b.min_value}if(b.max_value!==undefined){this.prefs.max_value=b.max_value}if(b.mode!==undefined){this.prefs.mode=b.mode}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){a.container_div.addClass("line-track");data=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=data.min;a.prefs.max_value=data.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").val(a.prefs.max_value)}a.vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_frequency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"
_minval").text(a.prefs.min_value);var d=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(a.prefs.max_value);d.css({position:"relative",top:"25px",left:"10px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+55+"px",left:"10px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,i){console.log(h,g,i)}})}},draw_tile:function(p,r,c,e){if(this.vertical_range===undefined){return}var s=r*DENSITY*p,a=DENSITY*p,b=$("<canvas class='tile'></canvas>"),v=p+"_"+r;if(this.data_cache.get(v)===undefined){this.get_data(p,r);return}var j=this.data_cache.get(v);if(j===null){return}b.css({position:"abs
olute",top:0,left:(s-this.view.low)*e});b.get(0).width=Math.ceil(a*e+this.left_offset);b.get(0).height=this.height_px;var o=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,n=this.vertical_range,t=this.total_frequency,d=this.height_px,m=this.prefs.mode;o.beginPath();if(data.length>1){var f=Math.ceil((data[1][0]-data[0][0])*e)}else{var f=10}var u,h;for(var q=0;q<data.length;q++){u=(data[q][0]-s)*e;h=data[q][1];if(m=="Intensity"){if(h===null){continue}if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/n*255);o.fillStyle="rgb("+h+","+h+","+h+")";o.fillRect(u,0,f,this.height_px)}else{if(h===null){if(k&&m==="Filled"){o.lineTo(u,d)}k=false;continue}else{if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/n*d);if(k){o.lineTo(u,h)}else{k=true;if(m==="Filled"){o.moveTo(u,d);o.lineTo(u,h)}else{o.moveTo(u,h)}}}}}if(m==="Filled"){if(k){o.lineTo(u,d)}o.fill()}else{o.stroke()}c.append(b);return b},gen_options:function(n){var a=$("<div />").addClass("for
m-row");var h="track_"+n+"_minval",l=$("<label></label>").attr("for",h).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),m=$("<input></input>").attr("id",h).val(b),k="track_"+n+"_maxval",g=$("<label></label>").attr("for",k).text("Max value:"),j=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",k).val(j),e="track_"+n+"_mode",d=$("<label></label>").attr("for",e).text("Display mode:"),i=(this.prefs.mode===undefined?"Line":this.prefs.mode),c=$('<select id="'+e+'"><option value="Line" id="mode_Line">Line</option><option value="Filled" id="mode_Filled">Filled</option><option value="Intensity" id="mode_Intensity">Intensity</option></select>');c.children("#mode_"+i).attr("selected","selected");return a.append(l).append(m).append(g).append(f).append(d).append(c)},update_options:function(d){var a=$("#track_"+d+"_minval").val(),c=$("#track_"+d+"_maxval").val(),b=$("#track_"+d+"_mode option:selected").val();if(a!==
this.prefs.min_value||c!==this.prefs.max_value||b!==this.prefs.mode){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(c);this.prefs.mode=b;this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+d+"_minval").text(this.prefs.min_value);$("#linetrack_"+d+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(c,a,b){this.track_type="FeatureTrack";Track.call(this,c,$("#viewport"));TiledTrack.call(this);this.height_px=100;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=3;this.default_font="9px Monaco, Lucida Console, monospace";this.inc_slots={};this.data_queue={};this.s_e_by_tile={};this.tile_cache=new Cache(CACHED_TILES_FEATURE);this.data_cache=new Cache(20);this.prefs={block_color:"black",label_color:"black",show_counts:false};if(b.block_color!==undef
ined){this.prefs.block_color=b.block_color}if(b.label_color!==undefined){this.prefs.label_color=b.label_color}if(b.show_counts!==undefined){this.prefs.show_counts=b.show_counts}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.max_low+"_"+a.view.max_high;a.mode="Auto";if(a.mode_div){a.mode_div.remove()}this.init_each({low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom,resolution:this.view.resolution},function(d){a.mode_div=$("<div class='right-float menubutton popup' />").text("Display Mode");a.header_div.append(a.mode_div);a.mode="Auto";var c=function(e){a.mode_div.text(e);a.mode=e;a.tile_cache.clear();a.draw()};make_popupmenu(a.mode_div,{Auto:function(){c("Auto")},Dense:function(){c("Dense")},Squish:function(){c("Squish")},Pack:function(){c("Pack")}});a.data_cache.set(b,d);a.draw()})},get_data:function(a,d){var b=this,c=a+"_"+d;if(!b.data_queue[c]){b.data_queue[c]=true;$.getJSON(data_url,{chrom:b.vie
w.chrom,low:a,high:d,dataset_id:b.dataset_id,resolution:this.view.resolution,mode:this.mode},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,h,c,r){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=1/a;this.inc_slots[a].mode=r;this.s_e_by_tile[a]={}}var n=this.inc_slots[a].w_scale,z=[],l=0,b=$("<canvas></canvas>").get(0).getContext("2d"),o=this.view.max_low;var B=[];if(this.inc_slots[a].mode!==r){delete this.inc_slots[a];this.inc_slots[a]={mode:r,w_scale:n};delete this.s_e_by_tile[a];this.s_e_by_tile[a]={}}for(var w=0,x=h.length;w<x;w++){var g=h[w],m=g[0];if(this.inc_slots[a][m]!==undefined){l=Math.max(l,this.inc_slots[a][m]);B.push(this.inc_slots[a][m])}else{z.push(w)}}for(var w=0,x=z.length;w<x;w++){var g=h[z[w]],m=g[0],s=g[1],d=g[2],q=g[3],e=Math.floor((s-o)*n),f=Math.ceil((d-o)*n);if(q!==undefined&&!c){var t=b.measureText(q).width;if(e-t<0){f+=t}else{e-=t}}var v=0;while(true){var p=true;if(this.s_e_by
_tile[a][v]!==undefined){for(var u=0,A=this.s_e_by_tile[a][v].length;u<A;u++){var y=this.s_e_by_tile[a][v][u];if(f>y[0]&&e<y[1]){p=false;break}}}if(p){if(this.s_e_by_tile[a][v]===undefined){this.s_e_by_tile[a][v]=[]}this.s_e_by_tile[a][v].push([e,f]);this.inc_slots[a][m]=v;l=Math.max(l,v);break}v++}}return l},rect_or_text:function(m,n,f,l,b,d,j,e,h){m.textAlign="center";var i=Math.round(n/2);if((this.mode==="Pack"||this.mode==="Auto")&&d!==undefined&&n>PX_PER_CHAR){m.fillStyle=this.prefs.block_color;m.fillRect(j,h+1,e,9);m.fillStyle="#eee";for(var g=0,k=d.length;g<k;g++){if(b+g>=f&&b+g<=l){var a=Math.floor(Math.max(0,(b+g-f)*n));m.fillText(d[g],a+this.left_offset+i,h+9)}}}else{m.fillStyle=this.prefs.block_color;m.fillRect(j,h+4,e,3)}},draw_tile:function(X,h,n,ak){var E=h*DENSITY*X,ad=(h+1)*DENSITY*X,D=DENSITY*X;var ae=E+"_"+ad;var z=this.data_cache.get(ae);if(z===undefined){this.data_queue[[E,ad]]=true;this.get_data(E,ad);return}var a=Math.ceil(D*ak),L=$("<canvas class='tile
'></canvas>"),Z=this.prefs.label_color,f=this.prefs.block_color,m=this.mode,V=(m==="Squish")||(m==="Dense")&&(m!=="Pack")||(m==="Auto"&&(z.extra_info==="no_detail")),P=this.left_offset,aj,s,al;if(z.dataset_type==="summary_tree"){s=30}else{if(m==="Dense"){s=15;al=10}else{al=(V?this.vertical_nodetail_px:this.vertical_detail_px);s=this.incremental_slots(this.view.zoom_res,z.data,V,m)*al+15;aj=this.inc_slots[this.view.zoom_res]}}L.css({position:"absolute",top:0,left:(E-this.view.low)*ak-P});L.get(0).width=a+P;L.get(0).height=s;n.parent().css("height",Math.max(this.height_px,s)+"px");var A=L.get(0).getContext("2d");A.fillStyle=f;A.font=this.default_font;A.textAlign="right";if(z.dataset_type=="summary_tree"){var K,H=55,ac=255-H,g=ac*2/3,R=z.data,C=z.max,l=z.avg;if(R.length>2){var b=Math.ceil((R[1][0]-R[0][0])*ak)}else{var b=50}for(var ag=0,w=R.length;ag<w;ag++){var T=Math.ceil((R[ag][0]-E)*ak);var S=R[ag][1];if(!S){continue}K=Math.floor(ac-(S/C)*ac);A.fillStyle="rgb("+K+","+K+","+
K+")";A.fillRect(T+P,0,b,20);if(this.prefs.show_counts){if(K>g){A.fillStyle="black"}else{A.fillStyle="#ddd"}A.textAlign="center";A.fillText(R[ag][1],T+P+(b/2),12)}}n.append(L);return L}var ai=z.data;var af=0;for(var ag=0,w=ai.length;ag<w;ag++){var M=ai[ag],J=M[0],ah=M[1],U=M[2],F=M[3];if(ah<=ad&&U>=E){var W=Math.floor(Math.max(0,(ah-E)*ak)),B=Math.ceil(Math.min(a,Math.max(0,(U-E)*ak))),Q=(m==="Dense"?0:aj[J]*al);if(z.dataset_type==="bai"){A.fillStyle=f;if(M[4] instanceof Array){var t=Math.floor(Math.max(0,(M[4][0]-E)*ak)),I=Math.ceil(Math.min(a,Math.max(0,(M[4][1]-E)*ak))),r=Math.floor(Math.max(0,(M[5][0]-E)*ak)),p=Math.ceil(Math.min(a,Math.max(0,(M[5][1]-E)*ak)));if(M[4][1]>=E&&M[4][0]<=ad){this.rect_or_text(A,ak,E,ad,M[4][0],M[4][2],t+P,I-t,Q)}if(M[5][1]>=E&&M[5][0]<=ad){this.rect_or_text(A,ak,E,ad,M[5][0],M[5][2],r+P,p-r,Q)}if(r>I){A.fillStyle="#999";A.fillRect(I+P,Q+5,r-I,1)}}else{A.fillStyle=f;this.rect_or_text(A,ak,E,ad,ah,F,W+P,B-W,Q)}if(m!=="Dense"&&!V&&ah>E){A.fillS
tyle=this.prefs.label_color;if(h===0&&W-A.measureText(F).width<0){A.textAlign="left";A.fillText(J,B+2+P,Q+8)}else{A.textAlign="right";A.fillText(J,W-2+P,Q+8)}A.fillStyle=f}}else{if(z.dataset_type==="interval_index"){if(V){A.fillRect(W+P,Q+5,B-W,1)}else{var v=M[4],O=M[5],Y=M[6],e=M[7];var u,aa,G=null,am=null;if(O&&Y){G=Math.floor(Math.max(0,(O-E)*ak));am=Math.ceil(Math.min(a,Math.max(0,(Y-E)*ak)))}if(m!=="Dense"&&F!==undefined&&ah>E){A.fillStyle=Z;if(h===0&&W-A.measureText(F).width<0){A.textAlign="left";A.fillText(F,B+2+P,Q+8)}else{A.textAlign="right";A.fillText(F,W-2+P,Q+8)}A.fillStyle=f}if(e){if(v){if(v=="+"){A.fillStyle=RIGHT_STRAND}else{if(v=="-"){A.fillStyle=LEFT_STRAND}}A.fillRect(W+P,Q,B-W,10);A.fillStyle=f}for(var ae=0,d=e.length;ae<d;ae++){var o=e[ae],c=Math.floor(Math.max(0,(o[0]-E)*ak)),N=Math.ceil(Math.min(a,Math.max((o[1]-E)*ak)));if(c>N){continue}u=5;aa=3;A.fillRect(c+P,Q+aa,N-c,u);if(G!==undefined&&!(c>am||N<G)){u=9;aa=1;var ab=Math.max(c,G),q=Math.min(N,am);A.
fillRect(ab+P,Q+aa,q-ab,u)}}}else{u=9;aa=1;A.fillRect(W+P,Q+aa,B-W,u);if(M.strand){if(M.strand=="+"){A.fillStyle=RIGHT_STRAND_INV}else{if(M.strand=="-"){A.fillStyle=LEFT_STRAND_INV}}A.fillRect(W+P,Q,B-W,10);A.fillStyle=prefs.block_color}}}}}af++}}n.append(L);return L},gen_options:function(i){var a=$("<div />").addClass("form-row");var e="track_"+i+"_block_color",k=$("<label />").attr("for",e).text("Block color:"),l=$("<input />").attr("id",e).attr("name",e).val(this.prefs.block_color),j="track_"+i+"_label_color",g=$("<label />").attr("for",j).text("Text color:"),h=$("<input />").attr("id",j).attr("name",j).val(this.prefs.label_color),f="track_"+i+"_show_count",c=$("<label />").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div />").append(b).append(c);return a.append(k).append(l).append(g).append(h).append(d)},update_options:function(e){var b=$("#tra
ck_"+e+"_block_color").val(),d=$("#track_"+e+"_label_color").val(),c=$("#track_"+e+"_mode option:selected").val(),a=$("#track_"+e+"_show_count").attr("checked");if(b!==this.prefs.block_color||d!==this.prefs.label_color||a!==this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=d;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(c,a,b){FeatureTrack.call(this,c,a,b);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
+var DENSITY=200,FEATURE_LEVELS=10,DATA_ERROR="There was an error in indexing this dataset. ",DATA_NOCONVERTER="A converter for this dataset is not installed. Please check your datatypes_conf.xml file.",DATA_NONE="No data for this chrom/contig.",DATA_PENDING="Currently indexing... please wait",DATA_LOADING="Loading data...",CACHED_TILES_FEATURE=10,CACHED_TILES_LINE=30,CACHED_DATA=5,CONTEXT=$("<canvas></canvas>").get(0).getContext("2d"),PX_PER_CHAR=CONTEXT.measureText("A").width,RIGHT_STRAND,LEFT_STRAND;var right_img=new Image();right_img.src="/static/images/visualization/strand_right.png";right_img.onload=function(){RIGHT_STRAND=CONTEXT.createPattern(right_img,"repeat")};var left_img=new Image();left_img.src="/static/images/visualization/strand_left.png";left_img.onload=function(){LEFT_STRAND=CONTEXT.createPattern(left_img,"repeat")};var right_img_inv=new Image();right_img_inv.src="/static/images/visualization/strand_right_inv.png";right_img_inv.onload=function(){RIGHT_STRAND
_INV=CONTEXT.createPattern(right_img_inv,"repeat")};var left_img_inv=new Image();left_img_inv.src="/static/images/visualization/strand_left_inv.png";left_img_inv.onload=function(){LEFT_STRAND_INV=CONTEXT.createPattern(left_img_inv,"repeat")};var Cache=function(a){this.num_elements=a;this.clear()};$.extend(Cache.prototype,{get:function(b){var a=this.key_ary.indexOf(b);if(a!=-1){this.key_ary.splice(a,1);this.key_ary.push(b)}return this.obj_cache[b]},set:function(b,c){if(!this.obj_cache[b]){if(this.key_ary.length>=this.num_elements){var a=this.key_ary.shift();delete this.obj_cache[a]}this.key_ary.push(b)}this.obj_cache[b]=c;return c},clear:function(){this.obj_cache={};this.key_ary=[]}});var View=function(a,c,e,d,b){this.container=a;this.vis_id=d;this.dbkey=b;this.title=e;this.chrom=c;this.tracks=[];this.label_tracks=[];this.max_low=0;this.max_high=0;this.track_id_counter=0;this.zoom_factor=3;this.min_separation=30;this.has_changes=false;this.init();this.reset()};$.extend(View.p
rototype,{init:function(){var c=this.container,a=this;this.content_div=$("<div/>").addClass("content").css("position","relative").appendTo(c);this.top_labeltrack=$("<div/>").addClass("top-labeltrack").appendTo(this.content_div);this.viewport_container=$("<div/>").addClass("viewport-container").addClass("viewport-container").appendTo(this.content_div);this.viewport=$("<div/>").addClass("viewport").appendTo(this.viewport_container);this.nav_container=$("<div/>").addClass("nav-container").appendTo(c);this.nav_labeltrack=$("<div/>").addClass("nav-labeltrack").appendTo(this.nav_container);this.nav=$("<div/>").addClass("nav").appendTo(this.nav_container);this.overview=$("<div/>").addClass("overview").appendTo(this.nav);this.overview_viewport=$("<div/>").addClass("overview-viewport").appendTo(this.overview);this.overview_box=$("<div/>").addClass("overview-box").appendTo(this.overview_viewport);this.nav_controls=$("<div/>").addClass("nav-controls").appendTo(this.nav);this.chrom_form
=$("<form/>").attr("action",function(){void (0)}).appendTo(this.nav_controls);this.chrom_select=$("<select/>").attr({name:"chrom"}).css("width","15em").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.chrom_form);this.low_input=$("<input/>").addClass("low").css("width","10em").appendTo(this.chrom_form);$("<span/>").text(" - ").appendTo(this.chrom_form);this.high_input=$("<input/>").addClass("high").css("width","10em").appendTo(this.chrom_form);this.hidden_input=$("<input/>").attr("type","hidden").val(this.vis_id).appendTo(this.chrom_form);this.zi_link=$("<a/>").click(function(){a.zoom_in();a.redraw()}).html('<img src="/images/fugue/magnifier-zoom.png" />').appendTo(this.chrom_form);this.zo_link=$("<a/>").click(function(){a.zoom_out();a.redraw()}).html('<img src="/images/fugue/magnifier-zoom-out.png" />').appendTo(this.chrom_form);var b=(this.vis_id!==undefined?{vis_id:this.vis_id}:{dbkey:this.dbkey});$.ajax({url:chrom_url,data:b,dataType:
"json",success:function(d){if(d.reference){a.add_label_track(new ReferenceTrack(a))}a.chrom_data=d.chrom_info;var f='<option value="">Select Chrom/Contig</option>';for(i in a.chrom_data){var e=a.chrom_data[i]["chrom"];f+='<option value="'+e+'">'+e+"</option>"}a.chrom_select.html(f);a.chrom_select.bind("change",function(){a.chrom=a.chrom_select.val();var h=$.grep(a.chrom_data,function(k,l){return k.chrom===a.chrom})[0];a.max_high=h.len;a.reset();a.redraw(true);for(var j in a.tracks){var g=a.tracks[j];if(g.init){g.init()}}a.redraw()})},error:function(){alert("Could not load chroms for this dbkey:",a.dbkey)}});this.content_div.bind("mousewheel",function(d,f){if(Math.abs(f)<0.5){return}if(f>0){a.zoom_in(d.pageX,this.viewport_container)}else{a.zoom_out()}d.preventDefault()});this.content_div.bind("dblclick",function(d){a.zoom_in(d.pageX,this.viewport_container)});this.overview_box.bind("dragstart",function(d){this.current_x=d.offsetX}).bind("drag",function(d){var g=d.offsetX-this
.current_x;this.current_x=d.offsetX;var f=Math.round(g/a.viewport_container.width()*(a.high-a.low));a.move_delta(-2*f)});this.viewport_container.bind("dragstart",function(d){this.original_low=a.low;this.current_height=d.clientY;this.current_x=d.offsetX}).bind("drag",function(g){var d=$(this);var j=g.offsetX-this.current_x;var f=d.scrollTop()-(g.clientY-this.current_height);if(f<d.get(0).scrollHeight-d.height()){d.scrollTop(f)}this.current_height=g.clientY;this.current_x=g.offsetX;var h=Math.round(j/a.viewport_container.width()*(a.high-a.low));a.move_delta(h)});this.top_labeltrack.bind("dragstart",function(d){this.drag_origin_x=d.clientX;this.drag_origin_pos=d.clientX/a.viewport_container.width()*(a.high-a.low)+a.low;this.drag_div=$("<div />").css({height:a.content_div.height(),top:"0px",position:"absolute","background-color":"#cfc",border:"1px solid #6a6",opacity:0.5}).appendTo($(this))}).bind("drag",function(j){var f=Math.min(j.clientX,this.drag_origin_x),d=Math.max(j.clien
tX,this.drag_origin_x),h=(a.high-a.low),g=a.viewport_container.width();a.low_input.val(commatize(Math.round(f/g*h)+a.low));a.high_input.val(commatize(Math.round(d/g*h)+a.low));this.drag_div.css({left:f+"px",width:(d-f)+"px"})}).bind("dragend",function(k){var f=Math.min(k.clientX,this.drag_origin_x),d=Math.max(k.clientX,this.drag_origin_x),h=(a.high-a.low),g=a.viewport_container.width(),j=a.low;a.low=Math.round(f/g*h)+j;a.high=Math.round(d/g*h)+j;this.drag_div.remove();a.redraw()});this.add_label_track(new LabelTrack(this,this.top_labeltrack));this.add_label_track(new LabelTrack(this,this.nav_labeltrack))},move_delta:function(c){var a=this;var b=a.high-a.low;if(a.low-c<a.max_low){a.low=a.max_low;a.high=a.max_low+b}else{if(a.high-c>a.max_high){a.high=a.max_high;a.low=a.max_high-b}else{a.high-=c;a.low-=c}}a.redraw()},add_track:function(a){a.view=this;a.track_id=this.track_id_counter;this.tracks.push(a);if(a.init){a.init()}a.container_div.attr("id","track_"+a.track_id);this.trac
k_id_counter+=1},add_label_track:function(a){a.view=this;this.label_tracks.push(a)},remove_track:function(a){this.has_changes=true;a.container_div.fadeOut("slow",function(){$(this).remove()});delete this.tracks[this.tracks.indexOf(a)]},update_options:function(){this.has_changes=true;var b=$("ul#sortable-ul").sortable("toArray");for(var c in b){var e=b[c].split("_li")[0].split("track_")[1];this.viewport.append($("#track_"+e))}for(var d in view.tracks){var a=view.tracks[d];if(a&&a.update_options){a.update_options(d)}}},reset:function(){this.low=this.max_low;this.high=this.max_high;this.viewport_container.find(".yaxislabel").remove()},redraw:function(f){var d=this.high-this.low,b=this.low,e=this.high;if(b<this.max_low){b=this.max_low}if(e>this.max_high){e=this.max_high}if(d<this.min_separation){e=b+this.min_separation}this.low=Math.floor(b);this.high=Math.ceil(e);this.resolution=Math.pow(10,Math.ceil(Math.log((this.high-this.low)/200)/Math.LN10));this.zoom_res=Math.pow(FEATURE_
LEVELS,Math.max(0,Math.ceil(Math.log(this.resolution,FEATURE_LEVELS)/Math.log(FEATURE_LEVELS))));this.overview_box.css({left:(this.low/(this.max_high-this.max_low))*this.overview_viewport.width(),width:Math.max(12,(this.high-this.low)/(this.max_high-this.max_low)*this.overview_viewport.width())}).show();this.low_input.val(commatize(this.low));this.high_input.val(commatize(this.high));if(!f){for(var c=0,a=this.tracks.length;c<a;c++){if(this.tracks[c]&&this.tracks[c].enabled){this.tracks[c].draw()}}for(var c=0,a=this.label_tracks.length;c<a;c++){this.label_tracks[c].draw()}}},zoom_in:function(b,c){if(this.max_high===0||this.high-this.low<this.min_separation){return}var d=this.high-this.low,e=d/2+this.low,a=(d/this.zoom_factor)/2;if(b){e=b/this.viewport_container.width()*(this.high-this.low)+this.low}this.low=Math.round(e-a);this.high=Math.round(e+a);this.redraw()},zoom_out:function(){if(this.max_high===0){return}var b=this.high-this.low,c=b/2+this.low,a=(b*this.zoom_factor)/2;
this.low=Math.round(c-a);this.high=Math.round(c+a);this.redraw()}});var Track=function(b,a,c){this.name=b;this.parent_element=c;this.view=a;this.init_global()};$.extend(Track.prototype,{init_global:function(){this.header_div=$("<div class='track-header'>").text(this.name);this.content_div=$("<div class='track-content'>");this.container_div=$("<div />").addClass("track").append(this.header_div).append(this.content_div);this.parent_element.append(this.container_div)},init_each:function(c,b){var a=this;a.enabled=false;a.data_queue={};a.tile_cache.clear();a.data_cache.clear();if(!a.content_div.text()){a.content_div.text(DATA_LOADING)}a.container_div.removeClass("nodata error pending");if(a.view.chrom){$.getJSON(data_url,c,function(d){if(!d||d==="error"||d.kind==="error"){a.container_div.addClass("error");a.content_div.text(DATA_ERROR);if(d.message){var f=a.view.tracks.indexOf(a);var e=$("<a href='javascript:void(0);'></a>").attr("id",f+"_error");e.text("Click to view error");$("
#"+f+"_error").live("click",function(){show_modal("Trackster Error","<pre>"+d.message+"</pre>",{Close:hide_modal})});a.content_div.append(e)}}else{if(d==="no converter"){a.container_div.addClass("error");a.content_div.text(DATA_NOCONVERTER)}else{if(d.data!==undefined&&(d.data===null||d.data.length===0)){a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}else{if(d==="pending"){a.container_div.addClass("pending");a.content_div.text(DATA_PENDING);setTimeout(function(){a.init()},5000)}else{a.content_div.text("");a.content_div.css("height",a.height_px+"px");a.enabled=true;b(d);a.draw()}}}}})}else{a.container_div.addClass("nodata");a.content_div.text(DATA_NONE)}}});var TiledTrack=function(){this.left_offset=200};$.extend(TiledTrack.prototype,Track.prototype,{draw:function(){var j=this.view.low,e=this.view.high,f=e-j,d=this.view.resolution;var l=$("<div style='position: relative;'></div>"),m=this.content_div.width()/f,h;this.content_div.children(":first").remove();thi
s.content_div.append(l),this.max_height=0;var a=Math.floor(j/d/DENSITY);while((a*DENSITY*d)<e){var k=this.content_div.width()+"_"+m+"_"+a;var c=this.tile_cache.get(k);if(c){var g=a*DENSITY*d;var b=(g-j)*m;if(this.left_offset){b-=this.left_offset}c.css({left:b});l.append(c);this.max_height=Math.max(this.max_height,c.height());this.content_div.css("height",this.max_height+"px")}else{this.delayed_draw(this,k,j,e,a,d,l,m)}a+=1}},delayed_draw:function(c,e,a,f,b,d,g,h){setTimeout(function(){if(!(a>c.view.high||f<c.view.low)){tile_element=c.draw_tile(d,b,g,h);if(tile_element){c.tile_cache.set(e,tile_element);c.max_height=Math.max(c.max_height,tile_element.height());c.content_div.css("height",c.max_height+"px")}}},50)}});var LabelTrack=function(a,b){Track.call(this,null,a,b);this.track_type="LabelTrack";this.hidden=true;this.container_div.addClass("label-track")};$.extend(LabelTrack.prototype,Track.prototype,{draw:function(){var c=this.view,d=c.high-c.low,g=Math.floor(Math.pow(10,Ma
th.floor(Math.log(d)/Math.log(10)))),a=Math.floor(c.low/g)*g,e=this.content_div.width(),b=$("<div style='position: relative; height: 1.3em;'></div>");while(a<c.high){var f=(a-c.low)/d*e;b.append($("<div class='label'>"+commatize(a)+"</div>").css({position:"absolute",left:f-1}));a+=g}this.content_div.children(":first").remove();this.content_div.append(b)}});var ReferenceTrack=function(a){this.track_type="ReferenceTrack";Track.call(this,null,a,a.nav_labeltrack);TiledTrack.call(this);this.hidden=true;this.height_px=12;this.container_div.addClass("reference-track");this.dummy_canvas=$("<canvas></canvas>").get(0).getContext("2d");this.data_queue={};this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE)};$.extend(ReferenceTrack.prototype,TiledTrack.prototype,{get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:reference_url,dataType:"json",data:{chrom:this.view.chrom,low:a,hi
gh:f,dbkey:this.view.dbkey},success:function(g){c.data_cache.set(e,g);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(f,b,k,o){var g=b*DENSITY*f,d=DENSITY*f,e=$("<canvas class='tile'></canvas>"),n=e.get(0).getContext("2d"),j=f+"_"+b;if(o>PX_PER_CHAR){if(this.data_cache.get(j)===undefined){this.get_data(f,b);return}var m=this.data_cache.get(j);if(m===null){return}e.get(0).width=Math.ceil(d*o+this.left_offset);e.get(0).height=this.height_px;e.css({position:"absolute",top:0,left:(g-this.view.low)*o+this.left_offset});for(var h=0,l=m.length;h<l;h++){var a=Math.round(h*o);n.fillText(m[h],a+this.left_offset,10)}k.append(e);return e}}});var LineTrack=function(d,b,a,c){this.track_type="LineTrack";Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=100;this.dataset_id=a;this.data_cache=new Cache(CACHED_DATA);this.tile_cache=new Cache(CACHED_TILES_LINE);this.prefs={min_value:undefined,max_value:undefined,mode
:"Line"};if(c.min_value!==undefined){this.prefs.min_value=c.min_value}if(c.max_value!==undefined){this.prefs.max_value=c.max_value}if(c.mode!==undefined){this.prefs.mode=c.mode}};$.extend(LineTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.tracks.indexOf(a);a.vertical_range=undefined;this.init_each({stats:true,chrom:a.view.chrom,low:null,high:null,dataset_id:a.dataset_id},function(c){a.container_div.addClass("line-track");data=c.data;if(isNaN(parseFloat(a.prefs.min_value))||isNaN(parseFloat(a.prefs.max_value))){a.prefs.min_value=data.min;a.prefs.max_value=data.max;$("#track_"+b+"_minval").val(a.prefs.min_value);$("#track_"+b+"_maxval").val(a.prefs.max_value)}a.vertical_range=a.prefs.max_value-a.prefs.min_value;a.total_frequency=data.total_frequency;$("#linetrack_"+b+"_minval").remove();$("#linetrack_"+b+"_maxval").remove();var e=$("<div />").addClass("yaxislabel").attr("id","linetrack_"+b+"_minval").text(a.prefs.min_value);var d=$("<div />").addClas
s("yaxislabel").attr("id","linetrack_"+b+"_maxval").text(a.prefs.max_value);d.css({position:"relative",top:"25px",left:"10px"});d.prependTo(a.container_div);e.css({position:"relative",top:a.height_px+55+"px",left:"10px"});e.prependTo(a.container_div)})},get_data:function(d,b){var c=this,a=b*DENSITY*d,f=(b+1)*DENSITY*d,e=d+"_"+b;if(!c.data_queue[e]){c.data_queue[e]=true;$.ajax({url:data_url,dataType:"json",data:{chrom:this.view.chrom,low:a,high:f,dataset_id:this.dataset_id,resolution:this.view.resolution},success:function(g){data=g.data;c.data_cache.set(e,data);delete c.data_queue[e];c.draw()},error:function(h,g,j){console.log(h,g,j)}})}},draw_tile:function(p,r,c,e){if(this.vertical_range===undefined){return}var s=r*DENSITY*p,a=DENSITY*p,b=$("<canvas class='tile'></canvas>"),v=p+"_"+r;if(this.data_cache.get(v)===undefined){this.get_data(p,r);return}var j=this.data_cache.get(v);if(j===null){return}b.css({position:"absolute",top:0,left:(s-this.view.low)*e});b.get(0).width=Math.
ceil(a*e+this.left_offset);b.get(0).height=this.height_px;var o=b.get(0).getContext("2d"),k=false,l=this.prefs.min_value,g=this.prefs.max_value,n=this.vertical_range,t=this.total_frequency,d=this.height_px,m=this.prefs.mode;o.beginPath();if(data.length>1){var f=Math.ceil((data[1][0]-data[0][0])*e)}else{var f=10}var u,h;for(var q=0;q<data.length;q++){u=(data[q][0]-s)*e;h=data[q][1];if(m=="Intensity"){if(h===null){continue}if(h<=l){h=l}else{if(h>=g){h=g}}h=255-Math.floor((h-l)/n*255);o.fillStyle="rgb("+h+","+h+","+h+")";o.fillRect(u,0,f,this.height_px)}else{if(h===null){if(k&&m==="Filled"){o.lineTo(u,d)}k=false;continue}else{if(h<=l){h=l}else{if(h>=g){h=g}}h=Math.round(d-(h-l)/n*d);if(k){o.lineTo(u,h)}else{k=true;if(m==="Filled"){o.moveTo(u,d);o.lineTo(u,h)}else{o.moveTo(u,h)}}}}}if(m==="Filled"){if(k){o.lineTo(u,d)}o.fill()}else{o.stroke()}c.append(b);return b},gen_options:function(o){var a=$("<div />").addClass("form-row");var h="track_"+o+"_minval",m=$("<label></label>").at
tr("for",h).text("Min value:"),b=(this.prefs.min_value===undefined?"":this.prefs.min_value),n=$("<input></input>").attr("id",h).val(b),l="track_"+o+"_maxval",g=$("<label></label>").attr("for",l).text("Max value:"),k=(this.prefs.max_value===undefined?"":this.prefs.max_value),f=$("<input></input>").attr("id",l).val(k),e="track_"+o+"_mode",d=$("<label></label>").attr("for",e).text("Display mode:"),j=(this.prefs.mode===undefined?"Line":this.prefs.mode),c=$('<select id="'+e+'"><option value="Line" id="mode_Line">Line</option><option value="Filled" id="mode_Filled">Filled</option><option value="Intensity" id="mode_Intensity">Intensity</option></select>');c.children("#mode_"+j).attr("selected","selected");return a.append(m).append(n).append(g).append(f).append(d).append(c)},update_options:function(d){var a=$("#track_"+d+"_minval").val(),c=$("#track_"+d+"_maxval").val(),b=$("#track_"+d+"_mode option:selected").val();if(a!==this.prefs.min_value||c!==this.prefs.max_value||b!==this.pre
fs.mode){this.prefs.min_value=parseFloat(a);this.prefs.max_value=parseFloat(c);this.prefs.mode=b;this.vertical_range=this.prefs.max_value-this.prefs.min_value;$("#linetrack_"+d+"_minval").text(this.prefs.min_value);$("#linetrack_"+d+"_maxval").text(this.prefs.max_value);this.tile_cache.clear();this.draw()}}});var FeatureTrack=function(d,b,a,c){this.track_type="FeatureTrack";Track.call(this,d,b,b.viewport_container);TiledTrack.call(this);this.height_px=0;this.container_div.addClass("feature-track");this.dataset_id=a;this.zo_slots={};this.show_labels_scale=0.001;this.showing_details=false;this.vertical_detail_px=10;this.vertical_nodetail_px=3;this.default_font="9px Monaco, Lucida Console, monospace";this.inc_slots={};this.data_queue={};this.s_e_by_tile={};this.tile_cache=new Cache(CACHED_TILES_FEATURE);this.data_cache=new Cache(20);this.prefs={block_color:"black",label_color:"black",show_counts:false};if(c.block_color!==undefined){this.prefs.block_color=c.block_color}if(c.labe
l_color!==undefined){this.prefs.label_color=c.label_color}if(c.show_counts!==undefined){this.prefs.show_counts=c.show_counts}};$.extend(FeatureTrack.prototype,TiledTrack.prototype,{init:function(){var a=this,b=a.view.max_low+"_"+a.view.max_high;a.mode="Auto";if(a.mode_div){a.mode_div.remove()}this.init_each({low:a.view.max_low,high:a.view.max_high,dataset_id:a.dataset_id,chrom:a.view.chrom,resolution:this.view.resolution},function(d){a.mode_div=$("<div class='right-float menubutton popup' />").text("Display Mode");a.header_div.append(a.mode_div);a.mode="Auto";var c=function(e){a.mode_div.text(e);a.mode=e;a.tile_cache.clear();a.draw()};make_popupmenu(a.mode_div,{Auto:function(){c("Auto")},Dense:function(){c("Dense")},Squish:function(){c("Squish")},Pack:function(){c("Pack")}});a.data_cache.set(b,d);a.draw()})},get_data:function(a,d){var b=this,c=a+"_"+d;if(!b.data_queue[c]){b.data_queue[c]=true;$.getJSON(data_url,{chrom:b.view.chrom,low:a,high:d,dataset_id:b.dataset_id,resolut
ion:this.view.resolution,mode:this.mode},function(e){b.data_cache.set(c,e);delete b.data_queue[c];b.draw()})}},incremental_slots:function(a,h,c,r){if(!this.inc_slots[a]){this.inc_slots[a]={};this.inc_slots[a].w_scale=1/a;this.inc_slots[a].mode=r;this.s_e_by_tile[a]={}}var n=this.inc_slots[a].w_scale,z=[],l=0,b=$("<canvas></canvas>").get(0).getContext("2d"),o=this.view.max_low;var B=[];if(this.inc_slots[a].mode!==r){delete this.inc_slots[a];this.inc_slots[a]={mode:r,w_scale:n};delete this.s_e_by_tile[a];this.s_e_by_tile[a]={}}for(var w=0,x=h.length;w<x;w++){var g=h[w],m=g[0];if(this.inc_slots[a][m]!==undefined){l=Math.max(l,this.inc_slots[a][m]);B.push(this.inc_slots[a][m])}else{z.push(w)}}for(var w=0,x=z.length;w<x;w++){var g=h[z[w]],m=g[0],s=g[1],d=g[2],q=g[3],e=Math.floor((s-o)*n),f=Math.ceil((d-o)*n);if(q!==undefined&&!c){var t=b.measureText(q).width;if(e-t<0){f+=t}else{e-=t}}var v=0;while(true){var p=true;if(this.s_e_by_tile[a][v]!==undefined){for(var u=0,A=this.s_e_by_t
ile[a][v].length;u<A;u++){var y=this.s_e_by_tile[a][v][u];if(f>y[0]&&e<y[1]){p=false;break}}}if(p){if(this.s_e_by_tile[a][v]===undefined){this.s_e_by_tile[a][v]=[]}this.s_e_by_tile[a][v].push([e,f]);this.inc_slots[a][m]=v;l=Math.max(l,v);break}v++}}return l},rect_or_text:function(n,o,f,m,b,d,k,e,h){n.textAlign="center";var j=Math.round(o/2);if((this.mode==="Pack"||this.mode==="Auto")&&d!==undefined&&o>PX_PER_CHAR){n.fillStyle=this.prefs.block_color;n.fillRect(k,h+1,e,9);n.fillStyle="#eee";for(var g=0,l=d.length;g<l;g++){if(b+g>=f&&b+g<=m){var a=Math.floor(Math.max(0,(b+g-f)*o));n.fillText(d[g],a+this.left_offset+j,h+9)}}}else{n.fillStyle=this.prefs.block_color;n.fillRect(k,h+4,e,3)}},draw_tile:function(X,h,n,ak){var E=h*DENSITY*X,ad=(h+1)*DENSITY*X,D=DENSITY*X;var ae=E+"_"+ad;var z=this.data_cache.get(ae);if(z===undefined){this.data_queue[[E,ad]]=true;this.get_data(E,ad);return}var a=Math.ceil(D*ak),L=$("<canvas class='tile'></canvas>"),Z=this.prefs.label_color,f=this.prefs.
block_color,m=this.mode,V=(m==="Squish")||(m==="Dense")&&(m!=="Pack")||(m==="Auto"&&(z.extra_info==="no_detail")),P=this.left_offset,aj,s,al;if(z.dataset_type==="summary_tree"){s=30}else{if(m==="Dense"){s=15;al=10}else{al=(V?this.vertical_nodetail_px:this.vertical_detail_px);s=this.incremental_slots(this.view.zoom_res,z.data,V,m)*al+15;aj=this.inc_slots[this.view.zoom_res]}}L.css({position:"absolute",top:0,left:(E-this.view.low)*ak-P});L.get(0).width=a+P;L.get(0).height=s;n.parent().css("height",Math.max(this.height_px,s)+"px");var A=L.get(0).getContext("2d");A.fillStyle=f;A.font=this.default_font;A.textAlign="right";if(z.dataset_type=="summary_tree"){var K,H=55,ac=255-H,g=ac*2/3,R=z.data,C=z.max,l=z.avg;if(R.length>2){var b=Math.ceil((R[1][0]-R[0][0])*ak)}else{var b=50}for(var ag=0,w=R.length;ag<w;ag++){var T=Math.ceil((R[ag][0]-E)*ak);var S=R[ag][1];if(!S){continue}K=Math.floor(ac-(S/C)*ac);A.fillStyle="rgb("+K+","+K+","+K+")";A.fillRect(T+P,0,b,20);if(this.prefs.show_coun
ts){if(K>g){A.fillStyle="black"}else{A.fillStyle="#ddd"}A.textAlign="center";A.fillText(R[ag][1],T+P+(b/2),12)}}n.append(L);return L}var ai=z.data;var af=0;for(var ag=0,w=ai.length;ag<w;ag++){var M=ai[ag],J=M[0],ah=M[1],U=M[2],F=M[3];if(ah<=ad&&U>=E){var W=Math.floor(Math.max(0,(ah-E)*ak)),B=Math.ceil(Math.min(a,Math.max(0,(U-E)*ak))),Q=(m==="Dense"?0:aj[J]*al);if(z.dataset_type==="bai"){A.fillStyle=f;if(M[4] instanceof Array){var t=Math.floor(Math.max(0,(M[4][0]-E)*ak)),I=Math.ceil(Math.min(a,Math.max(0,(M[4][1]-E)*ak))),r=Math.floor(Math.max(0,(M[5][0]-E)*ak)),p=Math.ceil(Math.min(a,Math.max(0,(M[5][1]-E)*ak)));if(M[4][1]>=E&&M[4][0]<=ad){this.rect_or_text(A,ak,E,ad,M[4][0],M[4][2],t+P,I-t,Q)}if(M[5][1]>=E&&M[5][0]<=ad){this.rect_or_text(A,ak,E,ad,M[5][0],M[5][2],r+P,p-r,Q)}if(r>I){A.fillStyle="#999";A.fillRect(I+P,Q+5,r-I,1)}}else{A.fillStyle=f;this.rect_or_text(A,ak,E,ad,ah,F,W+P,B-W,Q)}if(m!=="Dense"&&!V&&ah>E){A.fillStyle=this.prefs.label_color;if(h===0&&W-A.measureTex
t(F).width<0){A.textAlign="left";A.fillText(J,B+2+P,Q+8)}else{A.textAlign="right";A.fillText(J,W-2+P,Q+8)}A.fillStyle=f}}else{if(z.dataset_type==="interval_index"){if(V){A.fillRect(W+P,Q+5,B-W,1)}else{var v=M[4],O=M[5],Y=M[6],e=M[7];var u,aa,G=null,am=null;if(O&&Y){G=Math.floor(Math.max(0,(O-E)*ak));am=Math.ceil(Math.min(a,Math.max(0,(Y-E)*ak)))}if(m!=="Dense"&&F!==undefined&&ah>E){A.fillStyle=Z;if(h===0&&W-A.measureText(F).width<0){A.textAlign="left";A.fillText(F,B+2+P,Q+8)}else{A.textAlign="right";A.fillText(F,W-2+P,Q+8)}A.fillStyle=f}if(e){if(v){if(v=="+"){A.fillStyle=RIGHT_STRAND}else{if(v=="-"){A.fillStyle=LEFT_STRAND}}A.fillRect(W+P,Q,B-W,10);A.fillStyle=f}for(var ae=0,d=e.length;ae<d;ae++){var o=e[ae],c=Math.floor(Math.max(0,(o[0]-E)*ak)),N=Math.ceil(Math.min(a,Math.max((o[1]-E)*ak)));if(c>N){continue}u=5;aa=3;A.fillRect(c+P,Q+aa,N-c,u);if(G!==undefined&&!(c>am||N<G)){u=9;aa=1;var ab=Math.max(c,G),q=Math.min(N,am);A.fillRect(ab+P,Q+aa,q-ab,u)}}}else{u=9;aa=1;A.fillRec
t(W+P,Q+aa,B-W,u);if(M.strand){if(M.strand=="+"){A.fillStyle=RIGHT_STRAND_INV}else{if(M.strand=="-"){A.fillStyle=LEFT_STRAND_INV}}A.fillRect(W+P,Q,B-W,10);A.fillStyle=prefs.block_color}}}}}af++}}n.append(L);return L},gen_options:function(j){var a=$("<div />").addClass("form-row");var e="track_"+j+"_block_color",l=$("<label />").attr("for",e).text("Block color:"),m=$("<input />").attr("id",e).attr("name",e).val(this.prefs.block_color),k="track_"+j+"_label_color",g=$("<label />").attr("for",k).text("Text color:"),h=$("<input />").attr("id",k).attr("name",k).val(this.prefs.label_color),f="track_"+j+"_show_count",c=$("<label />").attr("for",f).text("Show summary counts"),b=$('<input type="checkbox" style="float:left;"></input>').attr("id",f).attr("name",f).attr("checked",this.prefs.show_counts),d=$("<div />").append(b).append(c);return a.append(l).append(m).append(g).append(h).append(d)},update_options:function(e){var b=$("#track_"+e+"_block_color").val(),d=$("#track_"+e+"_label
_color").val(),c=$("#track_"+e+"_mode option:selected").val(),a=$("#track_"+e+"_show_count").attr("checked");if(b!==this.prefs.block_color||d!==this.prefs.label_color||a!==this.prefs.show_counts){this.prefs.block_color=b;this.prefs.label_color=d;this.prefs.show_counts=a;this.tile_cache.clear();this.draw()}}});var ReadTrack=function(d,b,a,c){FeatureTrack.call(this,d,b,a,c);this.track_type="ReadTrack";this.vertical_detail_px=10;this.vertical_nodetail_px=5};$.extend(ReadTrack.prototype,TiledTrack.prototype,FeatureTrack.prototype,{});
--- a/static/scripts/trackster.js
+++ b/static/scripts/trackster.js
@@ -69,7 +69,8 @@ var Cache = function( num_elements ) {
}
});
-var View = function( chrom, title, vis_id, dbkey ) {
+var View = function( container, chrom, title, vis_id, dbkey ) {
+ this.container = container;
this.vis_id = vis_id;
this.dbkey = dbkey;
this.title = title;
@@ -82,22 +83,184 @@ var View = function( chrom, title, vis_i
this.zoom_factor = 3;
this.min_separation = 30;
this.has_changes = false;
+ this.init();
this.reset();
};
$.extend( View.prototype, {
- add_track: function ( track ) {
+ init: function() {
+ // Create DOM elements
+ var parent_element = this.container,
+ view = this;
+
+ this.content_div = $("<div/>").addClass("content").css("position", "relative").appendTo(parent_element);
+ this.top_labeltrack = $("<div/>").addClass("top-labeltrack").appendTo(this.content_div);
+ this.viewport_container = $("<div/>").addClass("viewport-container").addClass("viewport-container").appendTo(this.content_div);
+ this.viewport = $("<div/>").addClass("viewport").appendTo(this.viewport_container);
+
+ this.nav_container = $("<div/>").addClass("nav-container").appendTo(parent_element);
+ this.nav_labeltrack = $("<div/>").addClass("nav-labeltrack").appendTo(this.nav_container);
+ this.nav = $("<div/>").addClass("nav").appendTo(this.nav_container);
+ this.overview = $("<div/>").addClass("overview").appendTo(this.nav);
+ this.overview_viewport = $("<div/>").addClass("overview-viewport").appendTo(this.overview);
+ this.overview_box = $("<div/>").addClass("overview-box").appendTo(this.overview_viewport);
+
+ this.nav_controls = $("<div/>").addClass("nav-controls").appendTo(this.nav);
+ this.chrom_form = $("<form/>").attr("action", function() { void(0); } ).appendTo(this.nav_controls);
+ this.chrom_select = $("<select/>").attr({ "name": "chrom"}).css("width", "15em").addClass("no-autocomplete").append("<option value=''>Loading</option>").appendTo(this.chrom_form);
+ this.low_input = $("<input/>").addClass("low").css("width", "10em").appendTo(this.chrom_form);
+ $("<span/>").text(" - ").appendTo(this.chrom_form);
+ this.high_input = $("<input/>").addClass("high").css("width", "10em").appendTo(this.chrom_form);
+ this.hidden_input = $("<input/>").attr("type", "hidden").val(this.vis_id).appendTo(this.chrom_form);
+ this.zi_link = $("<a/>").click(function() { view.zoom_in(); view.redraw() }).html('<img src="/images/fugue/magnifier-zoom.png" />').appendTo(this.chrom_form);
+ this.zo_link = $("<a/>").click(function() { view.zoom_out(); view.redraw() }).html('<img src="/images/fugue/magnifier-zoom-out.png" />').appendTo(this.chrom_form);;
+
+ var data_d = (this.vis_id !== undefined ? { vis_id: this.vis_id } : { dbkey: this.dbkey });
+
+ $.ajax({
+ url: chrom_url,
+ data: data_d,
+ dataType: "json",
+ success: function ( result ) {
+ if (result['reference']) {
+ view.add_label_track( new ReferenceTrack(view) );
+ }
+ view.chrom_data = result['chrom_info'];
+ var chrom_options = '<option value="">Select Chrom/Contig</option>';
+ for (i in view.chrom_data) {
+ var chrom = view.chrom_data[i]['chrom'];
+ chrom_options += '<option value="' + chrom + '">' + chrom + '</option>';
+ }
+ view.chrom_select.html(chrom_options);
+ view.chrom_select.bind( "change", function () {
+ view.chrom = view.chrom_select.val();
+ var found = $.grep(view.chrom_data, function(v, i) {
+ return v.chrom === view.chrom;
+ })[0];
+ view.max_high = found.len;
+ view.reset();
+ view.redraw(true);
+
+ for (var track_id in view.tracks) {
+ var track = view.tracks[track_id];
+ if (track.init) {
+ track.init();
+ }
+ }
+ view.redraw();
+ });
+ },
+ error: function() {
+ alert( "Could not load chroms for this dbkey:", view.dbkey );
+ }
+ });
+
+ this.content_div.bind("mousewheel", function( e, delta ) {
+ if (Math.abs(delta) < 0.5) {
+ return;
+ }
+ if (delta > 0) {
+ view.zoom_in(e.pageX, this.viewport_container);
+ } else {
+ view.zoom_out();
+ }
+ e.preventDefault();
+ });
+
+ this.content_div.bind("dblclick", function( e ) {
+ view.zoom_in(e.pageX, this.viewport_container);
+ });
+
+ // To let the overview box be draggable
+ this.overview_box.bind("dragstart", function( e ) {
+ this.current_x = e.offsetX;
+ }).bind("drag", function( e ) {
+ var delta = e.offsetX - this.current_x;
+ this.current_x = e.offsetX;
+
+ var delta_chrom = Math.round(delta / view.viewport_container.width() * (view.high - view.low) );
+ view.move_delta(-2*delta_chrom);
+ });
+
+ this.viewport_container.bind( "dragstart", function( e ) {
+ this.original_low = view.low;
+ this.current_height = e.clientY;
+ this.current_x = e.offsetX;
+ }).bind( "drag", function( e ) {
+ var container = $(this);
+ var delta = e.offsetX - this.current_x;
+ var new_scroll = container.scrollTop() - (e.clientY - this.current_height);
+ if ( new_scroll < container.get(0).scrollHeight - container.height() ) {
+ container.scrollTop(new_scroll);
+ }
+ this.current_height = e.clientY;
+ this.current_x = e.offsetX;
+
+ var delta_chrom = Math.round(delta / view.viewport_container.width() * (view.high - view.low));
+ view.move_delta(delta_chrom);
+ });
+
+ this.top_labeltrack.bind( "dragstart", function(e) {
+ this.drag_origin_x = e.clientX;
+ this.drag_origin_pos = e.clientX / view.viewport_container.width() * (view.high - view.low) + view.low;
+ this.drag_div = $("<div />").css( {
+ "height": view.content_div.height(), "top": "0px", "position": "absolute",
+ "background-color": "#cfc", "border": "1px solid #6a6", "opacity": 0.5
+ } ).appendTo( $(this) );
+ }).bind( "drag", function(e) {
+ var min = Math.min(e.clientX, this.drag_origin_x),
+ max = Math.max(e.clientX, this.drag_origin_x),
+ span = (view.high - view.low),
+ width = view.viewport_container.width();
+
+ view.low_input.val(commatize(Math.round(min / width * span) + view.low));
+ view.high_input.val(commatize(Math.round(max / width * span) + view.low));
+ this.drag_div.css( { "left": min + "px", "width": (max - min) + "px" } );
+ }).bind( "dragend", function(e) {
+ var min = Math.min(e.clientX, this.drag_origin_x),
+ max = Math.max(e.clientX, this.drag_origin_x),
+ span = (view.high - view.low),
+ width = view.viewport_container.width(),
+ old_low = view.low;
+
+ view.low = Math.round(min / width * span) + old_low;
+ view.high = Math.round(max / width * span) + old_low;
+ this.drag_div.remove();
+ view.redraw();
+ });
+
+ this.add_label_track( new LabelTrack( this, this.top_labeltrack ) );
+ this.add_label_track( new LabelTrack( this, this.nav_labeltrack ) );
+
+ },
+ move_delta: function(delta_chrom) {
+ var view = this;
+ var current_chrom_span = view.high - view.low;
+ // Check for left and right boundaries
+ if (view.low - delta_chrom < view.max_low) {
+ view.low = view.max_low;
+ view.high = view.max_low + current_chrom_span;
+ } else if (view.high - delta_chrom > view.max_high) {
+ view.high = view.max_high;
+ view.low = view.max_high - current_chrom_span;
+ } else {
+ view.high -= delta_chrom;
+ view.low -= delta_chrom;
+ }
+ view.redraw();
+ },
+ add_track: function(track) {
track.view = this;
track.track_id = this.track_id_counter;
- this.tracks.push( track );
+ this.tracks.push(track);
if (track.init) { track.init(); }
track.container_div.attr('id', 'track_' + track.track_id);
this.track_id_counter += 1;
},
- add_label_track: function ( label_track ) {
+ add_label_track: function (label_track) {
label_track.view = this;
- this.label_tracks.push( label_track );
+ this.label_tracks.push(label_track);
},
- remove_track: function( track ) {
+ remove_track: function(track) {
this.has_changes = true;
track.container_div.fadeOut('slow', function() { $(this).remove(); });
delete this.tracks[this.tracks.indexOf(track)];
@@ -107,7 +270,7 @@ var View = function( chrom, title, vis_i
var sorted = $("ul#sortable-ul").sortable('toArray');
for (var id_i in sorted) {
var id = sorted[id_i].split("_li")[0].split("track_")[1];
- $("#viewport").append( $("#track_" + id) );
+ this.viewport.append( $("#track_" + id) );
}
for (var track_id in view.tracks) {
@@ -120,7 +283,7 @@ var View = function( chrom, title, vis_i
reset: function() {
this.low = this.max_low;
this.high = this.max_high;
- $(".yaxislabel").remove();
+ this.viewport_container.find(".yaxislabel").remove();
},
redraw: function(nodraw) {
var span = this.high - this.low,
@@ -144,20 +307,20 @@ var View = function( chrom, title, vis_i
this.zoom_res = Math.pow( FEATURE_LEVELS, Math.max(0,Math.ceil( Math.log( this.resolution, FEATURE_LEVELS ) / Math.log(FEATURE_LEVELS) )));
// Overview
- $("#overview-box").css( {
- left: ( this.low / (this.max_high - this.max_low) ) * $("#overview-viewport").width(),
+ this.overview_box.css( {
+ left: ( this.low / (this.max_high - this.max_low) ) * this.overview_viewport.width(),
// Minimum width for usability
- width: Math.max( 12, (this.high - this.low)/(this.max_high - this.max_low) * $("#overview-viewport").width() )
+ width: Math.max( 12, (this.high - this.low)/(this.max_high - this.max_low) * this.overview_viewport.width() )
}).show();
- $("#low").val( commatize(this.low) );
- $("#high").val( commatize(this.high) );
+ this.low_input.val( commatize(this.low) );
+ this.high_input.val( commatize(this.high) );
if (!nodraw) {
- for ( var i = 0, len = this.tracks.length; i < len; i++ ) {
+ for (var i = 0, len = this.tracks.length; i < len; i++) {
if (this.tracks[i] && this.tracks[i].enabled) {
this.tracks[i].draw();
}
}
- for ( var i = 0, len = this.label_tracks.length; i < len; i++ ) {
+ for (var i = 0, len = this.label_tracks.length; i < len; i++) {
this.label_tracks[i].draw();
}
}
@@ -170,7 +333,7 @@ var View = function( chrom, title, vis_i
cur_center = span / 2 + this.low,
new_half = (span / this.zoom_factor) / 2;
if (point) {
- cur_center = point / container.width() * (this.high - this.low) + this.low;
+ cur_center = point / this.viewport_container.width() * (this.high - this.low) + this.low;
}
this.low = Math.round(cur_center - new_half);
this.high = Math.round(cur_center + new_half);
@@ -189,9 +352,10 @@ var View = function( chrom, title, vis_i
}
});
-var Track = function ( name, parent_element ) {
+var Track = function (name, view, parent_element) {
this.name = name;
this.parent_element = parent_element;
+ this.view = view;
this.init_global();
};
$.extend( Track.prototype, {
@@ -307,8 +471,8 @@ var TiledTrack = function() {
}
});
-var LabelTrack = function ( parent_element ) {
- Track.call( this, null, parent_element );
+var LabelTrack = function (view, parent_element) {
+ Track.call( this, null, view, parent_element );
this.track_type = "LabelTrack";
this.hidden = true;
this.container_div.addClass( "label-track" );
@@ -335,9 +499,9 @@ var LabelTrack = function ( parent_eleme
}
});
-var ReferenceTrack = function () {
+var ReferenceTrack = function (view) {
this.track_type = "ReferenceTrack";
- Track.call( this, null, $("#top-labeltrack") );
+ Track.call( this, null, view, view.nav_labeltrack );
TiledTrack.call( this );
this.hidden = true;
@@ -404,9 +568,9 @@ var ReferenceTrack = function () {
}
});
-var LineTrack = function ( name, dataset_id, prefs ) {
+var LineTrack = function ( name, view, dataset_id, prefs ) {
this.track_type = "LineTrack";
- Track.call( this, name, $("#viewport") );
+ Track.call( this, name, view, view.viewport_container );
TiledTrack.call( this );
this.height_px = 100;
@@ -624,12 +788,12 @@ var LineTrack = function ( name, dataset
}
});
-var FeatureTrack = function ( name, dataset_id, prefs ) {
+var FeatureTrack = function ( name, view, dataset_id, prefs ) {
this.track_type = "FeatureTrack";
- Track.call( this, name, $("#viewport") );
+ Track.call( this, name, view, view.viewport_container );
TiledTrack.call( this );
- this.height_px = 100;
+ this.height_px = 0;
this.container_div.addClass( "feature-track" );
this.dataset_id = dataset_id;
this.zo_slots = {};
@@ -1065,8 +1229,8 @@ var FeatureTrack = function ( name, data
}
});
-var ReadTrack = function ( name, dataset_id, prefs ) {
- FeatureTrack.call( this, name, dataset_id, prefs );
+var ReadTrack = function ( name, view, dataset_id, prefs ) {
+ FeatureTrack.call( this, name, view, dataset_id, prefs );
this.track_type = "ReadTrack";
this.vertical_detail_px = 10;
this.vertical_nodetail_px = 5;
--- a/static/scripts/galaxy.base.js
+++ b/static/scripts/galaxy.base.js
@@ -156,12 +156,18 @@ function replace_big_select_inputs(min_l
var select_elt = $(this);
// Make sure that options is within range.
var num_options = select_elt.find('option').length;
- if ( (num_options < min_length) || (num_options > max_length) )
+ if ( (num_options < min_length) || (num_options > max_length) ) {
return;
+ }
// Skip multi-select because widget cannot handle multi-select.
- if (select_elt.attr('multiple') == true)
+ if (select_elt.attr('multiple') == true) {
return;
+ }
+
+ if (select_elt.hasClass("no-autocomplete")) {
+ return;
+ }
// Replace select with text + autocomplete.
var start_value = select_elt.attr('value');
--- a/lib/galaxy/web/controllers/visualization.py
+++ b/lib/galaxy/web/controllers/visualization.py
@@ -283,7 +283,16 @@ class VisualizationController( BaseContr
@web.require_login("get item content asynchronously")
def get_item_content_async( self, trans, id ):
""" Returns item content in HTML format. """
- return "TODO: visualization content"
+
+ # Get visualization, making sure it's accessible.
+ visualization = self.get_visualization( trans, id, False, True)
+ if visualization is None:
+ raise web.httpexceptions.HTTPNotFound()
+
+ # Return content.
+ visualization_config = self.get_visualization_config( trans, visualization )
+ return trans.fill_template_mako( "visualization/item_content.mako", encoded_id=trans.security.encode_id(visualization.id),
+ item=visualization, item_data=visualization_config, content_only=True )
@web.expose
@web.require_login( "create visualizations" )
--- a/templates/tracks/browser.mako
+++ b/templates/tracks/browser.mako
@@ -15,6 +15,9 @@
${h.css( "history", "autocomplete_tagging" )}
<link rel="stylesheet" type="text/css" href="${h.url_for('/static/trackster.css')}" /><style type="text/css">
+ #center {
+ overflow: auto;
+ }
ul#sortable-ul {
list-style: none;
padding: 0;
@@ -25,6 +28,12 @@
margin: 5px 0;
background: #eee;
}
+ .nav-container {
+ position: fixed;
+ width: 100%;
+ left: 0;
+ bottom: 0;
+ }
</style></%def>
@@ -36,37 +45,7 @@
<a id="refresh-button" class="panel-header-button right-float" href="javascript:void(0);" onclick="view.update_options();return false;">Refresh</a></div></div>
-<div id="content">
- <div id="top-labeltrack" style="position: relative;"></div>
- <div id="viewport-container" style="overflow-x: hidden; overflow-y: auto;">
- <div id="viewport"></div>
- </div>
-</div>
-<div id="nav-container" style="width:100%;">
- <div id="nav-labeltrack"></div>
- <div id="nav">
- <div id="overview">
- <div id="overview-viewport">
- <div id="overview-box"></div>
- </div>
- </div>
- <div id="nav-controls">
- <form action="#">
- <select id="chrom" name="chrom" style="width: 15em;">
- <option value="">Loading</option>
- </select>
- <input id="low" size="12" />:<input id="high" size="12" />
- <input type="hidden" name="id" value="${config.get('vis_id', '')}" />
- <a href="#" onclick="javascript:view.zoom_in();view.redraw();">
- <img src="${h.url_for('/static/images/fugue/magnifier-zoom.png')}" />
- </a>
- <a href="#" onclick="javascript:view.zoom_out();view.redraw();">
- <img src="${h.url_for('/static/images/fugue/magnifier-zoom-out.png')}" />
- </a>
- </form>
- </div>
- </div>
-</div>
+
</%def><%def name="right_panel()">
@@ -86,21 +65,22 @@
<%def name="javascripts()">
${parent.javascripts()}
-${h.js( 'galaxy.base', 'galaxy.panels', "json2", "jquery", "jquery.event.drag", "jquery.autocomplete", "jquery.mousewheel", "trackster", "ui.core", "ui.sortable" )}
+${h.js( "galaxy.base", "galaxy.panels", "json2", "jquery", "jquery.event.drag", "jquery.autocomplete", "jquery.mousewheel", "trackster", "ui.core", "ui.sortable" )}
<script type="text/javascript">
- var data_url = "${h.url_for( action='data' )}";
- var reference_url = "${h.url_for( action='reference' )}";
- var view;
+ var data_url = "${h.url_for( action='data' )}",
+ reference_url = "${h.url_for( action='reference' )}",
+ chrom_url = "${h.url_for( action='chroms' )}",
+ view;
$(function() {
%if config:
- view = new View( "${config.get('chrom')}", "${config.get('title') | h}", "${config.get('vis_id')}", "${config.get('dbkey')}" );
+ view = new View( $("#center"), "${config.get('chrom')}", "${config.get('title') | h}", "${config.get('vis_id')}", "${config.get('dbkey')}" );
%for track in config.get('tracks'):
- view.add_track(
- new ${track["track_type"]}( "${track['name'] | h}", ${track['dataset_id']}, ${track['prefs']} )
+ view.add_track(
+ new ${track["track_type"]}( "${track['name'] | h}", view, ${track['dataset_id']}, ${track['prefs']} )
);
%endfor
init();
@@ -124,14 +104,22 @@
}
});
%endif
+
+ $(document).bind( "redraw", function( e ) {
+ view.redraw();
+ });
+
+ // To adjust the size of the viewport to fit the fixed-height footer
+ var refresh = function( e ) {
+ view.viewport_container.height( $(window).height() - 100 );
+ view.nav_container.width( $("#center").width() );
+ view.redraw();
+ };
+ $(window).bind( "resize", function(e) { refresh(e); } );
+ $("#right-border").bind( "click dragend", function(e) { refresh(e); } );
+ $(window).trigger( "resize" );
- window.onbeforeunload = function() {
- if ( view.has_changes ) {
- return "There are unsaved changes to your visualization which will be lost.";
- }
- };
-
- // Execute this when everything is ready
+ // Execute initializer for EDITOR specific javascript
function init() {
$("#title").text(view.title + " (" + view.dbkey + ")");
$("ul#sortable-ul").sortable({
@@ -142,68 +130,11 @@
}
});
- $(document).bind( "redraw", function( e ) {
- view.redraw();
- });
-
- $("#content").bind("mousewheel", function( e, delta ) {
- if (Math.abs(delta) < 0.5) {
- return;
+ window.onbeforeunload = function() {
+ if ( view.has_changes ) {
+ return "There are unsaved changes to your visualization which will be lost.";
}
- if (delta > 0) {
- view.zoom_in(e.pageX, $("#viewport-container"));
- } else {
- view.zoom_out();
- }
- e.preventDefault();
- });
-
- $("#content").bind("dblclick", function( e ) {
- view.zoom_in(e.pageX, $("#viewport-container"));
- });
-
- // To let the overview box be draggable
- $("#overview-box").bind("dragstart", function( e ) {
- this.current_x = e.offsetX;
- }).bind("drag", function( e ) {
- var delta = e.offsetX - this.current_x;
- this.current_x = e.offsetX;
-
- var delta_chrom = Math.round(delta / $(document).width() * view.span);
- view.high += delta_chrom;
- view.low += delta_chrom;
- view.redraw();
- });
-
- // To adjust the size of the viewport to fit the fixed-height footer
- var refresh = function( e ) {
- $("#viewport-container").height( $(window).height() - 100 );
- $("#nav-container").width( $("#center").width() );
- view.redraw();
};
- $(window).bind( "resize", function(e) { refresh(e); } );
- $("#right-border").bind( "click dragend", function(e) { refresh(e); } );
- $(window).trigger( "resize" );
-
- $("#viewport-container").bind( "dragstart", function( e ) {
- this.original_low = view.low;
- this.current_height = e.clientY;
- this.current_x = e.offsetX;
- }).bind( "drag", function( e ) {
- var container = $(this);
- var delta = e.offsetX - this.current_x;
- var new_scroll = container.scrollTop() - (e.clientY - this.current_height);
- if ( new_scroll < container.get(0).scrollHeight - container.height() ) {
- container.scrollTop(new_scroll);
- }
- this.current_height = e.clientY;
- this.current_x = e.offsetX;
-
- var delta_chrom = Math.round(delta / $("#viewport-container").width() * (view.high - view.low));
- view.high -= delta_chrom;
- view.low -= delta_chrom;
- view.redraw();
- });
// Use a popup grid to add more tracks
$("#add-track").bind( "click", function(e) {
@@ -222,20 +153,10 @@
dataType: "json",
error: function() {},
success: function(track_data) {
- var new_track;
- var td = track_data;
- switch(track_data.track_type) {
- case "LineTrack":
- new_track = new LineTrack( track_data.name, track_data.dataset_id, track_data.prefs );
- break;
- case "FeatureTrack":
- new_track = new FeatureTrack( track_data.name, track_data.dataset_id, track_data.prefs );
- break;
- case "ReadTrack":
- new_track = new ReadTrack( track_data.name, track_data.dataset_id, track_data.prefs );
- break;
- }
- view.add_track(new_track);
+ var td = track_data,
+ track_types = { "LineTrack": LineTrack, "FeatureTrack": FeatureTrack, "ReadTrack": ReadTrack };
+
+ view.add_track(new track_types[track_data.track_type]( track_data.name, view, track_data.dataset_id, track_data.prefs) );
view.has_changes = true;
sidebar_box(new_track);
}
@@ -288,80 +209,6 @@
});
});
- view.add_label_track( new LabelTrack( $("#top-labeltrack") ) );
- view.add_label_track( new LabelTrack( $("#nav-labeltrack") ) );
-
- $("#top-labeltrack").bind( "dragstart", function(e) {
- this.drag_origin_x = e.clientX;
- this.drag_origin_pos = e.clientX / $("#viewport-container").width() * (view.high - view.low) + view.low;
- this.drag_div = $("<div />").css( {
- "height": $("#viewport-container").height(), "top": "0px", "position": "absolute",
- "background-color": "#cfc", "border": "1px solid #6a6", "opacity": 0.5
- } ).appendTo( $(this) );
- }).bind( "drag", function(e) {
- var min = Math.min(e.clientX, this.drag_origin_x),
- max = Math.max(e.clientX, this.drag_origin_x),
- span = (view.high - view.low),
- width = $("#viewport-container").width();
-
- $("#low").val(commatize(Math.round(min / width * span) + view.low));
- $("#high").val(commatize(Math.round(max / width * span) + view.low));
- this.drag_div.css( { "left": min + "px", "width": (max - min) + "px" } );
- }).bind( "dragend", function(e) {
- var min = Math.min(e.clientX, this.drag_origin_x),
- max = Math.max(e.clientX, this.drag_origin_x),
- span = (view.high - view.low),
- width = $("#viewport-container").width(),
- old_low = view.low;
-
- view.low = Math.round(min / width * span) + old_low;
- view.high = Math.round(max / width * span) + old_low;
- this.drag_div.remove();
- view.redraw();
- });
-
- $.ajax({
- url: "${h.url_for( action='chroms' )}",
- %if config.get('vis_id'):
- data: { vis_id: view.vis_id },
- %else:
- data: { dbkey: view.dbkey },
- %endif
- dataType: "json",
- success: function ( result ) {
- if (result['reference']) {
- view.add_label_track( new ReferenceTrack() );
- }
- view.chrom_data = result['chrom_info'];
- var chrom_options = '<option value="">Select Chrom/Contig</option>';
- for (i in view.chrom_data) {
- var chrom = view.chrom_data[i]['chrom'];
- chrom_options += '<option value="' + chrom + '">' + chrom + '</option>';
- }
- $("#chrom").html(chrom_options);
- $("#chrom").bind( "change", function () {
- view.chrom = $("#chrom").val();
- var found = $.grep(view.chrom_data, function(v, i) {
- return v.chrom === view.chrom;
- })[0];
- view.max_high = found.len;
- view.reset();
- view.redraw(true);
-
- for (var track_id in view.tracks) {
- var track = view.tracks[track_id];
- if (track.init) {
- track.init();
- }
- }
- view.redraw();
- });
- },
- error: function() {
- alert( "Could not load chroms for this dbkey:", view.dbkey );
- }
- });
-
function sidebar_box(track) {
if (!track.hidden) {
var track_id = track.track_id,
--- a/templates/history/display.mako
+++ b/templates/history/display.mako
@@ -39,8 +39,8 @@
<%def name="render_item_links( history )"><a
- href="${h.url_for( controller='/history', action='imp', id=trans.security.encode_id(history.id) )}"
- class="icon-button import"
+ href="${h.url_for( controller='/history', action='imp', id=trans.security.encode_id(history.id) )}"
+ class="icon-button import"
## Needed to overwide initial width so that link is floated left appropriately.
style="width: 100%"
title="Import history">Import history</a>
--- a/static/trackster.css
+++ b/static/trackster.css
@@ -1,39 +1,32 @@
-#content {
- width: 100%;
-}
-
-#center {
- overflow: auto;
-}
-
-#nav-container {
- position: fixed;
- left: 0;
- bottom: 0;
+.viewport-container {
+ overflow-x: hidden;
+ overflow-y: auto;
}
/*canvas{
border-left: 1px solid green;
border-right: 1px solid red; } /* debugging */
-#nav {
+.nav {
padding: 0 0;
color:#333;font-weight:bold;
}
-#nav-controls {
+.nav-controls {
text-align: center;
background:#cccccc;
background-image:url(style/panel_header_bg.png);
background-position:top center;
background-repeat:repeat-x;
- padding-bottom: 5px;
+ padding: 2px 0;
+}
+.nav-controls input {
+ margin: 0 5px;
+}
+.nav-controls a {
+ padding: 0 0.4em;
}
-#nav-controls a {
- padding: 5px 0.4em;
-}
-
-#overview {
+.overview {
width: 100%;
margin: 0px;
color: white;
@@ -41,7 +34,7 @@
margin-bottom: -4px;
}
-#overview-viewport {
+.overview-viewport {
position: relative;
height: 14px;
/* border-top: solid #666 1px;*/
@@ -51,7 +44,7 @@
border-bottom: solid gray 1px;
margin: 5px 0;
}
-#overview-box {
+.overview-box {
position: absolute;
margin-top: 0px;
height: 14px;
@@ -59,14 +52,14 @@
/*border-style: outset;*/
}
-#viewport {
+.viewport {
/* overflow-x: hidden;*/
background-color: #fff;
/* overflow: scroll;*/
/* border-bottom: 2px solid black;*/
}
-#viewport-canvas {
+.viewport-canvas {
width: 100%;
height: 100px;
}
@@ -122,11 +115,11 @@
margin-left: 5px;
}
-#top-labeltrack {
+.top-labeltrack {
border-bottom: solid #999 1px;
}
-#nav-labeltrack {
+.nav-labeltrack {
border-top: solid #999 1px;
border-bottom: solid #999 1px;
}
1
0
16 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1277995420 14400
# Node ID 9dc53a421c64732b11637f6e5698f82b0196f772
# Parent 65618fab28b83ccfd2b0575e8ecabd1e9cd5815a
Fix user tests.
--- a/templates/webapps/galaxy/user/info.mako
+++ b/templates/webapps/galaxy/user/info.mako
@@ -69,7 +69,7 @@
<p/><div class="toolForm">
- <form name="user_info" id="user_info" action="${h.url_for( controller='user', action='new_address', user_id=user.id, admin_view=admin_view )}" method="post" >
+ <form name="user_addresses" id="user_addresses" action="${h.url_for( controller='user', action='new_address', user_id=user.id, admin_view=admin_view )}" method="post" ><div class="toolFormTitle">User Addresses</div><div class="toolFormBody">
%if user.addresses:
@@ -125,7 +125,7 @@
%if trans.app.config.enable_api:
<div class="toolForm">
- <form name="user_info" id="user_info" action="${h.url_for( controller='user', action='new_api_key', user_id=user.id, admin_view=admin_view )}" method="post" >
+ <form name="user_api_keys" id="user_api_keys" action="${h.url_for( controller='user', action='new_api_key', user_id=user.id, admin_view=admin_view )}" method="post" ><div class="toolFormTitle">Web API Key</div><div class="toolFormBody"><div class="form-row">
--- a/templates/user/reset_password.mako
+++ b/templates/user/reset_password.mako
@@ -6,7 +6,7 @@
%endif
<div class="toolForm">
- <div class="toolFormTitle">Login</div>
+ <div class="toolFormTitle">Reset Password</div><form name="reset_password" id="reset_password" action="${h.url_for( controller='user', action='reset_password' )}" method="post" ><div class="form-row"><label>Email:</label>
--- a/test/base/twilltestcase.py
+++ b/test/base/twilltestcase.py
@@ -824,8 +824,8 @@ class TwillTestCase( unittest.TestCase )
self.home()
self.visit_url( "%s/user/show_info" % self.url )
self.check_page_for_string( "Manage User Information" )
- tc.fv( "1", "email", new_email )
- tc.fv( "1", "username", new_username )
+ tc.fv( "login_info", "email", new_email )
+ tc.fv( "login_info", "username", new_username )
tc.submit( "login_info_button" )
if check_str1:
self.check_page_for_string( check_str1 )
@@ -833,9 +833,9 @@ class TwillTestCase( unittest.TestCase )
self.home()
self.visit_page( "user/show_info" )
self.check_page_for_string( "Manage User Information" )
- tc.fv( "2", "current", password )
- tc.fv( "2", "password", new_password )
- tc.fv( "2", "confirm", new_password )
+ tc.fv( "change_password", "current", password )
+ tc.fv( "change_password", "password", new_password )
+ tc.fv( "change_password", "confirm", new_password )
tc.submit( "change_password_button" )
self.check_page_for_string( 'The password has been changed.' )
def edit_user_info( self, info_values ):
@@ -843,7 +843,7 @@ class TwillTestCase( unittest.TestCase )
self.visit_page( "user/show_info" )
self.check_page_for_string( "Manage User Information" )
for index, info_value in enumerate(info_values):
- tc.fv( "3", "field_%i" % index, info_value )
+ tc.fv( "user_info", "field_%i" % index, info_value )
tc.submit( "edit_user_info_button" )
self.check_page_for_string( "The user information has been updated with the changes." )
for value in info_values:
1
0
galaxy-dist commit e7678fd94340: Restructure and document the sample config file.
by commits-noreply@bitbucket.org 16 Jul '10
by commits-noreply@bitbucket.org 16 Jul '10
16 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1277996648 14400
# Node ID e7678fd94340e969552c8cea825bc2a2cbace68e
# Parent 9dc53a421c64732b11637f6e5698f82b0196f772
Restructure and document the sample config file.
--- a/lib/galaxy/web/buildapp.py
+++ b/lib/galaxy/web/buildapp.py
@@ -154,13 +154,13 @@ def wrap_in_middleware( app, global_conf
# performance
if debug:
# Middleware to check for WSGI compliance
- if asbool( conf.get( 'use_lint', True ) ):
+ if asbool( conf.get( 'use_lint', False ) ):
from paste import lint
app = lint.make_middleware( app, conf )
log.debug( "Enabling 'lint' middleware" )
# Middleware to run the python profiler on each request
if asbool( conf.get( 'use_profile', False ) ):
- import profile
+ from paste.debug import profile
app = profile.ProfileMiddleware( app, conf )
log.debug( "Enabling 'profile' middleware" )
# Middleware that intercepts print statements and shows them on the
--- a/universe_wsgi.ini.sample
+++ b/universe_wsgi.ini.sample
@@ -1,120 +1,173 @@
+#
+# Galaxy is configured by default to be useable in a single-user development
+# environment. To tune the application for a multi-user production
+# environment, see the documentation at:
+#
+# http://bitbucket.org/galaxy/galaxy-central/wiki/Config/ProductionServer
+#
+
+# Throughout this sample configuration file, except where stated otherwise,
+# uncommented values override the default if left unset, whereas commented
+# values are set to the default value.
+# examples of many of these options are explained in more detail in the wiki:
+#
+# Config hackers are encouraged to check there before asking for help.
+
# ---- HTTP Server ----------------------------------------------------------
+# Configuration of the internal HTTP server.
+
[server:main]
+# The internal HTTP server to use. Currently only Paste is provided. This
+# option is required.
use = egg:Paste#http
-port = 8080
-host = 127.0.0.1
-use_threadpool = true
-threadpool_workers = 10
-# ---- HTTP gzip compression ----
-# If planning to run Galaxy as a production service, we recommend running Galaxy
-# through a proxy and enabling gzip compression there (instructions at
-# http://bitbucket.org/galaxy/galaxy-central/wiki/Config/ProductionServer )
-# but you may also turn on Paste's built-in gzip compressor by uncommenting the following lines
-# and also the 'filter-with = gzip' line under [app:main]. This will reduce network traffic
-# and should speed up the interface, especially the visualization module.
-# [filter:gzip]
-# use = egg:Paste#gzip
+# The port on which to listen.
+#port = 8080
-# ---- Galaxy Web Interface -------------------------------------------------
+# The address on which to listen. By default, only listen to localhost (Galaxy
+# will not be accessible over the network). Use '0.0.0.0' to listen on all
+# available network interfaces.
+#host = 127.0.0.1
+
+# Use a threadpool for the web server instead of creating a thread for each
+# request.
+use_threadpool = True
+
+# Number of threads in the web server thread pool.
+#threadpool_workers = 10
+
+# ---- Filters --------------------------------------------------------------
+
+# Filters sit between Galaxy and the HTTP server.
+
+# These filters are disabled by default. They can be enabled with
+# 'filter-with' in the [app:main] section below.
+
+# Define the gzip filter.
+[filter:gzip]
+use = egg:Paste#gzip
+
+# Define the proxy-prefix filter.
+[filter:proxy-prefix]
+use = egg:PasteDeploy#prefix
+prefix = /galaxy
+
+# ---- Galaxy ---------------------------------------------------------------
+
+# Configuration of the Galaxy application.
+
[app:main]
-# Uncomment following line to enable Paste gzip compression
-# filter-with = gzip
+# -- Application and filtering
-# Uncomment following line below to enable visualization module
-# enable_tracks = True
-
-# Specifies the factory for the universe WSGI application
+# The factory for the WSGI application. This should not be changed.
paste.app_factory = galaxy.web.buildapp:app_factory
-# By default, Galaxy uses a SQLite database found here
-database_file = database/universe.sqlite
+# If not running behind a proxy server, you may want to enable gzip compression
+# to decrease the size of data transferred over the network. If using a proxy
+# server, please enable gzip compression there instead.
+#filter-with = gzip
-# You may use a SQLAlchemy connection string to specify an external database
-# instead. PostgreSQL and MySQL are supported.
-#database_connection = postgres:///galaxy
-#database_engine_option_echo = true
-#database_engine_option_echo_pool = true
-#database_engine_option_pool_size = 10
-#database_engine_option_max_overflow = 20
+# If running behind a proxy server and Galaxy is served from a subdirectory,
+# enable the proxy-prefix filter and set the prefix in the
+# [filter:proxy-prefix] section above.
+#filter-with = proxy-prefix
-# If using MySQL, see:
-# http://rapd.wordpress.com/2008/03/02/sqlalchemy-sqlerror-operationalerror-2…
-# To handle this issue, try the following setting:
-#database_engine_option_pool_recycle = 7200
+# -- Database
-# Where dataset files are saved
-file_path = database/files
-# Temporary storage for additional datasets, this should be shared through the cluster
-new_file_path = database/tmp
+# By default, Galaxy uses a SQLite database at 'database/universe.sqlite'. You
+# may use a SQLAlchemy connection string to specify an external database
+# instead. This string takes many options which are explained in detail in the
+# config file documentation.
+#database_connection = sqlite:///./database/universe.sqlite?isolation_level=IMMEDIATE
-# Tools
-tool_config_file = tool_conf.xml
-tool_path = tools
-tool_data_path = tool-data
+# If the server logs errors about not having enough database pool connections,
+# you will want to increase these values, or consider running more Galaxy
+# processes.
+#database_engine_option_pool_size = 5
+#database_engine_option_max_overflow = 10
-# Datatype converters
-datatype_converters_config_file = datatype_converters_conf.xml
-datatype_converters_path = %(here)s/lib/galaxy/datatypes/converters
+# If using MySQL and the server logs the error "MySQL server has gone away",
+# you will want to set this to some positive value (7200 should work).
+#database_engine_option_pool_recycle = -1
-# Datatype indexers
-datatype_indexers_path = %(here)s/lib/galaxy/datatypes/indexers
-# Metadata
-set_metadata_externally = False
+# -- Files and directories
-# Session support (beaker)
-use_beaker_session = True
-session_type = file
-session_data_dir = %(here)s/database/beaker_sessions
-session_key = galaxysessions
-session_secret = changethisinproduction
+# Dataset files are stored in this directory.
+#file_path = database/files
-# Galaxy session security
-id_secret = changethisinproductiontoo
+# Temporary files are stored in this directory.
+#new_file_path = database/tmp
-# Directories of files contained in the following directory can be uploaded to
-# a library from the Admin view
-#library_import_dir = /var/opt/galaxy/import
+# Tool config file, defines what tools are available in Galaxy.
+#tool_config_file = tool_conf.xml
-# The following can be configured to allow non-admin users to upload a
-# directory of files. The configured directory must contain sub-directories
-# named the same as the non-admin user's Galaxy login ( email ). The non-admin
-# user is restricted to uploading files or sub-directories of files contained
-# in their directory.
-#user_library_import_dir = /var/opt/galaxy/import/users
+# Path to the directory containing the tools defined in the config.
+#tool_path = tools
-# The admin library upload tool may contain a box allowing admins to paste
-# filesystem paths to files and directories to add to a library. Set to True
-# to enable. Please note the security implication that this will give Galaxy
-# Admins access to anything your Galaxy user has access to.
-#allow_library_path_paste = False
+# Directory where data used by tools is located, see the samples in that
+# directory and the wiki for help:
+# http://bitbucket.org/galaxy/galaxy-central/wiki/DataIntegration
+#tool_data_path = tool-data
-# path to sendmail
-sendmail_path = /usr/sbin/sendmail
+# Datatypes config file, defines what data (file) types are available in
+# Galaxy.
+#datatypes_config_file = datatypes_conf.xml
-# Address to join mailing list
-mailing_join_addr = galaxy-user-join(a)bx.psu.edu
+# -- Mail and notification
-# For use by 'report this error' link on error-state datasets
-#smtp_server = smtp.example.org
-#error_email_to = galaxy-bugs(a)example.org
+# Galaxy sends mail for various things: Subscribing users to the mailing list
+# if they request it, emailing password resets, notification from the Galaxy
+# Sample Tracking system, and reporting dataset errors. To do this, it needs
+# to send mail through an SMTP server, which you may define here.
+#smtp_server = None
-# Use the new iframe / javascript based layout
-use_new_layout = true
+# On the user registration form, users may choose to join the mailing list.
+# This is the address of the list they'll be subscribed to.
+#mailing_join_addr = galaxy-user-join(a)bx.psu.edu
-# Comma separated list of UCSC / gbrowse / GeneTrack browsers to use for viewing
-ucsc_display_sites = main,test,archaea,ucla
-gbrowse_display_sites = wormbase,tair,modencode_worm,modencode_fly
-# Define your GeneTrack servers in tool-data/shared/genetrack/genetrack_sites.txt
-#genetrack_display_sites =
+# Datasets in an error state include a link to report the error. Those reports
+# will be sent to this address. Error reports are disabled if no address is set.
+#error_email_to = None
-# Enable the (experimental! beta!) Web API. Documentation forthcoming.
-#enable_api = False
+# -- Display sites
-# Serving static files (needed if running standalone)
+# Galaxy can display data at various external browsers. These options specify
+# which browsers should be available. URLs and builds available at these
+# browsers are defined in the specifield files.
+
+# UCSC browsers: tool-data/shared/ucsc/ucsc_build_sites.txt
+#ucsc_display_sites = main,test,archaea,ucla
+
+# GBrowse servers: tool-data/shared/gbrowse/gbrowse_build_sites.txt
+#gbrowse_display_sites = wormbase,tair,modencode_worm,modencode_fly
+
+# GeneTrack servers: tool-data/shared/genetrack/genetrack_sites.txt
+#genetrack_display_sites = main,test
+
+# -- UI Localization
+
+# Append "/{brand}" to the "Galaxy" text in the masthead.
+#brand = None
+
+# The URL linked by the "Galaxy/brand" text.
+#logo_url = /
+
+# The URL linked by the "Galaxy Wiki" link in the "Help" menu.
+#wiki_url = http://bitbucket.org/galaxy/galaxy-central/wiki
+
+# The URL linked by the "Email comments..." link in the "Help" menu.
+#bugs_email = None
+
+# The URL linked by the "How to Cite..." link in the "Help" menu.
+#citation_url = http://bitbucket.org/galaxy/galaxy-central/wiki/Citations
+
+# Serve static content, which must be enabled if you're not serving it via a
+# proxy server. These options should be self explanatory and so are not
+# documented individually. You can use these paths (or ones in the proxy
+# server) to point to your own styles.
static_enabled = True
static_cache_time = 360
static_dir = %(here)s/static/
@@ -123,94 +176,142 @@ static_favicon_dir = %(here)s/static/fav
static_scripts_dir = %(here)s/static/scripts/
static_style_dir = %(here)s/static/june_2007_style/blue
-## Leave these commented out for the defaults at the main galaxy site
-## Uncomment and adjust these to change locally configurable items in the masthead
-## for a local mirror where you are doing private software development
-##
-## Brand: appends "/[brand]" to the "Galaxy" text in the masthead
-## logo_url: replaces the default "Galaxy + brand" link url
-## wiki_url: replaces the default galaxy main wiki
-## bugs_email: replaces the default galaxy bugs email list
-##citation_url: point to a URL listing citations
-#brand = Private local mirror
-#logo_url = /
-#wiki_url = /path/to/my/local/wiki
-#bugs_email = mailto:galaxy-bugs@example.org
-#citation_url = /path/to/my/citations
+# -- Logging and Debugging
-# ---- Logging and Debugging ------------------------------------------------
+# Verbosity of console log messages. Acceptable values can be found here:
+# http://docs.python.org/library/logging.html#logging-levels
+#log_level = DEBUG
-# Verbosity of log messages
-log_level = DEBUG
+# Print database operations to the server log (warning, quite verbose!).
+#database_engine_option_echo = False
-# Log memory usage
-log_memory_usage = False
+# Print database pool operations to the server log (warning, quite verbose!).
+#database_engine_option_echo_pool = False
-# Log events
-log_events = True
+# Turn on logging of application events and some user events to the database.
+#log_events = True
-# Log user actions
-log_actions = True
+# Turn on logging of user actions to the database. Actions currently logged are
+# grid views, tool searches, and use of "recently" used tools menu. The
+# log_events and log_actions functionality will eventually be merged.
+#log_actions = True
-# Configuration for debugging middleware
+# Debug enables access to various config options useful for development and
+# debugging: use_lint, use_profile, use_printdebug and use_interactive. It
+# also causes the files used by PBS/SGE (submission script, output, and error)
+# to remain on disk after the job is complete. Debug mode is disabled if
+# commented, but is uncommented by default in the sample config.
debug = True
-use_lint = False
-# Interactive debugging - NEVER enable this on a public site
+# Check for WSGI compliance.
+#use_lint = False
+
+# Run the Python profiler on each request.
+#use_profile = False
+
+# Intercept print statements and show them on the returned page.
+#use_printdebug = True
+
+# Enable live debugging in your browser. This should NEVER be enabled on a
+# public site. Enabled in the sample config for development.
use_interactive = True
-# Write thread status periodically to 'heartbeat.log' (careful, uses disk space rapidly!)
+# Write thread status periodically to 'heartbeat.log', (careful, uses disk
+# space rapidly!). Useful to determine why your processes may be consuming a
+# lot of CPU.
#use_heartbeat = False
-# Enable the memory debugging interface (careful, negatively impacts server performance)
+# Enable the memory debugging interface (careful, negatively impacts server
+# performance).
#use_memdump = False
-# Profiling middleware (cProfile based)
-#use_profile = False
+# -- Data Libraries
-# ---- Users and Security ---------------------------------------------------
+# These library upload options are described in much more detail in the wiki:
+# http://bitbucket.org/galaxy/galaxy-central/wiki/DataLibraries/UploadingFiles
+
+# Add an option to the library upload form which allows administrators to
+# upload a directory of files.
+#library_import_dir = None
+
+# Add an option to the library upload form which allows authorized
+# non-administrators to upload a directory of files. The configured directory
+# must contain sub-directories named the same as the non-admin user's Galaxy
+# login ( email ). The non-admin user is restricted to uploading files or
+# sub-directories of files contained in their directory.
+#user_library_import_dir = None
+
+# Add an option to the admin library upload tool allowing admins to paste
+# filesystem paths to files and directories in a box, and these paths will be
+# added to a library. Set to True to enable. Please note the security
+# implication that this will give Galaxy Admins access to anything your Galaxy
+# user has access to.
+#allow_library_path_paste = False
+
+# -- Users and Security
+
+# Galaxy encodes various internal values when these values will be output in
+# some format (for example, in a URL or cookie). You should set a key to be
+# used by the algorithm that encodes and decodes these values. It can be any
+# string. If left unchanged, anyone could construct a cookie that would grant
+# them access to others' sessions.
+#id_secret = USING THE DEFAULT IS NOT SECURE!
# User authentication can be delegated to an upstream proxy server (usually
-# Apache). This is explained on the Galaxy wiki:
-#
-# http://g2.trac.bx.psu.edu/wiki/HowToInstall/ApacheProxy
-
-# Use user provided in an upstream server's $REMOTE_USER variable
+# Apache). The upstream proxy should set a REMOTE_USER header in the request.
+# Enabling remote user disables regular logins. For more information, see:
+# http://bitbucket.org/galaxy/galaxy-central/wiki/Config/ApacheProxy
#use_remote_user = False
# If use_remote_user is enabled and your external authentication
-# method just returns bare usernames, set a default mail domain
-#remote_user_maildomain = example.org
+# method just returns bare usernames, set a default mail domain to be appended
+# to usernames, to become your Galaxy usernames (email addresses).
+#remote_user_maildomain = None
-# If use_remote_user is enabled, set a logout anchor href option
-#remote_user_logout_href = /logout
+# If use_remote_user is enabled, you can set this to a URL that will log your
+# users out.
+#remote_user_logout_href = None
-# this should be a comma-separated list of valid Galaxy users
-#admin_users = user1@example.org,user2@example.org
+# Administrative users - set this to a comma-separated list of valid Galaxy
+# users (email addresses). These users will have access to the Admin section
+# of the server, and will have access to create users, groups, roles,
+# libraries, and more. For more information, see:
+# http://bitbucket.org/galaxy/galaxy-central/wiki/Admin/AdminInterface
+#admin_users = None
-# Force everyone to log in (disable anonymous access)
+# Force everyone to log in (disable anonymous access).
#require_login = False
-# Can users register new accounts?
+# Allow unregistered users to create new accounts (otherwise, they will have to
+# be created by an admin).
#allow_user_creation = True
-# Can an admin user delete user accounts?
+# Allow administrators to delete accounts.
#allow_user_deletion = False
-# Should default dataset access permissions be private for new users; default is False (datasets are public)
-new_user_dataset_access_role_default_private = False
+# By default, users' data will be public, but setting this to True will cause
+# it to be private. Does not affect existing users and data, only ones created
+# after this option is set. Users may still change their default back to
+# public.
+#new_user_dataset_access_role_default_private = False
-# ---- Job Execution --------------------------------------------------------
+# -- Beta features
+
+# Enable Galaxy's built-in visualization module, Trackster.
+#enable_tracks = False
+
+# Enable the (experimental! beta!) Web API. Documentation forthcoming.
+#enable_api = False
+
+# -- Job Execution
# If running multiple Galaxy processes, one can be designated as the job
# runner. For more information, see:
# http://bitbucket.org/galaxy/galaxy-central/wiki/Config/LoadBalancing
#enable_job_running = True
-# Number of concurrent jobs to run (local job runner)
-#local_job_queue_workers = 5
-
-# Should jobs be tracked through the database, rather than in memory
+# Should jobs be tracked through the database, rather than in memory.
+# Necessary if you're running the load balanced setup.
#track_jobs_in_database = False
# Enable job recovery (if Galaxy is restarted while cluster jobs are running,
@@ -218,47 +319,50 @@ new_user_dataset_access_role_default_pri
# running more than one Galaxy server using the same database.
#enable_job_recovery = True
-# Job queue cleanup interval in minutes. Currently only used by RoundRobin
-job_queue_cleanup_interval = 30
+# Setting metadata on job outputs to in a separate process (or if using a
+# cluster, on the cluster). Thanks to Python's Global Interpreter Lock and the
+# hefty expense that setting metadata incurs, your Galaxy process may become
+# unresponsive when this operation occurs internally.
+#set_metadata_externally = False
+
+# Number of concurrent jobs to run (local job runner)
+#local_job_queue_workers = 5
# Jobs can be killed after a certain amount of execution time. Format is in
-# hh:mm:ss. Currently only implemented for PBS. Leave commented for
-# unlimited.
-#job_walltime = 10:00:00
+# hh:mm:ss. Currently only implemented for PBS.
+#job_walltime = None
-# Clustering Galaxy is not a straightforward process and requires a lot of
-# pre-configuration. See the ClusteringGalaxy Wiki before attempting to set
-# any of these options:
-#
-# http://g2.trac.bx.psu.edu/wiki/ClusteringGalaxy
-#
-# If running normally (without a cluster), do not change anything in this
-# section.
+# Clustering Galaxy is not a straightforward process and requires some
+# pre-configuration. See the the wiki before attempting to set any of these
+# options:
+# http://bitbucket.org/galaxy/galaxy-central/wiki/Config/Cluster
# Comma-separated list of job runners to start. local is always started. If
# left commented, no jobs will be run on the cluster, even if a cluster URL is
# explicitly defined in the [galaxy:tool_runners] section below. The runners
# currently available are 'pbs' and 'sge'.
-#start_job_runners = pbs
+#start_job_runners = None
# The URL for the default runner to use when a tool doesn't explicity define a
-# runner below. For help on the cluster URL format, see the ClusteringGalaxy
-# Wiki. Leave commented if not using a cluster job runner.
-#default_cluster_job_runner = pbs:///
+# runner below.
+#default_cluster_job_runner = local:///
# The cluster runners have their own thread pools used to prepare and finish
-# jobs (so that these operations do not block normal queue operation). The
-# value here is the number of worker threads available to each runner.
+# jobs (so that these sometimes lengthy operations do not block normal queue
+# operation). The value here is the number of worker threads available to each
+# started runner.
#cluster_job_queue_workers = 3
-# The PBS options are described in detail in the Galaxy Configuration section of
-# the ClusteringGalaxy Wiki, and are only necessary when using file staging.
+# These options are only used when using file staging with PBS.
#pbs_application_server =
#pbs_stage_path =
#pbs_dataset_server =
+# ---- Tool Job Runners -----------------------------------------------------
+
# Individual per-tool job runner overrides. If not listed here, a tool will
# run with the runner defined with default_cluster_job_runner.
+
[galaxy:tool_runners]
biomart = local:///
@@ -270,8 +374,8 @@ ucsc_table_direct_archaea1 = local:///
ucsc_table_direct_test1 = local:///
upload1 = local:///
+# ---- Galaxy Message Queue -------------------------------------------------
-# Galaxy Message Queue
# Galaxy uses AMQ protocol to receive messages from external sources like
# bar code scanners. Galaxy has been tested against RabbitMQ AMQP implementation.
# For Galaxy to receive messages from a message queue the RabbitMQ server has
@@ -279,6 +383,7 @@ upload1 = local:///
# and 'port' fields should point to where the RabbitMQ server is running.
[galaxy_amqp]
+
#host = 127.0.0.1
#port = 5672
#userid = galaxy
--- a/lib/galaxy/config.py
+++ b/lib/galaxy/config.py
@@ -23,6 +23,7 @@ class ConfigurationError( Exception ):
pass
class Configuration( object ):
+ deprecated_options = ( 'database_file', )
def __init__( self, **kwargs ):
self.config_dict = kwargs
self.root = kwargs.get( 'root_dir', '.' )
@@ -31,7 +32,7 @@ class Configuration( object ):
os.umask( self.umask ) # can't get w/o set, so set it back
self.gid = os.getgid() # if running under newgrp(1) we'll need to fix the group of data created on the cluster
# Database related configuration
- self.database = resolve_path( kwargs.get( "database_file", "database/universe.d" ), self.root )
+ self.database = resolve_path( kwargs.get( "database_file", "database/universe.sqlite" ), self.root )
self.database_connection = kwargs.get( "database_connection", False )
self.database_engine_options = get_database_engine_options( kwargs )
self.database_create_tables = string_as_bool( kwargs.get( "database_create_tables", "True" ) )
@@ -68,7 +69,6 @@ class Configuration( object ):
self.output_size_limit = int( kwargs.get( 'output_size_limit', 0 ) )
self.job_walltime = kwargs.get( 'job_walltime', None )
self.admin_users = kwargs.get( "admin_users", "" )
- self.sendmail_path = kwargs.get('sendmail_path',"/usr/sbin/sendmail")
self.mailing_join_addr = kwargs.get('mailing_join_addr',"galaxy-user-join(a)bx.psu.edu")
self.error_email_to = kwargs.get( 'error_email_to', None )
self.smtp_server = kwargs.get( 'smtp_server', None )
@@ -80,8 +80,8 @@ class Configuration( object ):
self.pbs_stage_path = kwargs.get('pbs_stage_path', "" )
self.use_heartbeat = string_as_bool( kwargs.get( 'use_heartbeat', 'False' ) )
self.use_memdump = string_as_bool( kwargs.get( 'use_memdump', 'False' ) )
- self.log_actions = string_as_bool( kwargs.get( 'log_actions', 'False' ) )
- self.log_events = string_as_bool( kwargs.get( 'log_events', 'False' ) )
+ self.log_actions = string_as_bool( kwargs.get( 'log_actions', 'True' ) )
+ self.log_events = string_as_bool( kwargs.get( 'log_events', 'True' ) )
self.ucsc_display_sites = kwargs.get( 'ucsc_display_sites', "main,test,archaea,ucla" ).lower().split(",")
self.gbrowse_display_sites = kwargs.get( 'gbrowse_display_sites', "wormbase,tair,modencode_worm,modencode_fly" ).lower().split(",")
self.genetrack_display_sites = kwargs.get( 'genetrack_display_sites', "main,test" ).lower().split(",")
@@ -159,7 +159,11 @@ class Configuration( object ):
raise eggs.EggNotFetchable( 'You must scramble the %s egg to use the %s job runner. Instructions are available at:\n http://bitbucket.org/galaxy/galaxy-central/wiki/Config/Cluster' % ( runner_to_egg[runner], runner ) )
except KeyError:
raise Exception( 'No such job runner: %s. Please double-check the value of start_job_runners in universe_wsgi.ini' % runner )
-
+ # Check for deprecated options.
+ for key in self.config_dict.keys():
+ if key in self.deprecated_options:
+ log.warning( "Config option '%s' is deprecated and will be removed in a future release. Please consult the latest version of the sample configuration file." % key )
+
def is_admin_user( self,user ):
"""
Determine if the provided user is listed in `admin_users`.
1
0
galaxy-dist commit 65618fab28b8: lims: removed the db engine echo option in the data transfer
by commits-noreply@bitbucket.org 16 Jul '10
by commits-noreply@bitbucket.org 16 Jul '10
16 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User rc
# Date 1277991101 14400
# Node ID 65618fab28b83ccfd2b0575e8ecabd1e9cd5815a
# Parent b9bb8c131cd1a3364d3960640c724d2a786636ef
lims: removed the db engine echo option in the data transfer
--- a/scripts/galaxy_messaging/server/galaxydb_interface.py
+++ b/scripts/galaxy_messaging/server/galaxydb_interface.py
@@ -28,7 +28,7 @@ class GalaxyDbInterface(object):
def __init__(self, dbstr):
self.dbstr = dbstr
self.db_engine = create_engine(self.dbstr)
- self.db_engine.echo = True
+ self.db_engine.echo = False
self.metadata = MetaData(self.db_engine)
self.session = sessionmaker(bind=self.db_engine)
self.event_table = Table('sample_event', self.metadata, autoload=True )
1
0
galaxy-dist commit 99f33c73bf81: Removed binseq.zip test and replace it with a zip test.
by commits-noreply@bitbucket.org 16 Jul '10
by commits-noreply@bitbucket.org 16 Jul '10
16 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1277989166 14400
# Node ID 99f33c73bf815468400426870da113991e459efc
# Parent e110168a83c91f14a2923149e195aa4e8855fde5
Removed binseq.zip test and replace it with a zip test.
--- a/test/functional/test_get_data.py
+++ b/test/functional/test_get_data.py
@@ -90,35 +90,36 @@ class UploadData( TwillTestCase ):
self.check_history_for_string( "File Format' to 'Scf' when uploading scf files" )
self.delete_history( id=self.security.encode_id( history.id ) )
def test_0025_upload_file( self ):
- """Test uploading 1.scf.zip, manually setting the file format"""
+ """Test uploading 4.bed.zip, manually setting the file format"""
self.check_history_for_string( 'Your history is empty' )
history = sa_session.query( galaxy.model.History ) \
.filter( and_( galaxy.model.History.table.c.deleted==False,
galaxy.model.History.table.c.user_id==admin_user.id ) ) \
.order_by( desc( galaxy.model.History.table.c.create_time ) ) \
.first()
- self.upload_file( '1.scf.zip', ftype='binseq.zip' )
+ self.upload_file( '4.bed.zip', ftype='bed' )
hda = sa_session.query( galaxy.model.HistoryDatasetAssociation ) \
.order_by( desc( galaxy.model.HistoryDatasetAssociation.table.c.create_time ) ) \
.first()
assert hda is not None, "Problem retrieving hda from database"
- self.verify_dataset_correctness( '1.scf.zip', hid=str( hda.hid ) )
- self.check_history_for_string( "Archive of 1 binary sequence files</pre>" )
+ self.verify_dataset_correctness( '4.bed', hid=str( hda.hid ) )
+ self.check_history_for_string( "<th>1.Chrom</th><th>2.Start</th><th>3.End</th>" )
self.delete_history( id=self.security.encode_id( history.id ) )
def test_0030_upload_file( self ):
- """Test uploading 1.scf.zip, NOT setting the file format"""
+ """Test uploading 4.bed.zip, NOT setting the file format"""
self.check_history_for_string( 'Your history is empty' )
history = sa_session.query( galaxy.model.History ) \
.filter( and_( galaxy.model.History.table.c.deleted==False,
galaxy.model.History.table.c.user_id==admin_user.id ) ) \
.order_by( desc( galaxy.model.History.table.c.create_time ) ) \
.first()
- self.upload_file( '1.scf.zip' )
+ self.upload_file( '4.bed.zip' )
hda = sa_session.query( galaxy.model.HistoryDatasetAssociation ) \
.order_by( desc( galaxy.model.HistoryDatasetAssociation.table.c.create_time ) ) \
.first()
assert hda is not None, "Problem retrieving hda from database"
- self.check_history_for_string( "'File Format' for archive consisting of binary files - use 'Binseq.zip'" )
+ self.verify_dataset_correctness( '4.bed', hid=str( hda.hid ) )
+ self.check_history_for_string( "<th>1.Chrom</th><th>2.Start</th><th>3.End</th>" )
self.delete_history( id=self.security.encode_id( history.id ) )
def test_0035_upload_file( self ):
"""Test uploading 1.sam NOT setting the file format"""
Binary file test-data/1.scf.zip has changed
Binary file test-data/4.bed.zip has changed
1
0
galaxy-dist commit b9bb8c131cd1: Merge, since I apparently keep forgetting to push my commits.
by commits-noreply@bitbucket.org 16 Jul '10
by commits-noreply@bitbucket.org 16 Jul '10
16 Jul '10
# HG changeset patch -- Bitbucket.org
# Project galaxy-dist
# URL http://bitbucket.org/galaxy/galaxy-dist/overview
# User Nate Coraor <nate(a)bx.psu.edu>
# Date 1277989841 14400
# Node ID b9bb8c131cd1a3364d3960640c724d2a786636ef
# Parent 99f33c73bf815468400426870da113991e459efc
# Parent 10027d1e6dc31200319f9d90a4d8233170393f2b
Merge, since I apparently keep forgetting to push my commits.
--- a/lib/galaxy/web/controllers/requests_admin.py
+++ b/lib/galaxy/web/controllers/requests_admin.py
@@ -4,9 +4,10 @@ from galaxy.model.orm import *
from galaxy.datatypes import sniff
from galaxy import model, util
from galaxy.util.streamball import StreamBall
-import logging, tempfile, zipfile, tarfile, os, sys, subprocess
+import logging, tempfile, zipfile, tarfile, os, sys, subprocess, smtplib, socket
from galaxy.web.form_builder import *
from datetime import datetime, timedelta
+from email.MIMEText import MIMEText
from galaxy.web.controllers.forms import get_all_forms
from sqlalchemy.sql.expression import func, and_
from sqlalchemy.sql import select
@@ -474,13 +475,21 @@ class RequestsAdmin( BaseController ):
trans.sa_session.flush()
# now that the request is complete send the email notification to the
# the user
- if request.notify:
- mail = os.popen("%s -t" % trans.app.config.sendmail_path, 'w')
- subject = "Galaxy Sample Tracking: '%s' sequencing request in complete." % request.name
- body = "The '%s' sequencing request (type: %s) is now complete. Datasets from all the samples are now available for analysis or download from the respective data libraries in Galaxy." % (request.name, request.type.name)
- email_content = "To: %s\nFrom: no-reply(a)nowhere.edu\nSubject: %s\n\n%s" % (request.user.email, subject, body)
- mail.write( email_content )
- x = mail.close()
+ if request.notify and trans.app.config.smtp_server is not None:
+ host = trans.request.host.split(':')[0]
+ if host == 'localhost':
+ host = socket.getfqdn()
+ msg = MIMEText( "The '%s' sequencing request (type: %s) is now complete. Datasets from all the samples are now available for analysis or download from the respective data libraries in Galaxy." % ( request.name, request.type.name ) )
+ to = msg[ 'To' ] = request.user.email
+ frm = msg[ 'From' ] = 'galaxy-no-reply@' + host
+ msg[ 'Subject' ] = "Galaxy Sample Tracking: '%s' sequencing request in complete." % request.name
+ try:
+ s = smtplib.SMTP()
+ s.connect( trans.app.config.smtp_server )
+ s.sendmail( frm, [ to ], msg.as_string() )
+ s.close()
+ except:
+ pass
@web.expose
@web.require_admin
1
0