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
December 2014
- 2 participants
- 245 discussions
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/4806dfc74e0e/
Changeset: 4806dfc74e0e
Branch: next-stable
User: guerler
Date: 2014-12-08 20:34:11+00:00
Summary: Security fixes for assigned templates
Affected #: 4 files
diff -r 0317c3ec09d99ac40a9217a4725c6a2fe89c3de8 -r 4806dfc74e0e3d903ad40ee33dacec67ed516daa templates/webapps/galaxy/tracks/history_datasets_select_grid.mako
--- a/templates/webapps/galaxy/tracks/history_datasets_select_grid.mako
+++ b/templates/webapps/galaxy/tracks/history_datasets_select_grid.mako
@@ -1,5 +1,5 @@
<%inherit file="/tracks/history_select_grid.mako"/><%def name="title()">
- <h2>History '${grid.get_current_item( trans, **kwargs ).name}'</h2>
+ <h2>History '${grid.get_current_item( trans, **kwargs ).name | h}'</h2></%def>
diff -r 0317c3ec09d99ac40a9217a4725c6a2fe89c3de8 -r 4806dfc74e0e3d903ad40ee33dacec67ed516daa templates/webapps/galaxy/tracks/index.mako
--- a/templates/webapps/galaxy/tracks/index.mako
+++ /dev/null
@@ -1,38 +0,0 @@
-<form id="form" method="POST">
- <div class="form-row">
- <label for="dbkey">Browser name:</label>
- <div class="form-row-input">
- <input type="text" name="title" id="title" value="Unnamed Browser"></input>
- </div>
- <div style="clear: both;"></div>
- </div>
- <div class="form-row">
- <label for="dbkey">Reference genome build (dbkey): </label>
- <div class="form-row-input">
- <select name="dbkey" id="dbkey" refresh_on_change="true">
- %for tmp_dbkey in dbkey_set:
- <option value="${tmp_dbkey}"
- %if tmp_dbkey == dbkey:
- selected="selected"
- %endif
- >${tmp_dbkey}</option>
- %endfor
- </select>
- </div>
- <div style="clear: both;"></div>
- </div>
- <div class="form-row">
- <label for="dataset_ids">Datasets to visualize: (${", ".join(available_tracks)} files are supported)</label>
- %for dataset_id, (dataset_ext, dataset_name) in datasets.iteritems():
- <div>
- <input type="checkbox" id="${dataset_id}" name="dataset_ids" value="${dataset_id}" />
- <label style="display:inline; font-weight: normal" for="${dataset_id}">[${dataset_ext}] ${dataset_name}</label>
- </div>
- %endfor
-
- <div style="clear: both;"></div>
- </div>
- <div class="form-row">
- <input type="submit" name="browse" value="Browse"/>
- </div>
-</form>
diff -r 0317c3ec09d99ac40a9217a4725c6a2fe89c3de8 -r 4806dfc74e0e3d903ad40ee33dacec67ed516daa templates/webapps/galaxy/tracks/library_datasets_select_grid.mako
--- a/templates/webapps/galaxy/tracks/library_datasets_select_grid.mako
+++ b/templates/webapps/galaxy/tracks/library_datasets_select_grid.mako
@@ -2,7 +2,7 @@
<%namespace file='/library/common/browse_library.mako' import="render_content, grid_javascripts" /><%def name="title()">
- <h2>History '${grid.get_current_item( trans, **kwargs ).name}'</h2>
+ <h2>History '${grid.get_current_item( trans, **kwargs ).name | h}'</h2></%def>
${select_header()}
diff -r 0317c3ec09d99ac40a9217a4725c6a2fe89c3de8 -r 4806dfc74e0e3d903ad40ee33dacec67ed516daa templates/webapps/galaxy/visualization/phyloviz.mako
--- a/templates/webapps/galaxy/visualization/phyloviz.mako
+++ b/templates/webapps/galaxy/visualization/phyloviz.mako
@@ -196,7 +196,7 @@
<p>Select a tree to view:
<select id="phylovizNexSelector">
% for tree, index in data["trees"]:
- <option value="${index}">${tree}</option>
+ <option value="${index | h}">${tree | h}</option>
% endfor
</select></p>
https://bitbucket.org/galaxy/galaxy-central/commits/49ef0cdbf45a/
Changeset: 49ef0cdbf45a
Branch: next-stable
User: guerler
Date: 2014-12-08 20:38:01+00:00
Summary: Parameters: Handle missing context
Affected #: 1 file
diff -r 4806dfc74e0e3d903ad40ee33dacec67ed516daa -r 49ef0cdbf45acca661900c22869f33cd01536ecf lib/galaxy/tools/parameters/basic.py
--- a/lib/galaxy/tools/parameters/basic.py
+++ b/lib/galaxy/tools/parameters/basic.py
@@ -767,7 +767,10 @@
if self.options:
return self.options.get_options( trans, other_values )
elif self.dynamic_options:
- return eval( self.dynamic_options, self.tool.code_namespace, other_values )
+ try:
+ return eval( self.dynamic_options, self.tool.code_namespace, other_values )
+ except Exception:
+ return []
else:
return self.static_options
@@ -779,7 +782,10 @@
if self.options:
return map( _get_UnvalidatedValue_value, set( v for _, v, _ in self.options.get_options( trans, other_values ) ) )
elif self.dynamic_options:
- return set( v for _, v, _ in eval( self.dynamic_options, self.tool.code_namespace, other_values ) )
+ try:
+ return set( v for _, v, _ in eval( self.dynamic_options, self.tool.code_namespace, other_values ) )
+ except Exception:
+ return set()
else:
return self.legal_values
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: dannon: Merged in dan/galaxy-central-prs/stable (pull request #597)
by commits-noreply@bitbucket.org 08 Dec '14
by commits-noreply@bitbucket.org 08 Dec '14
08 Dec '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/07404a82972d/
Changeset: 07404a82972d
Branch: stable
User: dannon
Date: 2014-12-08 20:22:53+00:00
Summary: Merged in dan/galaxy-central-prs/stable (pull request #597)
[STABLE] HTML escape user-settable values in Data Libraries. Update tests to reflect that e.g. quotes are now html escaped within pages. Eliminate the unnecessary use of Params() object for these controllers.
Affected #: 17 files
diff -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a -r 07404a82972d877b5529fffaeb3e7e05b69a02a3 lib/galaxy/webapps/galaxy/controllers/library.py
--- a/lib/galaxy/webapps/galaxy/controllers/library.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library.py
@@ -3,7 +3,7 @@
from galaxy import web
from galaxy.model.orm import and_, not_, or_
from galaxy.web.base.controller import BaseUIController
-from galaxy.web.framework.helpers import grids
+from galaxy.web.framework.helpers import escape, grids
from library_common import get_comptypes, lucene_search, whoosh_search
@@ -79,7 +79,6 @@
@web.expose
def list( self, trans, **kwd ):
- params = util.Params( kwd )
# define app configuration for generic mako template
app = {
'jscript' : "galaxy.library"
@@ -89,10 +88,9 @@
@web.expose
def index( self, trans, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- default_action = params.get( 'default_action', None )
+ message = escape( kwd.get( 'message', '' ) )
+ status = escape( kwd.get( 'status', 'done' ) )
+ default_action = kwd.get( 'default_action', None )
return trans.fill_template( "/library/index.mako",
default_action=default_action,
message=message,
diff -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a -r 07404a82972d877b5529fffaeb3e7e05b69a02a3 lib/galaxy/webapps/galaxy/controllers/library_admin.py
--- a/lib/galaxy/webapps/galaxy/controllers/library_admin.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library_admin.py
@@ -5,7 +5,7 @@
from galaxy import web
from galaxy.web.base.controller import BaseUIController
-from galaxy.web.framework.helpers import grids, time_ago
+from galaxy.web.framework.helpers import escape, grids, time_ago
from library_common import get_comptypes, lucene_search, whoosh_search
# from galaxy.model.orm import *
@@ -141,20 +141,19 @@
lddas=lddas,
show_deleted=show_deleted,
use_panels=use_panels,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
# Render the list view
return self.library_list_grid( trans, **kwd )
@web.expose
@web.require_admin
def create_library( self, trans, **kwd ):
- params = galaxy.util.Params( kwd )
- message = galaxy.util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- if params.get( 'create_library_button', False ):
- name = galaxy.util.restore_text( params.get( 'name', 'No name' ) )
- description = galaxy.util.restore_text( params.get( 'description', '' ) )
- synopsis = galaxy.util.restore_text( params.get( 'synopsis', '' ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ if kwd.get( 'create_library_button', False ):
+ name = kwd.get( 'name', 'No name' )
+ description = kwd.get( 'description', '' )
+ synopsis = kwd.get( 'synopsis', '' )
if synopsis in [ 'None', None ]:
synopsis = ''
library = trans.app.model.Library( name=name, description=description, synopsis=synopsis )
@@ -167,9 +166,9 @@
action='browse_library',
cntrller='library_admin',
id=trans.security.encode_id( library.id ),
- message=galaxy.util.sanitize_text( message ),
+ message=message,
status='done' ) )
- return trans.fill_template( '/admin/library/new_library.mako', message=message, status=status )
+ return trans.fill_template( '/admin/library/new_library.mako', message=escape( message ), status=escape( status ) )
@web.expose
@web.require_admin
def delete_library( self, trans, id, **kwd ):
@@ -196,8 +195,7 @@
# TODO: change this function to purge_library_item, behaving similar to delete_library_item
# assuming we want the ability to purge libraries.
# This function is currently only used by the functional tests.
- params = galaxy.util.Params( kwd )
- library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( params.id ) )
+ library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( kwd.get( 'id' ) ) )
def purge_folder( library_folder ):
for lf in library_folder.folders:
purge_folder( lf )
@@ -226,7 +224,7 @@
message = "Library '%s' has not been marked deleted, so it cannot be purged" % ( library.name )
return trans.response.send_redirect( web.url_for( controller='library_admin',
action='browse_libraries',
- message=galaxy.util.sanitize_text( message ),
+ message=message,
status='error' ) )
else:
purge_folder( library.root_folder )
@@ -236,5 +234,5 @@
message = "Library '%s' and all of its contents have been purged, datasets will be removed from disk via the cleanup_datasets script" % library.name
return trans.response.send_redirect( web.url_for( controller='library_admin',
action='browse_libraries',
- message=galaxy.util.sanitize_text( message ),
+ message=message,
status='done' ) )
diff -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a -r 07404a82972d877b5529fffaeb3e7e05b69a02a3 lib/galaxy/webapps/galaxy/controllers/library_common.py
--- a/lib/galaxy/webapps/galaxy/controllers/library_common.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library_common.py
@@ -20,6 +20,7 @@
from galaxy.util.streamball import StreamBall
from galaxy.web.base.controller import BaseUIController, UsesFormDefinitionsMixin, UsesExtendedMetadataMixin, UsesLibraryMixinItems
from galaxy.web.form_builder import AddressField, CheckboxField, SelectField, build_select_field
+from galaxy.web.framework.helpers import escape
from galaxy.model.orm import and_, eagerload_all
# Whoosh is compatible with Python 2.5+ Try to import Whoosh and set flag to indicate whether tool search is enabled.
@@ -92,14 +93,13 @@
@web.expose
def browse_library( self, trans, cntrller='library', **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
# If use_panels is True, the library is being accessed via an external link
# which did not originate from within the Galaxy instance, and the library will
# be displayed correctly with the mast head.
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- library_id = params.get( 'id', None )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ library_id = kwd.get( 'id', None )
if not library_id:
# To handle bots
message = "You must specify a library id."
@@ -116,9 +116,9 @@
message = "Invalid library id ( %s ) specified." % str( library_id )
status = 'error'
else:
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- created_ldda_ids = params.get( 'created_ldda_ids', '' )
- hidden_folder_ids = util.listify( params.get( 'hidden_folder_ids', '' ) )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ created_ldda_ids = kwd.get( 'created_ldda_ids', '' )
+ hidden_folder_ids = util.listify( kwd.get( 'hidden_folder_ids', '' ) )
if created_ldda_ids and not message:
message = "%d datasets are uploading in the background to the library '%s' (each is selected). " % \
( len( created_ldda_ids.split( ',' ) ), library.name )
@@ -137,8 +137,8 @@
show_deleted=show_deleted,
comptypes=comptypes,
current_user_roles=current_user_roles,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
else:
return trans.fill_template( 'library/common/browse_library.mako',
cntrller=cntrller,
@@ -149,44 +149,43 @@
show_deleted=show_deleted,
comptypes=comptypes,
current_user_roles=current_user_roles,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
except Exception, e:
message = 'Error attempting to display contents of library (%s): %s.' % ( str( library.name ), str( e ) )
status = 'error'
- default_action = params.get( 'default_action', None )
+ default_action = kwd.get( 'default_action', None )
return trans.response.send_redirect( web.url_for( use_panels=use_panels,
controller=cntrller,
action='browse_libraries',
default_action=default_action,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
@web.expose
def library_info( self, trans, cntrller, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
- library_id = params.get( 'id', None )
+ library_id = kwd.get( 'id', None )
try:
library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) )
except:
library = None
self._check_access( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'library_info_button', False ):
+ if kwd.get( 'library_info_button', False ):
self._check_modify( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted )
old_name = library.name
- new_name = util.restore_text( params.get( 'name', 'No name' ) )
+ new_name = kwd.get( 'name', 'No name' )
if not new_name:
message = 'Enter a valid name'
status='error'
else:
- new_description = util.restore_text( params.get( 'description', '' ) )
- new_synopsis = util.restore_text( params.get( 'synopsis', '' ) )
+ new_description = kwd.get( 'description', '' )
+ new_synopsis = kwd.get( 'synopsis', '' )
if new_synopsis in [ None, 'None' ]:
new_synopsis = ''
library.name = new_name
@@ -204,7 +203,7 @@
use_panels=use_panels,
id=trans.security.encode_id( library.id ),
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
# See if we have any associated templates
info_association, inherited = library.get_info_association()
@@ -220,29 +219,28 @@
show_deleted=show_deleted,
info_association=info_association,
inherited=inherited,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def library_permissions( self, trans, cntrller, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
- library_id = params.get( 'id', None )
+ library_id = kwd.get( 'id', None )
try:
library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) )
except:
library = None
self._check_access( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted )
self._check_manage( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'update_roles_button', False ):
+ if kwd.get( 'update_roles_button', False ):
# The user clicked the Save button on the 'Associate With Roles' form
permissions = {}
for k, v in trans.app.model.Library.permitted_actions.items():
- in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( params.get( k + '_in', [] ) ) ]
+ in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( kwd.get( k + '_in', [] ) ) ]
permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
trans.app.security_agent.set_all_library_permissions( trans, library, permissions )
trans.sa_session.refresh( library )
@@ -255,7 +253,7 @@
use_panels=use_panels,
id=trans.security.encode_id( library.id ),
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
roles = trans.app.security_agent.get_legitimate_roles( trans, library, cntrller )
all_roles = trans.app.security_agent.get_all_roles( trans, cntrller )
@@ -267,15 +265,14 @@
roles=roles,
all_roles=all_roles,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def create_folder( self, trans, cntrller, parent_id, library_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller in ( 'library_admin', 'api' )
current_user_roles = trans.get_current_user_roles()
try:
@@ -288,9 +285,9 @@
parent_library = parent_folder.parent_library
self._check_access( trans, cntrller, is_admin, parent_folder, current_user_roles, use_panels, library_id, show_deleted )
self._check_add( trans, cntrller, is_admin, parent_folder, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'new_folder_button', False ) or cntrller == 'api':
- new_folder = trans.app.model.LibraryFolder( name=util.restore_text( params.name ),
- description=util.restore_text( params.description ) )
+ if kwd.get( 'new_folder_button', False ) or cntrller == 'api':
+ new_folder = trans.app.model.LibraryFolder( name=kwd.get( 'name', '' ),
+ description=kwd.get( 'description', '' ) )
# We are associating the last used genome build with folders, so we will always
# initialize a new folder with the first dbkey in genome builds list which is currently
# ? unspecified (?)
@@ -322,7 +319,7 @@
show_deleted=show_deleted,
info_association=info_association,
inherited=inherited,
- message=message,
+ message=escape( message ),
status='done' )
# If not inheritable info_association, redirect to the library.
message = "The new folder named '%s' has been added to the data library." % new_folder.name
@@ -334,7 +331,7 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
# We do not render any template widgets on creation pages since saving the info_association
# cannot occur before the associated item is saved.
@@ -344,15 +341,14 @@
library_id=library_id,
folder=parent_folder,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def folder_info( self, trans, cntrller, id, library_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
try:
@@ -360,11 +356,11 @@
except:
folder = None
self._check_access( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'rename_folder_button', False ):
+ if kwd.get( 'rename_folder_button', False ):
self._check_modify( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted )
old_name = folder.name
- new_name = util.restore_text( params.name )
- new_description = util.restore_text( params.description )
+ new_name = kwd.get( 'name', '' )
+ new_description = kwd.get( 'description', '' )
if not new_name:
message = 'Enter a valid name'
status='error'
@@ -381,7 +377,7 @@
id=id,
library_id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
# See if we have any associated templates
widgets = []
@@ -401,15 +397,14 @@
show_deleted=show_deleted,
info_association=info_association,
inherited=inherited,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def folder_permissions( self, trans, cntrller, id, library_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
try:
@@ -418,14 +413,14 @@
folder = None
self._check_access( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted )
self._check_manage( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'update_roles_button', False ):
+ if kwd.get( 'update_roles_button', False ):
# The user clicked the Save button on the 'Associate With Roles' form
permissions = {}
for k, v in trans.app.model.Library.permitted_actions.items():
if k != 'LIBRARY_ACCESS':
# LIBRARY_ACCESS is a special permission set only at the library level
# and it is not inherited.
- in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( int( x ) ) for x in util.listify( params.get( k + '_in', [] ) ) ]
+ in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( int( x ) ) for x in util.listify( kwd.get( k + '_in', [] ) ) ]
permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
trans.app.security_agent.set_all_library_permissions( trans, folder, permissions )
trans.sa_session.refresh( folder )
@@ -437,7 +432,7 @@
id=trans.security.encode_id( folder.id ),
library_id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
# If the library is public all roles are legitimate, but if the library
# is restricted, only those roles associated with the LIBRARY_ACCESS
@@ -451,15 +446,14 @@
current_user_roles=current_user_roles,
roles=roles,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def ldda_edit_info( self, trans, cntrller, library_id, folder_id, id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
try:
@@ -468,7 +462,7 @@
ldda = None
self._check_access( trans, cntrller, is_admin, ldda, current_user_roles, use_panels, library_id, show_deleted )
self._check_modify( trans, cntrller, is_admin, ldda, current_user_roles, use_panels, library_id, show_deleted )
- dbkey = params.get( 'dbkey', '?' )
+ dbkey = kwd.get( 'dbkey', '?' )
if isinstance( dbkey, list ):
dbkey = dbkey[0]
file_formats = [ dtype_name for dtype_name, dtype_value in trans.app.datatypes_registry.datatypes_by_extension.iteritems() if dtype_value.allow_datatype_change ]
@@ -492,26 +486,26 @@
info_association, inherited = ldda.get_info_association()
if info_association and ( not( inherited ) or info_association.inheritable ):
widgets = ldda.get_template_widgets( trans )
- if params.get( 'change', False ):
+ if kwd.get( 'change', False ):
# The user clicked the Save button on the 'Change data type' form
if __ok_to_edit_metadata( ldda.id ):
- if ldda.datatype.allow_datatype_change and trans.app.datatypes_registry.get_datatype_by_extension( params.datatype ).allow_datatype_change:
- trans.app.datatypes_registry.change_datatype( ldda, params.datatype )
+ if ldda.datatype.allow_datatype_change and trans.app.datatypes_registry.get_datatype_by_extension( kwd.get( 'datatype' ) ).allow_datatype_change:
+ trans.app.datatypes_registry.change_datatype( ldda, kwd.get( 'datatype' ) )
trans.sa_session.flush()
message = "Data type changed for library dataset '%s'." % ldda.name
status = 'done'
else:
- message = "You are unable to change datatypes in this manner. Changing %s to %s is not allowed." % ( ldda.extension, params.datatype )
+ message = "You are unable to change datatypes in this manner. Changing %s to %s is not allowed." % ( ldda.extension, kwd.get( 'datatype' ) )
status = 'error'
else:
message = "This dataset is currently being used as input or output. You cannot change datatype until the jobs have completed or you have canceled them."
status = "error"
- elif params.get( 'save', False ):
+ elif kwd.get( 'save', False ):
# The user clicked the Save button on the 'Edit Attributes' form
old_name = ldda.name
- new_name = util.restore_text( params.get( 'name', '' ) )
- new_info = util.restore_text( params.get( 'info', '' ) )
- new_message = util.restore_text( params.get( 'message', '' ) )
+ new_name = kwd.get( 'name', '' )
+ new_info = kwd.get( 'info', '' )
+ new_message = kwd.get( 'message', '' )
if not new_name:
message = 'Enter a valid name'
status = 'error'
@@ -524,12 +518,12 @@
for name, spec in ldda.datatype.metadata_spec.items():
if spec.get("readonly"):
continue
- optional = params.get( "is_" + name, None )
+ optional = kwd.get( "is_" + name, None )
if optional and optional == 'true':
# optional element... == 'true' actually means it is NOT checked (and therefore ommitted)
setattr( ldda.metadata, name, None )
else:
- setattr( ldda.metadata, name, spec.unwrap( params.get ( name, None ) ) )
+ setattr( ldda.metadata, name, spec.unwrap( kwd.get( name, None ) ) )
ldda.metadata.dbkey = dbkey
ldda.datatype.after_setting_metadata( ldda )
message = "Attributes updated for library dataset '%s'." % ldda.name
@@ -538,7 +532,7 @@
message = "Attributes updated, but metadata could not be changed because this dataset is currently being used as input or output. You must cancel or wait for these jobs to complete before changing metadata."
status = 'warning'
trans.sa_session.flush()
- elif params.get( 'detect', False ):
+ elif kwd.get( 'detect', False ):
# The user clicked the Auto-detect button on the 'Edit Attributes' form
if __ok_to_edit_metadata( ldda.id ):
for name, spec in ldda.datatype.metadata_spec.items():
@@ -553,8 +547,8 @@
message = "This dataset is currently being used as input or output. You cannot change metadata until the jobs have completed or you have canceled them."
status = 'error'
trans.sa_session.flush()
- elif params.get( 'change_extended_metadata', False):
- em_string = util.restore_text( params.get("extended_metadata", "") )
+ elif kwd.get( 'change_extended_metadata', False):
+ em_string = kwd.get("extended_metadata", "" )
if len(em_string):
payload = None
try:
@@ -604,16 +598,15 @@
show_deleted=show_deleted,
info_association=info_association,
inherited=inherited,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def ldda_info( self, trans, cntrller, library_id, folder_id, id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- show_associated_hdas_and_lddas = util.string_as_bool( params.get( 'show_associated_hdas_and_lddas', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ show_associated_hdas_and_lddas = util.string_as_bool( kwd.get( 'show_associated_hdas_and_lddas', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( id ) )
@@ -653,15 +646,14 @@
current_user_roles=current_user_roles,
info_association=info_association,
inherited=inherited,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def ldda_permissions( self, trans, cntrller, library_id, folder_id, id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
ids = util.listify( id )
lddas = []
libraries = []
@@ -685,7 +677,7 @@
id=library_id,
cntrller=cntrller,
use_panels=use_panels,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
# If access to the dataset is restricted, then use the roles associated with the DATASET_ACCESS permission to
# determine the legitimate roles. If the dataset is public, see if access to the library is restricted. If
@@ -698,7 +690,7 @@
roles = trans.app.security_agent.get_legitimate_roles( trans, library, cntrller )
else:
roles = trans.app.security_agent.get_legitimate_roles( trans, ldda.dataset, cntrller )
- if params.get( 'update_roles_button', False ):
+ if kwd.get( 'update_roles_button', False ):
# Dataset permissions
access_action = trans.app.security_agent.get_action( trans.app.security_agent.permitted_actions.DATASET_ACCESS.action )
manage_permissions_action = trans.app.security_agent.get_action( trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS.action )
@@ -755,8 +747,8 @@
library_id=library_id,
roles=roles,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
if len( ids ) > 1:
# Ensure that the permissions across all library items are identical, otherwise we can't update them together.
check_list = []
@@ -781,7 +773,7 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
# Display permission form, permissions will be updated for all lddas simultaneously.
return trans.fill_template( "/library/common/ldda_permissions.mako",
@@ -791,31 +783,30 @@
library_id=library_id,
roles=roles,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def upload_library_dataset( self, trans, cntrller, library_id, folder_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- ldda_message = util.restore_text( params.get( 'ldda_message', '' ) )
- deleted = util.string_as_bool( params.get( 'deleted', False ) )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- replace_id = params.get( 'replace_id', None )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ ldda_message = kwd.get( 'ldda_message', '' )
+ deleted = util.string_as_bool( kwd.get( 'deleted', False ) )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ replace_id = kwd.get( 'replace_id', None )
replace_dataset = None
- upload_option = params.get( 'upload_option', 'upload_file' )
- if params.get( 'files_0|space_to_tab', False ):
- space_to_tab = params.get( 'files_0|space_to_tab', '' )
+ upload_option = kwd.get( 'upload_option', 'upload_file' )
+ if kwd.get( 'files_0|space_to_tab', False ):
+ space_to_tab = kwd.get( 'files_0|space_to_tab', '' )
else:
- space_to_tab = params.get( 'space_to_tab', '' )
- link_data_only = params.get( 'link_data_only', 'copy_files' )
- dbkey = params.get( 'dbkey', '?' )
+ space_to_tab = kwd.get( 'space_to_tab', '' )
+ link_data_only = kwd.get( 'link_data_only', 'copy_files' )
+ dbkey = kwd.get( 'dbkey', '?' )
if isinstance( dbkey, list ):
last_used_build = dbkey[0]
else:
last_used_build = dbkey
- roles = params.get( 'roles', '' )
+ roles = kwd.get( 'roles', '' )
is_admin = trans.user_is_admin() and cntrller in ( 'library_admin', 'api' )
current_user_roles = trans.get_current_user_roles()
widgets = []
@@ -844,7 +835,7 @@
library = folder.parent_library
if folder and last_used_build in [ 'None', None, '?' ]:
last_used_build = folder.genome_build
- if params.get( 'runtool_btn', False ) or params.get( 'ajax_upload', False ) or cntrller == 'api':
+ if kwd.get( 'runtool_btn', False ) or kwd.get( 'ajax_upload', False ) or cntrller == 'api':
error = False
if upload_option == 'upload_paths' and not trans.app.config.allow_library_path_paste:
error = True
@@ -869,7 +860,7 @@
replace_id=replace_id,
upload_option=upload_option,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
else:
# See if we have any inherited templates.
@@ -883,7 +874,7 @@
for index, widget_dict in enumerate( widgets ):
widget = widget_dict[ 'widget' ]
if isinstance( widget, AddressField ):
- value = util.restore_text( params.get( widget.name, '' ) )
+ value = kwd.get( widget.name, '' )
if value == 'new':
if self.field_param_values_ok( widget.name, 'AddressField', **kwd ):
# Save the new address
@@ -966,7 +957,7 @@
default_action=default_action,
created_ldda_ids=created_ldda_ids,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
else:
created_ldda_ids = ''
@@ -981,7 +972,7 @@
id=library_id,
created_ldda_ids=created_ldda_ids,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
# Note: if the upload form was submitted due to refresh_on_change for a form field, we cannot re-populate
# the field for the selected file ( files_0|file_data ) if the user selected one. This is because the value
@@ -1040,8 +1031,8 @@
link_data_only=link_data_only,
show_deleted=show_deleted,
ldda_message=ldda_message,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
def upload_dataset( self, trans, cntrller, library_id, folder_id, replace_dataset=None, **kwd ):
# Set up the traditional tool state/params
tool_id = 'upload1'
@@ -1054,16 +1045,15 @@
if input.type == "upload_dataset":
dataset_upload_inputs.append( input )
# Library-specific params
- params = util.Params( kwd ) # is this filetoolparam safe?
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- server_dir = util.restore_text( params.get( 'server_dir', '' ) )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ server_dir = kwd.get( 'server_dir', '' )
if replace_dataset not in [ None, 'None' ]:
replace_id = trans.security.encode_id( replace_dataset.id )
else:
replace_id = None
- upload_option = params.get( 'upload_option', 'upload_file' )
+ upload_option = kwd.get( 'upload_option', 'upload_file' )
response_code = 200
if upload_option == 'upload_directory':
if server_dir in [ None, 'None', '' ]:
@@ -1092,7 +1082,7 @@
try:
# FIXME: instead of passing params here ( which have been processed by util.Params(), the original kwd
# should be passed so that complex objects that may have been included in the initial request remain.
- library_bunch = upload_common.handle_library_params( trans, params, folder_id, replace_dataset )
+ library_bunch = upload_common.handle_library_params( trans, kwd, folder_id, replace_dataset )
except:
response_code = 500
message = "Unable to parse upload parameters, please report this error."
@@ -1103,9 +1093,9 @@
tool_params = upload_common.persist_uploads( tool_params )
uploaded_datasets = upload_common.get_uploaded_datasets( trans, cntrller, tool_params, precreated_datasets, dataset_upload_inputs, library_bunch=library_bunch )
elif upload_option == 'upload_directory':
- uploaded_datasets, response_code, message = self.get_server_dir_uploaded_datasets( trans, cntrller, params, full_dir, import_dir_desc, library_bunch, response_code, message )
+ uploaded_datasets, response_code, message = self.get_server_dir_uploaded_datasets( trans, cntrller, kwd, full_dir, import_dir_desc, library_bunch, response_code, message )
elif upload_option == 'upload_paths':
- uploaded_datasets, response_code, message = self.get_path_paste_uploaded_datasets( trans, cntrller, params, library_bunch, response_code, message )
+ uploaded_datasets, response_code, message = self.get_path_paste_uploaded_datasets( trans, cntrller, kwd, library_bunch, response_code, message )
upload_common.cleanup_unused_precreated_datasets( precreated_datasets )
if upload_option == 'upload_file' and not uploaded_datasets:
response_code = 400
@@ -1121,7 +1111,7 @@
replace_id=replace_id,
upload_option=upload_option,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
json_file_path = upload_common.create_paramfile( trans, uploaded_datasets )
data_list = [ ud.data for ud in uploaded_datasets ]
@@ -1135,7 +1125,7 @@
def make_library_uploaded_dataset( self, trans, cntrller, params, name, path, type, library_bunch, in_folder=None ):
link_data_only = params.get( 'link_data_only', 'copy_files' )
uuid_str = params.get( 'uuid', None )
- file_type = params.file_type
+ file_type = params.get( 'file_type' )
library_bunch.replace_dataset = None # not valid for these types of upload
uploaded_dataset = util.bunch.Bunch()
new_name = name
@@ -1151,8 +1141,8 @@
uploaded_dataset.type = type
uploaded_dataset.ext = None
uploaded_dataset.file_type = file_type
- uploaded_dataset.dbkey = params.dbkey
- uploaded_dataset.space_to_tab = params.space_to_tab
+ uploaded_dataset.dbkey = params.get( 'dbkey' )
+ uploaded_dataset.space_to_tab = params.get( 'space_to_tab' )
if in_folder:
uploaded_dataset.in_folder = in_folder
uploaded_dataset.data = upload_common.new_upload( trans, cntrller, uploaded_dataset, library_bunch )
@@ -1247,7 +1237,7 @@
files_and_folders.append((file_path, file, in_folder))
return files_and_folders
def _paths_list(self, params):
- return [ (l.strip(), os.path.abspath(l.strip())) for l in params.filesystem_paths.splitlines() if l.strip() ]
+ return [ (l.strip(), os.path.abspath(l.strip())) for l in params.get( 'filesystem_paths', '' ).splitlines() if l.strip() ]
def _check_path_paste_params(self, params):
if params.get( 'filesystem_paths', '' ) == '':
@@ -1259,33 +1249,32 @@
if not os.path.exists( path ):
bad_paths.append( path )
if bad_paths:
- message = "Invalid paths:<br><ul><li>%s</li></ul>" % "</li><li>".join( bad_paths )
+ message = 'Invalid paths: "%s".' % '", "'.join( bad_paths )
response_code = 400
return None, response_code, message
return None
@web.expose
def add_history_datasets_to_library( self, trans, cntrller, library_id, folder_id, hda_ids='', **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- ldda_message = util.restore_text( params.get( 'ldda_message', '' ) )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- replace_id = params.get( 'replace_id', None )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ ldda_message = kwd.get( 'ldda_message', '' )
+ show_deleted = kwd.get( 'show_deleted', False )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ replace_id = kwd.get( 'replace_id', None )
replace_dataset = None
- upload_option = params.get( 'upload_option', 'import_from_history' )
- if params.get( 'files_0|space_to_tab', False ):
- space_to_tab = params.get( 'files_0|space_to_tab', '' )
+ upload_option = kwd.get( 'upload_option', 'import_from_history' )
+ if kwd.get( 'files_0|space_to_tab', False ):
+ space_to_tab = kwd.get( 'files_0|space_to_tab', '' )
else:
- space_to_tab = params.get( 'space_to_tab', '' )
- link_data_only = params.get( 'link_data_only', 'copy_files' )
- dbkey = params.get( 'dbkey', '?' )
+ space_to_tab = kwd.get( 'space_to_tab', '' )
+ link_data_only = kwd.get( 'link_data_only', 'copy_files' )
+ dbkey = kwd.get( 'dbkey', '?' )
if isinstance( dbkey, list ):
last_used_build = dbkey[0]
else:
last_used_build = dbkey
- roles = params.get( 'roles', '' )
+ roles = kwd.get( 'roles', '' )
is_admin = trans.user_is_admin() and cntrller in ( 'library_admin', 'api' )
current_user_roles = trans.get_current_user_roles()
widgets = []
@@ -1323,9 +1312,9 @@
cntrller=cntrller,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
- if params.get( 'add_history_datasets_to_library_button', False ):
+ if kwd.get( 'add_history_datasets_to_library_button', False ):
hda_ids = util.listify( hda_ids )
if hda_ids:
dataset_names = []
@@ -1354,7 +1343,7 @@
trans.app.security_agent.copy_library_permissions( trans, folder, ldda )
trans.app.security_agent.copy_library_permissions( trans, folder, ldda.library_dataset )
else:
- library_bunch = upload_common.handle_library_params( trans, params, folder_id, replace_dataset )
+ library_bunch = upload_common.handle_library_params( trans, kwd, folder_id, replace_dataset )
if library_bunch.template and library_bunch.template_field_contents:
# Since information templates are inherited, the template fields can be displayed on the upload form.
# If the user has added field contents, we'll need to create a new form_values and info_association
@@ -1422,12 +1411,12 @@
id=library_id,
created_ldda_ids=created_ldda_ids,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
else:
message = 'Select at least one dataset from the list of active datasets in your current history'
status = 'error'
- upload_option = params.get( 'upload_option', 'import_from_history' )
+ upload_option = kwd.get( 'upload_option', 'import_from_history' )
widgets = self._get_populated_widgets( folder )
# Send list of data formats to the upload form so the "extension" select list can be populated dynamically
file_formats = trans.app.datatypes_registry.upload_file_formats
@@ -1461,8 +1450,8 @@
link_data_only=link_data_only,
show_deleted=show_deleted,
ldda_message=ldda_message,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
def _build_roles_select_list( self, trans, cntrller, library, selected_role_ids=[] ):
# Get the list of legitimate roles to display on the upload form. If the library is public,
# all active roles are legitimate. If the library is restricted by the LIBRARY_ACCESS permission, only
@@ -1521,8 +1510,7 @@
def download_dataset_from_folder( self, trans, cntrller, id, library_id=None, **kwd ):
"""Catches the dataset id and displays file contents as directed"""
show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
- params = util.Params( kwd )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
try:
@@ -1555,15 +1543,14 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
@web.expose
def library_dataset_info( self, trans, cntrller, id, library_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
try:
@@ -1571,11 +1558,11 @@
except:
library_dataset = None
self._check_access( trans, cntrller, is_admin, library_dataset, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'edit_attributes_button', False ):
+ if kwd.get( 'edit_attributes_button', False ):
self._check_modify( trans, cntrller, is_admin, library_dataset, current_user_roles, use_panels, library_id, show_deleted )
old_name = library_dataset.name
- new_name = util.restore_text( params.get( 'name', '' ) )
- new_info = util.restore_text( params.get( 'info', '' ) )
+ new_name = kwd.get( 'name', '' )
+ new_info = kwd.get( 'info', '' )
if not new_name:
message = 'Enter a valid name'
status = 'error'
@@ -1604,15 +1591,14 @@
widgets=widgets,
widget_fields_have_contents=widget_fields_have_contents,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def library_dataset_permissions( self, trans, cntrller, id, library_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
try:
@@ -1621,7 +1607,7 @@
library_dataset = None
self._check_access( trans, cntrller, is_admin, library_dataset, current_user_roles, use_panels, library_id, show_deleted )
self._check_manage( trans, cntrller, is_admin, library_dataset, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'update_roles_button', False ):
+ if kwd.get( 'update_roles_button', False ):
# The user clicked the Save button on the 'Associate With Roles' form
permissions = {}
for k, v in trans.app.model.Library.permitted_actions.items():
@@ -1652,22 +1638,21 @@
roles=roles,
current_user_roles=current_user_roles,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def make_library_item_public( self, trans, cntrller, library_id, item_type, id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
current_user_roles = trans.get_current_user_roles()
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
if item_type == 'library':
library = trans.sa_session.query( trans.model.Library ).get( trans.security.decode_id( id ) )
self._check_access( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted )
self._check_manage( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted )
- contents = util.string_as_bool( params.get( 'contents', 'False' ) )
+ contents = util.string_as_bool( kwd.get( 'contents', 'False' ) )
trans.app.security_agent.make_library_public( library, contents=contents )
if contents:
message = "The data library (%s) and all its contents have been made publicly accessible." % library.name
@@ -1694,7 +1679,7 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
@web.expose
def act_on_multiple_datasets( self, trans, cntrller, library_id=None, ldda_ids='', **kwd ):
@@ -1718,12 +1703,11 @@
rval += '%s %i %s%s %s\r\n' % ( crc, size, self.url_base, quoted_fname, relpath )
return rval
# Perform an action on a list of library datasets.
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- action = params.get( 'do_action', None )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ action = kwd.get( 'do_action', None )
lddas = []
error = False
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
@@ -1738,7 +1722,7 @@
else:
if action in [ 'import_to_current_history', 'import_to_histories' ]:
new_kwd = {}
- if action == 'import_to_current_history':
+ if current_history is not None and action == 'import_to_current_history':
encoded_current_history_id = trans.security.encode_id( current_history.id )
selected_history_id = encoded_current_history_id
new_kwd[ 'do_action' ] = action
@@ -1809,7 +1793,7 @@
folder_id=folder_id,
id=",".join( encoded_ldda_ids ),
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
else:
message = "You are not authorized to manage permissions on any of the selected datasets."
@@ -1970,11 +1954,11 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
else:
# We arrived here from the library_dataset_search_results page, so redirect there.
- search_term = params.get( 'search_term', '' )
+ search_term = kwd.get( 'search_term', '' )
comptypes = get_comptypes( trans )
return trans.fill_template( '/library/common/library_dataset_search_results.mako',
cntrller=cntrller,
@@ -1984,8 +1968,8 @@
lddas=lddas,
show_deleted=show_deleted,
use_panels=use_panels,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def import_datasets_to_histories( self, trans, cntrller, library_id='', folder_id='', ldda_ids='', target_history_id='', target_history_ids='', new_history_name='', **kwd ):
@@ -1995,12 +1979,11 @@
# - a select list option for acting on multiple selected datasets within a library
# ( ldda_ids is a comma separated string of ldda ids )
# - a menu option for a library dataset search result set ( ldda_ids is a comma separated string of ldda ids )
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- action = params.get( 'do_action', None )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ action = kwd.get( 'do_action', None )
user = trans.get_user()
current_history = trans.get_history()
if library_id:
@@ -2019,7 +2002,7 @@
target_history_ids = set( [ trans.security.decode_id( target_history_id ) for target_history_id in target_history_ids if target_history_id ] )
elif target_history_id:
target_history_ids = [ trans.security.decode_id( target_history_id ) ]
- if params.get( 'import_datasets_to_histories_button', False ):
+ if kwd.get( 'import_datasets_to_histories_button', False ):
invalid_datasets = 0
if not ldda_ids or not ( target_history_ids or new_history_name ):
message = "You must provide one or more source library datasets and one or more target histories."
@@ -2083,11 +2066,13 @@
# to the lddas in order for the menu optin to be available.
ldda = trans.sa_session.query( trans.model.LibraryDatasetDatasetAssociation ).get( ldda_id )
source_lddas.append( ldda )
+ if current_history is None:
+ current_history = trans.get_history( create=True )
if current_history is not None:
target_histories = [ current_history ]
else:
target_histories = []
- message = 'You must have a history before you can import datasets. You can do this by <a href="%s" target="_top">loading the analysis interface</a>.' % url_for(controller='root')
+ message = 'You must have a history before you can import datasets. You can do this by loading the analysis interface.'
status = 'error'
if user:
target_histories = user.active_histories
@@ -2097,7 +2082,7 @@
action='browse_library',
cntrller=cntrller,
id=library_id,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
return trans.fill_template( "/library/common/import_datasets_to_histories.mako",
cntrller=cntrller,
@@ -2111,15 +2096,14 @@
new_history_name=new_history_name,
show_deleted=show_deleted,
use_panels=use_panels,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def manage_template_inheritance( self, trans, cntrller, item_type, library_id, folder_id=None, ldda_id=None, **kwd ):
- params = util.Params( kwd )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
is_admin = ( trans.user_is_admin() and cntrller == 'library_admin' )
current_user_roles = trans.get_current_user_roles()
try:
@@ -2138,7 +2122,7 @@
cntrller=cntrller,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
info_association, inherited = item.get_info_association( restrict=True )
if info_association:
@@ -2157,7 +2141,7 @@
folder_id=folder_id,
id=id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
@web.expose
def move_library_item( self, trans, cntrller, item_type, item_id, source_library_id='', make_target_current=True, **kwd ):
@@ -2168,11 +2152,10 @@
# 'ldda' and item_id is a comma separated string of ldda ids )
# - a menu option for a library dataset search result set ( item_type is 'ldda' and item_id is a
# comma separated string of ldda ids )
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
make_target_current = util.string_as_bool( make_target_current )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
user = trans.get_user()
@@ -2186,14 +2169,14 @@
else:
# Request sent from the library_dataset_search_results page.
source_library = None
- target_library_id = params.get( 'target_library_id', '' )
+ target_library_id = kwd.get( 'target_library_id', '' )
if target_library_id not in [ '', 'none', None ]:
target_library = trans.sa_session.query( trans.model.Library ).get( trans.security.decode_id( target_library_id ) )
elif make_target_current:
target_library = source_library
else:
target_library = None
- target_folder_id = params.get( 'target_folder_id', '' )
+ target_folder_id = kwd.get( 'target_folder_id', '' )
if target_folder_id not in [ '', 'none', None ]:
target_folder = trans.sa_session.query( trans.model.LibraryFolder ).get( trans.security.decode_id( target_folder_id ) )
if target_library is None:
@@ -2208,7 +2191,7 @@
elif item_type == 'folder':
move_folder_id = item_id
move_folder = trans.sa_session.query( trans.model.LibraryFolder ).get( trans.security.decode_id( move_folder_id ) )
- if params.get( 'move_library_item_button', False ):
+ if kwd.get( 'move_library_item_button', False ):
if not ( move_ldda_ids or move_folder_id ) or target_folder_id in [ '', 'none', None ]:
message = "You must select a source folder or one or more source datasets, and a target folder."
status = 'error'
@@ -2372,8 +2355,8 @@
target_folder_id_select_field=target_folder_id_select_field,
show_deleted=show_deleted,
use_panels=use_panels,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def delete_library_item( self, trans, cntrller, library_id, item_id, item_type, **kwd ):
# This action will handle deleting all types of library items. State is saved for libraries and
@@ -2541,7 +2524,7 @@
action='browse_libraries',
cntrller=cntrller,
use_panels=use_panels,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
return trans.response.send_redirect( web.url_for( controller='library_common',
action='browse_library',
@@ -2549,7 +2532,7 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
def _check_add( self, trans, cntrller, is_admin, item, current_user_roles, use_panels, library_id, show_deleted ):
# Deny access if the user is not an admin and does not have the LIBRARY_ADD permission.
@@ -2564,7 +2547,7 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
def _check_manage( self, trans, cntrller, is_admin, item, current_user_roles, use_panels, library_id, show_deleted ):
if isinstance( item, trans.model.LibraryDataset ):
@@ -2580,7 +2563,7 @@
id=library_id,
cntrller=cntrller,
use_panels=use_panels,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
# Deny access if the user is not an admin and does not have the LIBRARY_MANAGE permission.
if not ( is_admin or trans.app.security_agent.can_manage_library_item( current_user_roles, item ) ):
@@ -2592,7 +2575,7 @@
id=library_id,
cntrller=cntrller,
use_panels=use_panels,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
def _check_modify( self, trans, cntrller, is_admin, item, current_user_roles, use_panels, library_id, show_deleted ):
# Deny modification if the user is not an admin and does not have the LIBRARY_MODIFY permission.
@@ -2606,7 +2589,7 @@
id=library_id,
use_panels=use_panels,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
# ---- Utility methods -------------------------------------------------------
@@ -2740,9 +2723,8 @@
return map( operator.getitem, intermed, ( -1, ) * len( intermed ) )
def lucene_search( trans, cntrller, search_term, search_url, **kwd ):
"""Return display of results from a full-text lucene search of data libraries."""
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
full_url = "%s/find?%s" % ( search_url, urllib.urlencode( { "kwd" : search_term } ) )
response = urllib2.urlopen( full_url )
ldda_ids = util.json.loads( response.read() )[ "ids" ]
@@ -2751,9 +2733,8 @@
return status, message, get_sorted_accessible_library_items( trans, cntrller, lddas, 'name' )
def whoosh_search( trans, cntrller, search_term, **kwd ):
"""Return display of results from a full-text whoosh search of data libraries."""
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
ok = True
if whoosh_search_enabled:
whoosh_index_dir = trans.app.config.whoosh_index_dir
diff -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a -r 07404a82972d877b5529fffaeb3e7e05b69a02a3 templates/webapps/galaxy/library/common/browse_library.mako
--- a/templates/webapps/galaxy/library/common/browse_library.mako
+++ b/templates/webapps/galaxy/library/common/browse_library.mako
@@ -236,29 +236,29 @@
%if current_version and ( not ldda.library_dataset.deleted or show_deleted ):
<tr class="datasetRow"
%if parent is not None:
- parent="${parent}"
+ parent="${parent | h}"
%endif
- id="libraryItem-${ldda.id}">
+ id="libraryItem-${ldda.id | h}"><td style="padding-left: ${pad+20}px;">
- <input style="float: left;" type="checkbox" name="ldda_ids" id="${trans.security.encode_id( ldda.id )}" value="${trans.security.encode_id( ldda.id )}"
+ <input style="float: left;" type="checkbox" name="ldda_ids" id="${trans.security.encode_id( ldda.id ) | h}" value="${trans.security.encode_id( ldda.id ) | h}"
%if selected:
checked="checked"
%endif
/>
%if simple:
- <label for="${trans.security.encode_id( ldda.id )}">${ util.unicodify( ldda.name )}</label>
+ <label for="${trans.security.encode_id( ldda.id ) | h}">${ util.unicodify( ldda.name ) | h}</label>
%else:
- <div style="float: left; margin-left: 1px;" class="menubutton split popup" id="dataset-${ldda.id}-popup">
+ <div style="float: left; margin-left: 1px;" class="menubutton split popup" id="dataset-${ldda.id | h}-popup"><a class="view-info" href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">
%if ldda.library_dataset.deleted:
- <div class="libraryItem-error">${util.unicodify( ldda.name )}</div>
+ <div class="libraryItem-error">${util.unicodify( ldda.name ) | h}</div>
%else:
- ${util.unicodify( ldda.name )}
+ ${util.unicodify( ldda.name ) | h}
%endif
</a></div>
%if not library.deleted:
- <div popupmenu="dataset-${ldda.id}-popup">
+ <div popupmenu="dataset-${ldda.id | h}-popup">
%if not branch_deleted( folder ) and not ldda.library_dataset.deleted and can_modify:
<a class="action-button" href="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit information</a><a class="action-button" href="${h.url_for( controller='library_common', action='move_library_item', cntrller=cntrller, item_type='ldda', item_id=trans.security.encode_id( ldda.id ), source_library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Move this dataset</a>
@@ -287,7 +287,7 @@
%endif
%if can_modify:
%if not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.deleted:
- <a class="action-button" confirm="Click OK to delete dataset '${util.unicodify( ldda.name )}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Delete this dataset</a>
+ <a class="action-button" confirm="Click OK to delete dataset '${util.unicodify( ldda.name ) | h}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Delete this dataset</a>
%elif not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.purged and ldda.library_dataset.deleted:
<a class="action-button" href="${h.url_for( controller='library_common', action='undelete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Undelete this dataset</a>
%endif
@@ -298,10 +298,10 @@
</td>
% if not simple:
<td id="libraryItemInfo">${render_library_item_info( ldda )}</td>
- <td>${ldda.extension}</td>
+ <td>${ldda.extension | h}</td>
% endif
- <td>${ldda.create_time.strftime( trans.app.config.pretty_datetime_format )}</td>
- <td>${ldda.get_size( nice_size=True )}</td>
+ <td>${ldda.create_time.strftime( trans.app.config.pretty_datetime_format ) | h}</td>
+ <td>${ldda.get_size( nice_size=True ) | h}</td></tr><%
my_row = row_counter.count
@@ -355,28 +355,28 @@
%>
%if not root_folder and ( not folder.deleted or show_deleted ):
<% encoded_id = trans.security.encode_id( folder.id ) %>
- <tr id="folder-${encoded_id}" class="folderRow libraryOrFolderRow"
+ <tr id="folder-${encoded_id | h}" class="folderRow libraryOrFolderRow"
%if parent is not None:
- parent="${parent}"
+ parent="${parent | h}"
style="display: none;"
%endif
>
- <td style="padding-left: ${folder_pad}px;">
+ <td style="padding-left: ${folder_pad | h}px;"><input type="checkbox" class="folderCheckbox"/>
- <span class="expandLink folder-${encoded_id}-click">
- <div style="float: left; margin-left: 2px;" class="menubutton split popup" id="folder_img-${folder.id}-popup">
- <a class="folder-${encoded_id}-click" href="javascript:void(0);">
+ <span class="expandLink folder-${encoded_id | h}-click">
+ <div style="float: left; margin-left: 2px;" class="menubutton split popup" id="folder_img-${folder.id | h}-popup">
+ <a class="folder-${encoded_id | h}-click" href="javascript:void(0);"><span class="rowIcon"></span>
%if folder.deleted:
- <div class="libraryItem-error">${folder.name}</div>
+ <div class="libraryItem-error">${folder.name | h}</div>
%else:
- ${folder.name}
+ ${folder.name | h}
%endif
</a></div></span>
%if not library.deleted:
- <div popupmenu="folder_img-${folder.id}-popup">
+ <div popupmenu="folder_img-${folder.id | h}-popup">
%if not branch_deleted( folder ) and can_add:
<a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add datasets</a><a class="action-button" href="${h.url_for( controller='library_common', action='create_folder', cntrller=cntrller, parent_id=trans.security.encode_id( folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add sub-folder</a>
@@ -407,7 +407,7 @@
%endif
%if can_modify:
%if not library.deleted and not folder.deleted:
- <a class="action-button" confirm="Click OK to delete the folder '${folder.name}.'" href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Delete this folder</a>
+ <a class="action-button" confirm="Click OK to delete the folder '${folder.name | h}.'" href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Delete this folder</a>
%elif not library.deleted and folder.deleted and not folder.purged:
<a class="action-button" href="${h.url_for( controller='library_common', action='undelete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Undelete this folder</a>
%endif
@@ -416,7 +416,7 @@
%endif
<td>
%if folder.description:
- ${folder.description}
+ ${folder.description | h}
%endif
<td colspan="3"></td></tr>
@@ -504,7 +504,7 @@
return str( self.count )
%>
- <h2>Data Library “${library.name}”</h2>
+ <h2>Data Library “${library.name | h}”</h2><ul class="manage-table-actions">
%if not library.deleted and ( is_admin or can_add ):
@@ -517,7 +517,7 @@
%if not library.deleted:
%if can_modify:
<a class="action-button" href="${h.url_for( controller='library_common', action='library_info', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit information</a>
- <a class="action-button" confirm="Click OK to delete the library named '${library.name}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library' )}">Delete this data library</a>
+ <a class="action-button" confirm="Click OK to delete the library named '${library.name | h}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library' )}">Delete this data library</a>
%if show_deleted:
<a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=False )}">Hide deleted items</a>
%else:
@@ -555,7 +555,7 @@
%if library.synopsis not in [ '', 'None', None ]:
<div class="libraryItemBody">
- ${library.synopsis}
+ ${library.synopsis | h}
</div>
%endif
@@ -610,6 +610,6 @@
${render_compression_types_help( comptypes )}
%endif
%if not has_accessible_folders:
- The data library '${library.name}' does not contain any datasets that you can access.
+ The data library '${library.name | h}' does not contain any datasets that you can access.
%endif
</%def>
diff -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a -r 07404a82972d877b5529fffaeb3e7e05b69a02a3 templates/webapps/galaxy/library/common/browse_library_opt.mako
--- a/templates/webapps/galaxy/library/common/browse_library_opt.mako
+++ b/templates/webapps/galaxy/library/common/browse_library_opt.mako
@@ -228,29 +228,29 @@
%if current_version and ( not ldda.library_dataset.deleted or show_deleted ):
<tr class="datasetRow"
%if parent is not None:
- parent="${parent}"
+ parent="${parent | h}"
%endif
- id="libraryItem-${ldda.id}">
+ id="libraryItem-${ldda.id | h}"><td style="padding-left: ${pad+20}px;">
- <input style="float: left;" type="checkbox" name="ldda_ids" id="${trans.security.encode_id( ldda.id )}" value="${trans.security.encode_id( ldda.id )}"
+ <input style="float: left;" type="checkbox" name="ldda_ids" id="${trans.security.encode_id( ldda.id ) | h}" value="${trans.security.encode_id( ldda.id ) | h}"
%if selected:
checked="checked"
%endif
/>
%if simple:
- <label for="${trans.security.encode_id( ldda.id )}">${ util.unicodify( ldda.name )}</label>
+ <label for="${trans.security.encode_id( ldda.id ) | h}">${ util.unicodify( ldda.name ) | h}</label>
%else:
- <div style="float: left; margin-left: 1px;" class="menubutton split popup" id="dataset-${ldda.id}-popup">
+ <div style="float: left; margin-left: 1px;" class="menubutton split popup" id="dataset-${ldda.id | h}-popup"><a class="view-info" href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">
%if ldda.library_dataset.deleted:
- <div class="libraryItem-error">${util.unicodify( ldda.name )}</div>
+ <div class="libraryItem-error">${util.unicodify( ldda.name ) | h}</div>
%else:
- ${util.unicodify( ldda.name )}
+ ${util.unicodify( ldda.name ) | h}
%endif
</a></div>
%if not library.deleted:
- <div popupmenu="dataset-${ldda.id}-popup">
+ <div popupmenu="dataset-${ldda.id | h}-popup">
%if not branch_deleted( folder ) and not ldda.library_dataset.deleted and can_modify:
<a class="action-button" href="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit information</a><a class="action-button" href="${h.url_for( controller='library_common', action='move_library_item', cntrller=cntrller, item_type='ldda', item_id=trans.security.encode_id( ldda.id ), source_library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Move this dataset</a>
@@ -279,7 +279,7 @@
%endif
%if can_modify:
%if not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.deleted:
- <a class="action-button" confirm="Click OK to delete dataset '${util.unicodify( ldda.name )}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Delete this dataset</a>
+ <a class="action-button" confirm="Click OK to delete dataset '${util.unicodify( ldda.name ) | h}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Delete this dataset</a>
%elif not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.purged and ldda.library_dataset.deleted:
<a class="action-button" href="${h.url_for( controller='library_common', action='undelete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Undelete this dataset</a>
%endif
@@ -290,10 +290,10 @@
</td>
% if not simple:
<td id="libraryItemInfo">${render_library_item_info( ldda )}</td>
- <td>${ldda.extension}</td>
+ <td>${ldda.extension | h}</td>
% endif
- <td>${ldda.create_time.strftime( "%Y-%m-%d" )}</td>
- <td>${ldda.get_size( nice_size=True )}</td>
+ <td>${ldda.create_time.strftime( "%Y-%m-%d" ) | h}</td>
+ <td>${ldda.get_size( nice_size=True ) | h}</td></tr><%
my_row = row_counter.count
@@ -362,28 +362,28 @@
%>
%if not root_folder and ( not folder.deleted or show_deleted ):
<% encoded_id = trans.security.encode_id( folder.id ) %>
- <tr id="folder-${encoded_id}" class="folderRow libraryOrFolderRow"
+ <tr id="folder-${encoded_id | h}" class="folderRow libraryOrFolderRow"
%if parent is not None:
- parent="${parent}"
+ parent="${parent | h}"
style="display: none;"
%endif
>
- <td style="padding-left: ${folder_pad}px;">
+ <td style="padding-left: ${folder_pad | h}px;"><input type="checkbox" class="folderCheckbox"/>
- <span class="expandLink folder-${encoded_id}-click">
- <div style="float: left; margin-left: 2px;" class="menubutton split popup" id="folder_img-${folder.id}-popup">
- <a class="folder-${encoded_id}-click" href="javascript:void(0);">
+ <span class="expandLink folder-${encoded_id | h}-click">
+ <div style="float: left; margin-left: 2px;" class="menubutton split popup" id="folder_img-${folder.id | h}-popup">
+ <a class="folder-${encoded_id | h}-click" href="javascript:void(0);"><span class="rowIcon"></span>
%if folder.deleted:
- <div class="libraryItem-error">${folder.name}</div>
+ <div class="libraryItem-error">${folder.name | h}</div>
%else:
- ${folder.name}
+ ${folder.name | h}
%endif
</a></div></span>
%if not library.deleted:
- <div popupmenu="folder_img-${folder.id}-popup">
+ <div popupmenu="folder_img-${folder.id | h}-popup">
%if not branch_deleted( folder ) and can_add:
<a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add datasets</a><a class="action-button" href="${h.url_for( controller='library_common', action='create_folder', cntrller=cntrller, parent_id=trans.security.encode_id( folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add sub-folder</a>
@@ -414,7 +414,7 @@
%endif
%if can_modify:
%if not library.deleted and not folder.deleted:
- <a class="action-button" confirm="Click OK to delete the folder '${folder.name}.'" href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Delete this folder</a>
+ <a class="action-button" confirm="Click OK to delete the folder '${folder.name | h}.'" href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Delete this folder</a>
%elif not library.deleted and folder.deleted and not folder.purged:
<a class="action-button" href="${h.url_for( controller='library_common', action='undelete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Undelete this folder</a>
%endif
@@ -423,7 +423,7 @@
%endif
<td>
%if folder.description:
- ${folder.description}
+ ${folder.description | h}
%endif
<td colspan="3"></td></tr>
@@ -515,12 +515,12 @@
<li><a class="action-button" href="${h.url_for( controller='library_common', action='create_folder', cntrller=cntrller, parent_id=trans.security.encode_id( library.root_folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add folder</a></li>
%endif
%if ( ( not library.deleted ) and ( can_modify or can_manage ) ) or ( can_modify and not library.purged ) or ( library.purged ):
- <li><a class="action-button" id="library-${library.id}-popup" class="menubutton">Library Actions</a></li>
- <div popupmenu="library-${library.id}-popup">
+ <li><a class="action-button" id="library-${library.id | h}-popup" class="menubutton">Library Actions</a></li>
+ <div popupmenu="library-${library.id | h}-popup">
%if not library.deleted:
%if can_modify:
<a class="action-button" href="${h.url_for( controller='library_common', action='library_info', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit information</a>
- <a class="action-button" confirm="Click OK to delete the library named '${library.name}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library' )}">Delete this data library</a>
+ <a class="action-button" confirm="Click OK to delete the library named '${library.name | h}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library' )}">Delete this data library</a>
%if show_deleted:
<a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=False )}">Hide deleted items</a>
%else:
@@ -558,7 +558,7 @@
%if library.synopsis not in [ '', 'None', None ]:
<div class="libraryItemBody">
- ${library.synopsis}
+ ${library.synopsis | h}
</div>
%endif
@@ -616,6 +616,6 @@
${render_compression_types_help( comptypes )}
%endif
%if not has_accessible_folders:
- The data library '${library.name}' does not contain any datasets that you can access.
+ The data library '${library.name | h}' does not contain any datasets that you can access.
%endif
</%def>
diff -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a -r 07404a82972d877b5529fffaeb3e7e05b69a02a3 templates/webapps/galaxy/library/common/common.mako
--- a/templates/webapps/galaxy/library/common/common.mako
+++ b/templates/webapps/galaxy/library/common/common.mako
@@ -88,19 +88,19 @@
else:
tool_form_title = 'Upload files'
%>
- <div class="toolFormTitle">${tool_form_title}</div>
+ <div class="toolFormTitle">${tool_form_title | h}</div><div class="toolFormBody"><form name="upload_library_dataset" id="upload_library_dataset" action="${action}" enctype="multipart/form-data" method="post"><input type="hidden" name="tool_id" value="upload1"/><input type="hidden" name="tool_state" value="None"/>
- <input type="hidden" name="cntrller" value="${cntrller}"/>
- <input type="hidden" name="library_id" value="${library_id}"/>
- <input type="hidden" name="folder_id" value="${folder_id}"/>
- <input type="hidden" name="show_deleted" value="${show_deleted}"/>
+ <input type="hidden" name="cntrller" value="${cntrller | h}"/>
+ <input type="hidden" name="library_id" value="${library_id | h}"/>
+ <input type="hidden" name="folder_id" value="${folder_id | h}"/>
+ <input type="hidden" name="show_deleted" value="${show_deleted | h}"/>
%if replace_dataset not in [ None, 'None' ]:
- <input type="hidden" name="replace_id" value="${trans.security.encode_id( replace_dataset.id )}"/>
+ <input type="hidden" name="replace_id" value="${trans.security.encode_id( replace_dataset.id ) | h}"/><div class="form-row">
- You are currently selecting a new file to replace '<a href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=library_id, folder_id=folder_id, id=trans.security.encode_id( replace_dataset.library_dataset_dataset_association.id ) )}">${util.unicodify( replace_dataset.name )}</a>'.
+ You are currently selecting a new file to replace '<a href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=library_id, folder_id=folder_id, id=trans.security.encode_id( replace_dataset.library_dataset_dataset_association.id ) )}">${util.unicodify( replace_dataset.name ) | h}</a>'.
<div style="clear: both"></div></div>
%endif
@@ -120,7 +120,7 @@
<select name="file_type"><option value="auto" selected>Auto-detect</option>
%for file_format in file_formats:
- <option value="${file_format}">${file_format}</option>
+ <option value="${file_format | h}">${file_format | h}</option>
%endfor
</select></div>
@@ -176,23 +176,23 @@
%for entry in os.listdir( import_dir ):
## Do not include entries that are not directories
%if os.path.isdir( os.path.join( import_dir, entry ) ):
- <option>${entry}</option>
+ <option>${entry | h}</option>
%endif
%endfor
%else:
%if ( trans.user_is_admin() and cntrller == 'library_admin' ):
- <option>${import_dir}</option>
+ <option>${import_dir | h}</option>
%else:
- <option>${trans.user.email}</option>
+ <option>${trans.user.email | h}</option>
%endif
%endif
</select></div><div class="toolParamHelp" style="clear: both;">
%if contains_directories:
- Upload all files in a sub-directory of <strong>${import_dir}</strong> on the Galaxy server.
+ Upload all files in a sub-directory of <strong>${import_dir | h}</strong> on the Galaxy server.
%else:
- Upload all files in <strong>${import_dir}</strong> on the Galaxy server.
+ Upload all files in <strong>${import_dir | h}</strong> on the Galaxy server.
%endif
</div><div style="clear: both"></div>
@@ -282,9 +282,9 @@
%>
%for dbkey in dbkeys:
%if dbkey[1] == default_selected:
- <option value="${dbkey[1]}" selected>${dbkey[0]}</option>
+ <option value="${dbkey[1] | h}" selected>${dbkey[0] | h}</option>
%else:
- <option value="${dbkey[1]}">${dbkey[0]}</option>
+ <option value="${dbkey[1] | h}">${dbkey[0] | h}</option>
%endif
%endfor
</select>
@@ -295,7 +295,7 @@
<label>Message:</label><div class="form-row-input">
%if ldda_message:
- <textarea name="ldda_message" rows="3" cols="35">${ldda_message}</textarea>
+ <textarea name="ldda_message" rows="3" cols="35">${ldda_message | h}</textarea>
%else:
<textarea name="ldda_message" rows="3" cols="35"></textarea>
%endif
@@ -320,13 +320,13 @@
%if widgets:
%for i, field in enumerate( widgets ):
<div class="form-row">
- <label>${field[ 'label' ]}</label>
+ <label>${field[ 'label' ] | h}</label><div class="form-row-input">
${field[ 'widget' ].get_html()}
</div><div class="toolParamHelp" style="clear: both;">
%if field[ 'helptext' ]:
- ${field[ 'helptext' ]}<br/>
+ ${field[ 'helptext' ] | h}<br/>
%endif
*Inherited template field
</div>
@@ -342,14 +342,14 @@
</div>
%elif upload_option == 'import_from_history':
<div class="toolForm">
- <div class="toolFormTitle">Active datasets in your current history (${ util.unicodify( history.name )})</div>
+ <div class="toolFormTitle">Active datasets in your current history (${ util.unicodify( history.name ) | h})</div><div class="toolFormBody">
%if history and history.active_datasets:
<form name="add_history_datasets_to_library" action="${h.url_for( controller='library_common', action='add_history_datasets_to_library', cntrller=cntrller, library_id=library_id )}" enctype="multipart/form-data" method="post">
- <input type="hidden" name="folder_id" value="${folder_id}"/>
- <input type="hidden" name="show_deleted" value="${show_deleted}"/>
+ <input type="hidden" name="folder_id" value="${folder_id | h}"/>
+ <input type="hidden" name="show_deleted" value="${show_deleted | h}"/><input type="hidden" name="upload_option" value="import_from_history"/>
- <input type="hidden" name="ldda_message" value="${ldda_message}"/>
+ <input type="hidden" name="ldda_message" value="${ldda_message | h}"/><%
role_ids_selected = ''
if roles_select_list:
@@ -357,32 +357,32 @@
if selected:
role_ids_selected = ','.join( selected )
%>
- <input type="hidden" name="roles" value="${role_ids_selected}"/>
+ <input type="hidden" name="roles" value="${role_ids_selected | h}"/>
%if replace_dataset not in [ None, 'None' ]:
- <input type="hidden" name="replace_id" value="${trans.security.encode_id( replace_dataset.id )}"/>
+ <input type="hidden" name="replace_id" value="${trans.security.encode_id( replace_dataset.id ) | h}"/><div class="form-row">
- You are currently selecting a new file to replace '<a href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=library_id, folder_id=folder_id, id=trans.security.encode_id( replace_dataset.library_dataset_dataset_association.id ) )}">${ util.unicodify( replace_dataset.name )}</a>'.
+ You are currently selecting a new file to replace '<a href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=library_id, folder_id=folder_id, id=trans.security.encode_id( replace_dataset.library_dataset_dataset_association.id ) )}">${ util.unicodify( replace_dataset.name ) | h}</a>'.
<div style="clear: both"></div></div>
%endif
%for hda in history.visible_datasets:
<% encoded_id = trans.security.encode_id( hda.id ) %><div class="form-row">
- <input name="hda_ids" id="hist_${encoded_id}" value="${encoded_id}" type="checkbox"/>
- <label for="hist_${encoded_id}" style="display: inline;font-weight:normal;">${hda.hid}: ${ util.unicodify( hda.name )}</label>
+ <input name="hda_ids" id="hist_${encoded_id | h}" value="${encoded_id | h}" type="checkbox"/>
+ <label for="hist_${encoded_id | h}" style="display: inline;font-weight:normal;">${hda.hid | h}: ${ util.unicodify( hda.name ) | h}</label></div>
%endfor
%if widgets:
- <input type="hidden" name="template_id" value="${template_id}"/>
+ <input type="hidden" name="template_id" value="${template_id | h}"/>
%for i, field in enumerate( widgets ):
<div class="form-row">
- <label>${field[ 'label' ]}</label>
+ <label>${field[ 'label' ] | h}</label><div class="form-row-input">
${field[ 'widget' ].get_html()}
</div><div class="toolParamHelp" style="clear: both;">
%if field[ 'helptext' ]:
- ${field[ 'helptext' ]}<br/>
+ ${field[ 'helptext' ] | h}<br/>
%endif
*Inherited template field
</div>
diff -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a -r 07404a82972d877b5529fffaeb3e7e05b69a02a3 templates/webapps/galaxy/library/common/import_datasets_to_histories.mako
--- a/templates/webapps/galaxy/library/common/import_datasets_to_histories.mako
+++ b/templates/webapps/galaxy/library/common/import_datasets_to_histories.mako
@@ -34,8 +34,8 @@
checked = " checked='checked'"
%><div class="form-row">
- <input type="checkbox" name="ldda_ids" id="dataset_${encoded_id}" value="${encoded_id}" ${checked}/>
- <label for="dataset_${encoded_id}" style="display: inline;font-weight:normal;">${util.unicodify( source_ldda.name )}</label>
+ <input type="checkbox" name="ldda_ids" id="dataset_${encoded_id | h}" value="${encoded_id | h}" ${checked}/>
+ <label for="dataset_${encoded_id | h}" style="display: inline;font-weight:normal;">${util.unicodify( source_ldda.name ) | h}</label></div>
%endfor
%else:
@@ -61,7 +61,7 @@
else:
current_history_text = ""
%>
- <option value="${encoded_id}"${selected_text}>${i + 1}: ${h.truncate( util.unicodify( target_history.name ), 30 )}${current_history_text}</option>
+ <option value="${encoded_id | h}"${selected_text}>${i + 1}: ${h.truncate( util.unicodify( target_history.name ), 30 ) | h}${current_history_text | h}</option>
%endfor
</select><br/><br/>
@@ -77,8 +77,8 @@
current_history_text = ""
%><div class="form-row">
- <input type="checkbox" name="target_history_ids" id="target_history_${encoded_id}" value="${encoded_id}"/>
- <label for="target_history_${encoded_id}" style="display: inline; font-weight:normal;">${i + 1}: ${util.unicodify( target_history.name )}${current_history_text}</label>
+ <input type="checkbox" name="target_history_ids" id="target_history_${encoded_id | h}" value="${encoded_id | h}"/>
+ <label for="target_history_${encoded_id | h}" style="display: inline; font-weight:normal;">${i + 1}: ${util.unicodify( target_history.name ) | h}${current_history_text | h}</label></div>
%endfor
</div>
diff -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a -r 07404a82972d877b5529fffaeb3e7e05b69a02a3 templates/webapps/galaxy/library/common/ldda_edit_info.mako
--- a/templates/webapps/galaxy/library/common/ldda_edit_info.mako
+++ b/templates/webapps/galaxy/library/common/ldda_edit_info.mako
@@ -34,9 +34,9 @@
<select name="datatype">
%for ext in file_formats:
%if ldda.ext == ext:
- <option value="${ext}" selected="yes">${ext}</option>
+ <option value="${ext | h}" selected="yes">${ext | h}</option>
%else:
- <option value="${ext}">${ext}</option>
+ <option value="${ext | h}">${ext | h}</option>
%endif
%endfor
</select>
@@ -44,24 +44,24 @@
%if ( trans.user_is_admin() and cntrller=='library_admin' ) or trans.app.security_agent.can_modify_library_item( current_user_roles, ldda.library_dataset ):
<div class="toolForm">
- <div class="toolFormTitle">Edit attributes of ${util.unicodify( ldda.name )}</div>
+ <div class="toolFormTitle">Edit attributes of ${util.unicodify( ldda.name ) | h}</div><div class="toolFormBody"><form name="edit_attributes" action="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=library_id, folder_id=trans.security.encode_id( ldda.library_dataset.folder.id ), use_panels=use_panels, show_deleted=show_deleted, )}" method="post">
- <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id )}"/>
+ <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id ) | h}"/><div class="form-row"><label>Name:</label>
- <input type="text" name="name" value="${util.unicodify( ldda.name )}" size="40"/>
+ <input type="text" name="name" value="${util.unicodify( ldda.name ) | h}" size="40"/><div style="clear: both"></div></div><div class="form-row"><label>Info:</label>
- <input type="text" name="info" value="${util.unicodify( ldda.info )}" size="40"/>
+ <input type="text" name="info" value="${util.unicodify( ldda.info ) | h}" size="40"/><div style="clear: both"></div></div><div class="form-row"><label>Message:</label>
%if ldda.message:
- <textarea name="message" rows="3" cols="35">${ldda.message}</textarea>
+ <textarea name="message" rows="3" cols="35">${ldda.message | h}</textarea>
%else:
<textarea name="message" rows="3" cols="35"></textarea>
%endif
@@ -73,7 +73,7 @@
%for name, spec in ldda.metadata.spec.items():
%if spec.visible:
<div class="form-row">
- <label>${spec.desc}:</label>
+ <label>${spec.desc | h}:</label>
${ldda.metadata.get_html_by_name( name, trans=trans )}
<div style="clear: both"></div></div>
@@ -85,7 +85,7 @@
</form><form name="auto_detect" action="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=library_id, folder_id=trans.security.encode_id( ldda.library_dataset.folder.id ), use_panels=use_panels, show_deleted=show_deleted, )}" method="post"><div class="form-row">
- <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id )}"/>
+ <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id ) | h}"/><input type="submit" name="detect" value="Auto-detect"/><div class="toolParamHelp" style="clear: both;">
This will inspect the dataset and attempt to correct the above column values if they are not accurate.
@@ -101,7 +101,7 @@
%if ldda.datatype.allow_datatype_change:
<form name="change_datatype" action="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=library_id, folder_id=trans.security.encode_id( ldda.library_dataset.folder.id ), use_panels=use_panels, show_deleted=show_deleted, )}" method="post"><div class="form-row">
- <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id )}"/>
+ <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id ) | h}"/><label>New Type:</label>
${datatype( ldda, file_formats )}
<div class="toolParamHelp" style="clear: both;">
@@ -129,10 +129,10 @@
<div class="form-row"><label>Extended Metadata:</label></div>
- <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id )}"/>
+ <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id ) | h}"/><div class="form-row">
%if ldda.extended_metadata:
- <textarea name="extended_metadata" rows="15" cols="35">${util.pretty_print_json(ldda.extended_metadata.data)}</textarea>
+ <textarea name="extended_metadata" rows="15" cols="35">${util.pretty_print_json(ldda.extended_metadata.data) | h}</textarea>
%else:
<textarea name="extended_metadata" rows="15" cols="35"></textarea>
%endif
@@ -147,28 +147,28 @@
<p/>
%else:
<div class="toolForm">
- <div class="toolFormTitle">View information about ${util.unicodify( ldda.name )}</div>
+ <div class="toolFormTitle">View information about ${util.unicodify( ldda.name ) | h}</div><div class="toolFormBody"><div class="form-row"><label>Name:</label>
- ${util.unicodify( ldda.name )}
+ ${util.unicodify( ldda.name ) | h}
<div style="clear: both"></div></div><div class="form-row"><label>Info:</label>
- ${util.unicodify( ldda.info )}
+ ${util.unicodify( ldda.info ) | h}
<div style="clear: both"></div></div><div class="form-row"><label>Data Format:</label>
- ${ldda.ext}
+ ${ldda.ext | h}
<div style="clear: both"></div></div>
%for name, spec in ldda.metadata.spec.items():
%if spec.visible:
<div class="form-row">
- <label>${spec.desc}:</label>
- ${ldda.metadata.get( name )}
+ <label>${spec.desc | h}:</label>
+ ${ldda.metadata.get( name ) | h}
<div style="clear: both"></div></div>
%endif
This diff is so big that we needed to truncate the remainder.
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/0f0cca84c94a/
Changeset: 0f0cca84c94a
Branch: stable
User: dan
Date: 2014-12-08 17:27:48+00:00
Summary: HTML escape user-settable values in Data Libraries. Update tests to reflect that e.g. quotes are now html escaped within pages. Eliminate the unnecessary use of Params() object for these controllers.
Affected #: 17 files
diff -r ed67b05dc0402bf9ff9fbf35198ea2cac92e2c22 -r 0f0cca84c94ac88e6e9d347a57fd08b99d7c9176 lib/galaxy/webapps/galaxy/controllers/library.py
--- a/lib/galaxy/webapps/galaxy/controllers/library.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library.py
@@ -3,7 +3,7 @@
from galaxy import web
from galaxy.model.orm import and_, not_, or_
from galaxy.web.base.controller import BaseUIController
-from galaxy.web.framework.helpers import grids
+from galaxy.web.framework.helpers import escape, grids
from library_common import get_comptypes, lucene_search, whoosh_search
@@ -79,7 +79,6 @@
@web.expose
def list( self, trans, **kwd ):
- params = util.Params( kwd )
# define app configuration for generic mako template
app = {
'jscript' : "galaxy.library"
@@ -89,10 +88,9 @@
@web.expose
def index( self, trans, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- default_action = params.get( 'default_action', None )
+ message = escape( kwd.get( 'message', '' ) )
+ status = escape( kwd.get( 'status', 'done' ) )
+ default_action = kwd.get( 'default_action', None )
return trans.fill_template( "/library/index.mako",
default_action=default_action,
message=message,
diff -r ed67b05dc0402bf9ff9fbf35198ea2cac92e2c22 -r 0f0cca84c94ac88e6e9d347a57fd08b99d7c9176 lib/galaxy/webapps/galaxy/controllers/library_admin.py
--- a/lib/galaxy/webapps/galaxy/controllers/library_admin.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library_admin.py
@@ -5,7 +5,7 @@
from galaxy import web
from galaxy.web.base.controller import BaseUIController
-from galaxy.web.framework.helpers import grids, time_ago
+from galaxy.web.framework.helpers import escape, grids, time_ago
from library_common import get_comptypes, lucene_search, whoosh_search
# from galaxy.model.orm import *
@@ -141,20 +141,19 @@
lddas=lddas,
show_deleted=show_deleted,
use_panels=use_panels,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
# Render the list view
return self.library_list_grid( trans, **kwd )
@web.expose
@web.require_admin
def create_library( self, trans, **kwd ):
- params = galaxy.util.Params( kwd )
- message = galaxy.util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- if params.get( 'create_library_button', False ):
- name = galaxy.util.restore_text( params.get( 'name', 'No name' ) )
- description = galaxy.util.restore_text( params.get( 'description', '' ) )
- synopsis = galaxy.util.restore_text( params.get( 'synopsis', '' ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ if kwd.get( 'create_library_button', False ):
+ name = kwd.get( 'name', 'No name' )
+ description = kwd.get( 'description', '' )
+ synopsis = kwd.get( 'synopsis', '' )
if synopsis in [ 'None', None ]:
synopsis = ''
library = trans.app.model.Library( name=name, description=description, synopsis=synopsis )
@@ -167,9 +166,9 @@
action='browse_library',
cntrller='library_admin',
id=trans.security.encode_id( library.id ),
- message=galaxy.util.sanitize_text( message ),
+ message=message,
status='done' ) )
- return trans.fill_template( '/admin/library/new_library.mako', message=message, status=status )
+ return trans.fill_template( '/admin/library/new_library.mako', message=escape( message ), status=escape( status ) )
@web.expose
@web.require_admin
def delete_library( self, trans, id, **kwd ):
@@ -196,8 +195,7 @@
# TODO: change this function to purge_library_item, behaving similar to delete_library_item
# assuming we want the ability to purge libraries.
# This function is currently only used by the functional tests.
- params = galaxy.util.Params( kwd )
- library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( params.id ) )
+ library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( kwd.get( 'id' ) ) )
def purge_folder( library_folder ):
for lf in library_folder.folders:
purge_folder( lf )
@@ -226,7 +224,7 @@
message = "Library '%s' has not been marked deleted, so it cannot be purged" % ( library.name )
return trans.response.send_redirect( web.url_for( controller='library_admin',
action='browse_libraries',
- message=galaxy.util.sanitize_text( message ),
+ message=message,
status='error' ) )
else:
purge_folder( library.root_folder )
@@ -236,5 +234,5 @@
message = "Library '%s' and all of its contents have been purged, datasets will be removed from disk via the cleanup_datasets script" % library.name
return trans.response.send_redirect( web.url_for( controller='library_admin',
action='browse_libraries',
- message=galaxy.util.sanitize_text( message ),
+ message=message,
status='done' ) )
diff -r ed67b05dc0402bf9ff9fbf35198ea2cac92e2c22 -r 0f0cca84c94ac88e6e9d347a57fd08b99d7c9176 lib/galaxy/webapps/galaxy/controllers/library_common.py
--- a/lib/galaxy/webapps/galaxy/controllers/library_common.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library_common.py
@@ -20,6 +20,7 @@
from galaxy.util.streamball import StreamBall
from galaxy.web.base.controller import BaseUIController, UsesFormDefinitionsMixin, UsesExtendedMetadataMixin, UsesLibraryMixinItems
from galaxy.web.form_builder import AddressField, CheckboxField, SelectField, build_select_field
+from galaxy.web.framework.helpers import escape
from galaxy.model.orm import and_, eagerload_all
# Whoosh is compatible with Python 2.5+ Try to import Whoosh and set flag to indicate whether tool search is enabled.
@@ -92,14 +93,13 @@
@web.expose
def browse_library( self, trans, cntrller='library', **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
# If use_panels is True, the library is being accessed via an external link
# which did not originate from within the Galaxy instance, and the library will
# be displayed correctly with the mast head.
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- library_id = params.get( 'id', None )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ library_id = kwd.get( 'id', None )
if not library_id:
# To handle bots
message = "You must specify a library id."
@@ -116,9 +116,9 @@
message = "Invalid library id ( %s ) specified." % str( library_id )
status = 'error'
else:
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- created_ldda_ids = params.get( 'created_ldda_ids', '' )
- hidden_folder_ids = util.listify( params.get( 'hidden_folder_ids', '' ) )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ created_ldda_ids = kwd.get( 'created_ldda_ids', '' )
+ hidden_folder_ids = util.listify( kwd.get( 'hidden_folder_ids', '' ) )
if created_ldda_ids and not message:
message = "%d datasets are uploading in the background to the library '%s' (each is selected). " % \
( len( created_ldda_ids.split( ',' ) ), library.name )
@@ -137,8 +137,8 @@
show_deleted=show_deleted,
comptypes=comptypes,
current_user_roles=current_user_roles,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
else:
return trans.fill_template( 'library/common/browse_library.mako',
cntrller=cntrller,
@@ -149,44 +149,43 @@
show_deleted=show_deleted,
comptypes=comptypes,
current_user_roles=current_user_roles,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
except Exception, e:
message = 'Error attempting to display contents of library (%s): %s.' % ( str( library.name ), str( e ) )
status = 'error'
- default_action = params.get( 'default_action', None )
+ default_action = kwd.get( 'default_action', None )
return trans.response.send_redirect( web.url_for( use_panels=use_panels,
controller=cntrller,
action='browse_libraries',
default_action=default_action,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
@web.expose
def library_info( self, trans, cntrller, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
- library_id = params.get( 'id', None )
+ library_id = kwd.get( 'id', None )
try:
library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) )
except:
library = None
self._check_access( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'library_info_button', False ):
+ if kwd.get( 'library_info_button', False ):
self._check_modify( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted )
old_name = library.name
- new_name = util.restore_text( params.get( 'name', 'No name' ) )
+ new_name = kwd.get( 'name', 'No name' )
if not new_name:
message = 'Enter a valid name'
status='error'
else:
- new_description = util.restore_text( params.get( 'description', '' ) )
- new_synopsis = util.restore_text( params.get( 'synopsis', '' ) )
+ new_description = kwd.get( 'description', '' )
+ new_synopsis = kwd.get( 'synopsis', '' )
if new_synopsis in [ None, 'None' ]:
new_synopsis = ''
library.name = new_name
@@ -204,7 +203,7 @@
use_panels=use_panels,
id=trans.security.encode_id( library.id ),
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
# See if we have any associated templates
info_association, inherited = library.get_info_association()
@@ -220,29 +219,28 @@
show_deleted=show_deleted,
info_association=info_association,
inherited=inherited,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def library_permissions( self, trans, cntrller, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
- library_id = params.get( 'id', None )
+ library_id = kwd.get( 'id', None )
try:
library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) )
except:
library = None
self._check_access( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted )
self._check_manage( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'update_roles_button', False ):
+ if kwd.get( 'update_roles_button', False ):
# The user clicked the Save button on the 'Associate With Roles' form
permissions = {}
for k, v in trans.app.model.Library.permitted_actions.items():
- in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( params.get( k + '_in', [] ) ) ]
+ in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( kwd.get( k + '_in', [] ) ) ]
permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
trans.app.security_agent.set_all_library_permissions( trans, library, permissions )
trans.sa_session.refresh( library )
@@ -255,7 +253,7 @@
use_panels=use_panels,
id=trans.security.encode_id( library.id ),
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
roles = trans.app.security_agent.get_legitimate_roles( trans, library, cntrller )
all_roles = trans.app.security_agent.get_all_roles( trans, cntrller )
@@ -267,15 +265,14 @@
roles=roles,
all_roles=all_roles,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def create_folder( self, trans, cntrller, parent_id, library_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller in ( 'library_admin', 'api' )
current_user_roles = trans.get_current_user_roles()
try:
@@ -288,9 +285,9 @@
parent_library = parent_folder.parent_library
self._check_access( trans, cntrller, is_admin, parent_folder, current_user_roles, use_panels, library_id, show_deleted )
self._check_add( trans, cntrller, is_admin, parent_folder, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'new_folder_button', False ) or cntrller == 'api':
- new_folder = trans.app.model.LibraryFolder( name=util.restore_text( params.name ),
- description=util.restore_text( params.description ) )
+ if kwd.get( 'new_folder_button', False ) or cntrller == 'api':
+ new_folder = trans.app.model.LibraryFolder( name=kwd.get( 'name', '' ),
+ description=kwd.get( 'description', '' ) )
# We are associating the last used genome build with folders, so we will always
# initialize a new folder with the first dbkey in genome builds list which is currently
# ? unspecified (?)
@@ -322,7 +319,7 @@
show_deleted=show_deleted,
info_association=info_association,
inherited=inherited,
- message=message,
+ message=escape( message ),
status='done' )
# If not inheritable info_association, redirect to the library.
message = "The new folder named '%s' has been added to the data library." % new_folder.name
@@ -334,7 +331,7 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
# We do not render any template widgets on creation pages since saving the info_association
# cannot occur before the associated item is saved.
@@ -344,15 +341,14 @@
library_id=library_id,
folder=parent_folder,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def folder_info( self, trans, cntrller, id, library_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
try:
@@ -360,11 +356,11 @@
except:
folder = None
self._check_access( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'rename_folder_button', False ):
+ if kwd.get( 'rename_folder_button', False ):
self._check_modify( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted )
old_name = folder.name
- new_name = util.restore_text( params.name )
- new_description = util.restore_text( params.description )
+ new_name = kwd.get( 'name', '' )
+ new_description = kwd.get( 'description', '' )
if not new_name:
message = 'Enter a valid name'
status='error'
@@ -381,7 +377,7 @@
id=id,
library_id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
# See if we have any associated templates
widgets = []
@@ -401,15 +397,14 @@
show_deleted=show_deleted,
info_association=info_association,
inherited=inherited,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def folder_permissions( self, trans, cntrller, id, library_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
try:
@@ -418,14 +413,14 @@
folder = None
self._check_access( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted )
self._check_manage( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'update_roles_button', False ):
+ if kwd.get( 'update_roles_button', False ):
# The user clicked the Save button on the 'Associate With Roles' form
permissions = {}
for k, v in trans.app.model.Library.permitted_actions.items():
if k != 'LIBRARY_ACCESS':
# LIBRARY_ACCESS is a special permission set only at the library level
# and it is not inherited.
- in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( int( x ) ) for x in util.listify( params.get( k + '_in', [] ) ) ]
+ in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( int( x ) ) for x in util.listify( kwd.get( k + '_in', [] ) ) ]
permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
trans.app.security_agent.set_all_library_permissions( trans, folder, permissions )
trans.sa_session.refresh( folder )
@@ -437,7 +432,7 @@
id=trans.security.encode_id( folder.id ),
library_id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
# If the library is public all roles are legitimate, but if the library
# is restricted, only those roles associated with the LIBRARY_ACCESS
@@ -451,15 +446,14 @@
current_user_roles=current_user_roles,
roles=roles,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def ldda_edit_info( self, trans, cntrller, library_id, folder_id, id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
try:
@@ -468,7 +462,7 @@
ldda = None
self._check_access( trans, cntrller, is_admin, ldda, current_user_roles, use_panels, library_id, show_deleted )
self._check_modify( trans, cntrller, is_admin, ldda, current_user_roles, use_panels, library_id, show_deleted )
- dbkey = params.get( 'dbkey', '?' )
+ dbkey = kwd.get( 'dbkey', '?' )
if isinstance( dbkey, list ):
dbkey = dbkey[0]
file_formats = [ dtype_name for dtype_name, dtype_value in trans.app.datatypes_registry.datatypes_by_extension.iteritems() if dtype_value.allow_datatype_change ]
@@ -492,26 +486,26 @@
info_association, inherited = ldda.get_info_association()
if info_association and ( not( inherited ) or info_association.inheritable ):
widgets = ldda.get_template_widgets( trans )
- if params.get( 'change', False ):
+ if kwd.get( 'change', False ):
# The user clicked the Save button on the 'Change data type' form
if __ok_to_edit_metadata( ldda.id ):
- if ldda.datatype.allow_datatype_change and trans.app.datatypes_registry.get_datatype_by_extension( params.datatype ).allow_datatype_change:
- trans.app.datatypes_registry.change_datatype( ldda, params.datatype )
+ if ldda.datatype.allow_datatype_change and trans.app.datatypes_registry.get_datatype_by_extension( kwd.get( 'datatype' ) ).allow_datatype_change:
+ trans.app.datatypes_registry.change_datatype( ldda, kwd.get( 'datatype' ) )
trans.sa_session.flush()
message = "Data type changed for library dataset '%s'." % ldda.name
status = 'done'
else:
- message = "You are unable to change datatypes in this manner. Changing %s to %s is not allowed." % ( ldda.extension, params.datatype )
+ message = "You are unable to change datatypes in this manner. Changing %s to %s is not allowed." % ( ldda.extension, kwd.get( 'datatype' ) )
status = 'error'
else:
message = "This dataset is currently being used as input or output. You cannot change datatype until the jobs have completed or you have canceled them."
status = "error"
- elif params.get( 'save', False ):
+ elif kwd.get( 'save', False ):
# The user clicked the Save button on the 'Edit Attributes' form
old_name = ldda.name
- new_name = util.restore_text( params.get( 'name', '' ) )
- new_info = util.restore_text( params.get( 'info', '' ) )
- new_message = util.restore_text( params.get( 'message', '' ) )
+ new_name = kwd.get( 'name', '' )
+ new_info = kwd.get( 'info', '' )
+ new_message = kwd.get( 'message', '' )
if not new_name:
message = 'Enter a valid name'
status = 'error'
@@ -524,12 +518,12 @@
for name, spec in ldda.datatype.metadata_spec.items():
if spec.get("readonly"):
continue
- optional = params.get( "is_" + name, None )
+ optional = kwd.get( "is_" + name, None )
if optional and optional == 'true':
# optional element... == 'true' actually means it is NOT checked (and therefore ommitted)
setattr( ldda.metadata, name, None )
else:
- setattr( ldda.metadata, name, spec.unwrap( params.get ( name, None ) ) )
+ setattr( ldda.metadata, name, spec.unwrap( kwd.get( name, None ) ) )
ldda.metadata.dbkey = dbkey
ldda.datatype.after_setting_metadata( ldda )
message = "Attributes updated for library dataset '%s'." % ldda.name
@@ -538,7 +532,7 @@
message = "Attributes updated, but metadata could not be changed because this dataset is currently being used as input or output. You must cancel or wait for these jobs to complete before changing metadata."
status = 'warning'
trans.sa_session.flush()
- elif params.get( 'detect', False ):
+ elif kwd.get( 'detect', False ):
# The user clicked the Auto-detect button on the 'Edit Attributes' form
if __ok_to_edit_metadata( ldda.id ):
for name, spec in ldda.datatype.metadata_spec.items():
@@ -553,8 +547,8 @@
message = "This dataset is currently being used as input or output. You cannot change metadata until the jobs have completed or you have canceled them."
status = 'error'
trans.sa_session.flush()
- elif params.get( 'change_extended_metadata', False):
- em_string = util.restore_text( params.get("extended_metadata", "") )
+ elif kwd.get( 'change_extended_metadata', False):
+ em_string = kwd.get("extended_metadata", "" )
if len(em_string):
payload = None
try:
@@ -604,16 +598,15 @@
show_deleted=show_deleted,
info_association=info_association,
inherited=inherited,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def ldda_info( self, trans, cntrller, library_id, folder_id, id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- show_associated_hdas_and_lddas = util.string_as_bool( params.get( 'show_associated_hdas_and_lddas', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ show_associated_hdas_and_lddas = util.string_as_bool( kwd.get( 'show_associated_hdas_and_lddas', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( id ) )
@@ -653,15 +646,14 @@
current_user_roles=current_user_roles,
info_association=info_association,
inherited=inherited,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def ldda_permissions( self, trans, cntrller, library_id, folder_id, id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
ids = util.listify( id )
lddas = []
libraries = []
@@ -685,7 +677,7 @@
id=library_id,
cntrller=cntrller,
use_panels=use_panels,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
# If access to the dataset is restricted, then use the roles associated with the DATASET_ACCESS permission to
# determine the legitimate roles. If the dataset is public, see if access to the library is restricted. If
@@ -698,7 +690,7 @@
roles = trans.app.security_agent.get_legitimate_roles( trans, library, cntrller )
else:
roles = trans.app.security_agent.get_legitimate_roles( trans, ldda.dataset, cntrller )
- if params.get( 'update_roles_button', False ):
+ if kwd.get( 'update_roles_button', False ):
# Dataset permissions
access_action = trans.app.security_agent.get_action( trans.app.security_agent.permitted_actions.DATASET_ACCESS.action )
manage_permissions_action = trans.app.security_agent.get_action( trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS.action )
@@ -755,8 +747,8 @@
library_id=library_id,
roles=roles,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
if len( ids ) > 1:
# Ensure that the permissions across all library items are identical, otherwise we can't update them together.
check_list = []
@@ -781,7 +773,7 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
# Display permission form, permissions will be updated for all lddas simultaneously.
return trans.fill_template( "/library/common/ldda_permissions.mako",
@@ -791,31 +783,30 @@
library_id=library_id,
roles=roles,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def upload_library_dataset( self, trans, cntrller, library_id, folder_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- ldda_message = util.restore_text( params.get( 'ldda_message', '' ) )
- deleted = util.string_as_bool( params.get( 'deleted', False ) )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- replace_id = params.get( 'replace_id', None )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ ldda_message = kwd.get( 'ldda_message', '' )
+ deleted = util.string_as_bool( kwd.get( 'deleted', False ) )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ replace_id = kwd.get( 'replace_id', None )
replace_dataset = None
- upload_option = params.get( 'upload_option', 'upload_file' )
- if params.get( 'files_0|space_to_tab', False ):
- space_to_tab = params.get( 'files_0|space_to_tab', '' )
+ upload_option = kwd.get( 'upload_option', 'upload_file' )
+ if kwd.get( 'files_0|space_to_tab', False ):
+ space_to_tab = kwd.get( 'files_0|space_to_tab', '' )
else:
- space_to_tab = params.get( 'space_to_tab', '' )
- link_data_only = params.get( 'link_data_only', 'copy_files' )
- dbkey = params.get( 'dbkey', '?' )
+ space_to_tab = kwd.get( 'space_to_tab', '' )
+ link_data_only = kwd.get( 'link_data_only', 'copy_files' )
+ dbkey = kwd.get( 'dbkey', '?' )
if isinstance( dbkey, list ):
last_used_build = dbkey[0]
else:
last_used_build = dbkey
- roles = params.get( 'roles', '' )
+ roles = kwd.get( 'roles', '' )
is_admin = trans.user_is_admin() and cntrller in ( 'library_admin', 'api' )
current_user_roles = trans.get_current_user_roles()
widgets = []
@@ -844,7 +835,7 @@
library = folder.parent_library
if folder and last_used_build in [ 'None', None, '?' ]:
last_used_build = folder.genome_build
- if params.get( 'runtool_btn', False ) or params.get( 'ajax_upload', False ) or cntrller == 'api':
+ if kwd.get( 'runtool_btn', False ) or kwd.get( 'ajax_upload', False ) or cntrller == 'api':
error = False
if upload_option == 'upload_paths' and not trans.app.config.allow_library_path_paste:
error = True
@@ -869,7 +860,7 @@
replace_id=replace_id,
upload_option=upload_option,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
else:
# See if we have any inherited templates.
@@ -883,7 +874,7 @@
for index, widget_dict in enumerate( widgets ):
widget = widget_dict[ 'widget' ]
if isinstance( widget, AddressField ):
- value = util.restore_text( params.get( widget.name, '' ) )
+ value = kwd.get( widget.name, '' )
if value == 'new':
if self.field_param_values_ok( widget.name, 'AddressField', **kwd ):
# Save the new address
@@ -966,7 +957,7 @@
default_action=default_action,
created_ldda_ids=created_ldda_ids,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
else:
created_ldda_ids = ''
@@ -981,7 +972,7 @@
id=library_id,
created_ldda_ids=created_ldda_ids,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
# Note: if the upload form was submitted due to refresh_on_change for a form field, we cannot re-populate
# the field for the selected file ( files_0|file_data ) if the user selected one. This is because the value
@@ -1040,8 +1031,8 @@
link_data_only=link_data_only,
show_deleted=show_deleted,
ldda_message=ldda_message,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
def upload_dataset( self, trans, cntrller, library_id, folder_id, replace_dataset=None, **kwd ):
# Set up the traditional tool state/params
tool_id = 'upload1'
@@ -1054,16 +1045,15 @@
if input.type == "upload_dataset":
dataset_upload_inputs.append( input )
# Library-specific params
- params = util.Params( kwd ) # is this filetoolparam safe?
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- server_dir = util.restore_text( params.get( 'server_dir', '' ) )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ server_dir = kwd.get( 'server_dir', '' )
if replace_dataset not in [ None, 'None' ]:
replace_id = trans.security.encode_id( replace_dataset.id )
else:
replace_id = None
- upload_option = params.get( 'upload_option', 'upload_file' )
+ upload_option = kwd.get( 'upload_option', 'upload_file' )
response_code = 200
if upload_option == 'upload_directory':
if server_dir in [ None, 'None', '' ]:
@@ -1092,7 +1082,7 @@
try:
# FIXME: instead of passing params here ( which have been processed by util.Params(), the original kwd
# should be passed so that complex objects that may have been included in the initial request remain.
- library_bunch = upload_common.handle_library_params( trans, params, folder_id, replace_dataset )
+ library_bunch = upload_common.handle_library_params( trans, kwd, folder_id, replace_dataset )
except:
response_code = 500
message = "Unable to parse upload parameters, please report this error."
@@ -1103,9 +1093,9 @@
tool_params = upload_common.persist_uploads( tool_params )
uploaded_datasets = upload_common.get_uploaded_datasets( trans, cntrller, tool_params, precreated_datasets, dataset_upload_inputs, library_bunch=library_bunch )
elif upload_option == 'upload_directory':
- uploaded_datasets, response_code, message = self.get_server_dir_uploaded_datasets( trans, cntrller, params, full_dir, import_dir_desc, library_bunch, response_code, message )
+ uploaded_datasets, response_code, message = self.get_server_dir_uploaded_datasets( trans, cntrller, kwd, full_dir, import_dir_desc, library_bunch, response_code, message )
elif upload_option == 'upload_paths':
- uploaded_datasets, response_code, message = self.get_path_paste_uploaded_datasets( trans, cntrller, params, library_bunch, response_code, message )
+ uploaded_datasets, response_code, message = self.get_path_paste_uploaded_datasets( trans, cntrller, kwd, library_bunch, response_code, message )
upload_common.cleanup_unused_precreated_datasets( precreated_datasets )
if upload_option == 'upload_file' and not uploaded_datasets:
response_code = 400
@@ -1121,7 +1111,7 @@
replace_id=replace_id,
upload_option=upload_option,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
json_file_path = upload_common.create_paramfile( trans, uploaded_datasets )
data_list = [ ud.data for ud in uploaded_datasets ]
@@ -1135,7 +1125,7 @@
def make_library_uploaded_dataset( self, trans, cntrller, params, name, path, type, library_bunch, in_folder=None ):
link_data_only = params.get( 'link_data_only', 'copy_files' )
uuid_str = params.get( 'uuid', None )
- file_type = params.file_type
+ file_type = params.get( 'file_type' )
library_bunch.replace_dataset = None # not valid for these types of upload
uploaded_dataset = util.bunch.Bunch()
new_name = name
@@ -1151,8 +1141,8 @@
uploaded_dataset.type = type
uploaded_dataset.ext = None
uploaded_dataset.file_type = file_type
- uploaded_dataset.dbkey = params.dbkey
- uploaded_dataset.space_to_tab = params.space_to_tab
+ uploaded_dataset.dbkey = params.get( 'dbkey' )
+ uploaded_dataset.space_to_tab = params.get( 'space_to_tab' )
if in_folder:
uploaded_dataset.in_folder = in_folder
uploaded_dataset.data = upload_common.new_upload( trans, cntrller, uploaded_dataset, library_bunch )
@@ -1247,7 +1237,7 @@
files_and_folders.append((file_path, file, in_folder))
return files_and_folders
def _paths_list(self, params):
- return [ (l.strip(), os.path.abspath(l.strip())) for l in params.filesystem_paths.splitlines() if l.strip() ]
+ return [ (l.strip(), os.path.abspath(l.strip())) for l in params.get( 'filesystem_paths', '' ).splitlines() if l.strip() ]
def _check_path_paste_params(self, params):
if params.get( 'filesystem_paths', '' ) == '':
@@ -1259,33 +1249,32 @@
if not os.path.exists( path ):
bad_paths.append( path )
if bad_paths:
- message = "Invalid paths:<br><ul><li>%s</li></ul>" % "</li><li>".join( bad_paths )
+ message = 'Invalid paths: "%s".' % '", "'.join( bad_paths )
response_code = 400
return None, response_code, message
return None
@web.expose
def add_history_datasets_to_library( self, trans, cntrller, library_id, folder_id, hda_ids='', **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- ldda_message = util.restore_text( params.get( 'ldda_message', '' ) )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- replace_id = params.get( 'replace_id', None )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ ldda_message = kwd.get( 'ldda_message', '' )
+ show_deleted = kwd.get( 'show_deleted', False )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ replace_id = kwd.get( 'replace_id', None )
replace_dataset = None
- upload_option = params.get( 'upload_option', 'import_from_history' )
- if params.get( 'files_0|space_to_tab', False ):
- space_to_tab = params.get( 'files_0|space_to_tab', '' )
+ upload_option = kwd.get( 'upload_option', 'import_from_history' )
+ if kwd.get( 'files_0|space_to_tab', False ):
+ space_to_tab = kwd.get( 'files_0|space_to_tab', '' )
else:
- space_to_tab = params.get( 'space_to_tab', '' )
- link_data_only = params.get( 'link_data_only', 'copy_files' )
- dbkey = params.get( 'dbkey', '?' )
+ space_to_tab = kwd.get( 'space_to_tab', '' )
+ link_data_only = kwd.get( 'link_data_only', 'copy_files' )
+ dbkey = kwd.get( 'dbkey', '?' )
if isinstance( dbkey, list ):
last_used_build = dbkey[0]
else:
last_used_build = dbkey
- roles = params.get( 'roles', '' )
+ roles = kwd.get( 'roles', '' )
is_admin = trans.user_is_admin() and cntrller in ( 'library_admin', 'api' )
current_user_roles = trans.get_current_user_roles()
widgets = []
@@ -1323,9 +1312,9 @@
cntrller=cntrller,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
- if params.get( 'add_history_datasets_to_library_button', False ):
+ if kwd.get( 'add_history_datasets_to_library_button', False ):
hda_ids = util.listify( hda_ids )
if hda_ids:
dataset_names = []
@@ -1354,7 +1343,7 @@
trans.app.security_agent.copy_library_permissions( trans, folder, ldda )
trans.app.security_agent.copy_library_permissions( trans, folder, ldda.library_dataset )
else:
- library_bunch = upload_common.handle_library_params( trans, params, folder_id, replace_dataset )
+ library_bunch = upload_common.handle_library_params( trans, kwd, folder_id, replace_dataset )
if library_bunch.template and library_bunch.template_field_contents:
# Since information templates are inherited, the template fields can be displayed on the upload form.
# If the user has added field contents, we'll need to create a new form_values and info_association
@@ -1422,12 +1411,12 @@
id=library_id,
created_ldda_ids=created_ldda_ids,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
else:
message = 'Select at least one dataset from the list of active datasets in your current history'
status = 'error'
- upload_option = params.get( 'upload_option', 'import_from_history' )
+ upload_option = kwd.get( 'upload_option', 'import_from_history' )
widgets = self._get_populated_widgets( folder )
# Send list of data formats to the upload form so the "extension" select list can be populated dynamically
file_formats = trans.app.datatypes_registry.upload_file_formats
@@ -1461,8 +1450,8 @@
link_data_only=link_data_only,
show_deleted=show_deleted,
ldda_message=ldda_message,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
def _build_roles_select_list( self, trans, cntrller, library, selected_role_ids=[] ):
# Get the list of legitimate roles to display on the upload form. If the library is public,
# all active roles are legitimate. If the library is restricted by the LIBRARY_ACCESS permission, only
@@ -1521,8 +1510,7 @@
def download_dataset_from_folder( self, trans, cntrller, id, library_id=None, **kwd ):
"""Catches the dataset id and displays file contents as directed"""
show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
- params = util.Params( kwd )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
try:
@@ -1555,15 +1543,14 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
@web.expose
def library_dataset_info( self, trans, cntrller, id, library_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
try:
@@ -1571,11 +1558,11 @@
except:
library_dataset = None
self._check_access( trans, cntrller, is_admin, library_dataset, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'edit_attributes_button', False ):
+ if kwd.get( 'edit_attributes_button', False ):
self._check_modify( trans, cntrller, is_admin, library_dataset, current_user_roles, use_panels, library_id, show_deleted )
old_name = library_dataset.name
- new_name = util.restore_text( params.get( 'name', '' ) )
- new_info = util.restore_text( params.get( 'info', '' ) )
+ new_name = kwd.get( 'name', '' )
+ new_info = kwd.get( 'info', '' )
if not new_name:
message = 'Enter a valid name'
status = 'error'
@@ -1604,15 +1591,14 @@
widgets=widgets,
widget_fields_have_contents=widget_fields_have_contents,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def library_dataset_permissions( self, trans, cntrller, id, library_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
try:
@@ -1621,7 +1607,7 @@
library_dataset = None
self._check_access( trans, cntrller, is_admin, library_dataset, current_user_roles, use_panels, library_id, show_deleted )
self._check_manage( trans, cntrller, is_admin, library_dataset, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'update_roles_button', False ):
+ if kwd.get( 'update_roles_button', False ):
# The user clicked the Save button on the 'Associate With Roles' form
permissions = {}
for k, v in trans.app.model.Library.permitted_actions.items():
@@ -1652,22 +1638,21 @@
roles=roles,
current_user_roles=current_user_roles,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def make_library_item_public( self, trans, cntrller, library_id, item_type, id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
current_user_roles = trans.get_current_user_roles()
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
if item_type == 'library':
library = trans.sa_session.query( trans.model.Library ).get( trans.security.decode_id( id ) )
self._check_access( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted )
self._check_manage( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted )
- contents = util.string_as_bool( params.get( 'contents', 'False' ) )
+ contents = util.string_as_bool( kwd.get( 'contents', 'False' ) )
trans.app.security_agent.make_library_public( library, contents=contents )
if contents:
message = "The data library (%s) and all its contents have been made publicly accessible." % library.name
@@ -1694,7 +1679,7 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
@web.expose
def act_on_multiple_datasets( self, trans, cntrller, library_id=None, ldda_ids='', **kwd ):
@@ -1718,12 +1703,11 @@
rval += '%s %i %s%s %s\r\n' % ( crc, size, self.url_base, quoted_fname, relpath )
return rval
# Perform an action on a list of library datasets.
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- action = params.get( 'do_action', None )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ action = kwd.get( 'do_action', None )
lddas = []
error = False
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
@@ -1738,7 +1722,7 @@
else:
if action in [ 'import_to_current_history', 'import_to_histories' ]:
new_kwd = {}
- if action == 'import_to_current_history':
+ if current_history is not None and action == 'import_to_current_history':
encoded_current_history_id = trans.security.encode_id( current_history.id )
selected_history_id = encoded_current_history_id
new_kwd[ 'do_action' ] = action
@@ -1809,7 +1793,7 @@
folder_id=folder_id,
id=",".join( encoded_ldda_ids ),
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
else:
message = "You are not authorized to manage permissions on any of the selected datasets."
@@ -1970,11 +1954,11 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
else:
# We arrived here from the library_dataset_search_results page, so redirect there.
- search_term = params.get( 'search_term', '' )
+ search_term = kwd.get( 'search_term', '' )
comptypes = get_comptypes( trans )
return trans.fill_template( '/library/common/library_dataset_search_results.mako',
cntrller=cntrller,
@@ -1984,8 +1968,8 @@
lddas=lddas,
show_deleted=show_deleted,
use_panels=use_panels,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def import_datasets_to_histories( self, trans, cntrller, library_id='', folder_id='', ldda_ids='', target_history_id='', target_history_ids='', new_history_name='', **kwd ):
@@ -1995,12 +1979,11 @@
# - a select list option for acting on multiple selected datasets within a library
# ( ldda_ids is a comma separated string of ldda ids )
# - a menu option for a library dataset search result set ( ldda_ids is a comma separated string of ldda ids )
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- action = params.get( 'do_action', None )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ action = kwd.get( 'do_action', None )
user = trans.get_user()
current_history = trans.get_history()
if library_id:
@@ -2019,7 +2002,7 @@
target_history_ids = set( [ trans.security.decode_id( target_history_id ) for target_history_id in target_history_ids if target_history_id ] )
elif target_history_id:
target_history_ids = [ trans.security.decode_id( target_history_id ) ]
- if params.get( 'import_datasets_to_histories_button', False ):
+ if kwd.get( 'import_datasets_to_histories_button', False ):
invalid_datasets = 0
if not ldda_ids or not ( target_history_ids or new_history_name ):
message = "You must provide one or more source library datasets and one or more target histories."
@@ -2083,11 +2066,13 @@
# to the lddas in order for the menu optin to be available.
ldda = trans.sa_session.query( trans.model.LibraryDatasetDatasetAssociation ).get( ldda_id )
source_lddas.append( ldda )
+ if current_history is None:
+ current_history = trans.get_history( create=True )
if current_history is not None:
target_histories = [ current_history ]
else:
target_histories = []
- message = 'You must have a history before you can import datasets. You can do this by <a href="%s" target="_top">loading the analysis interface</a>.' % url_for(controller='root')
+ message = 'You must have a history before you can import datasets. You can do this by loading the analysis interface.'
status = 'error'
if user:
target_histories = user.active_histories
@@ -2097,7 +2082,7 @@
action='browse_library',
cntrller=cntrller,
id=library_id,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
return trans.fill_template( "/library/common/import_datasets_to_histories.mako",
cntrller=cntrller,
@@ -2111,15 +2096,14 @@
new_history_name=new_history_name,
show_deleted=show_deleted,
use_panels=use_panels,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def manage_template_inheritance( self, trans, cntrller, item_type, library_id, folder_id=None, ldda_id=None, **kwd ):
- params = util.Params( kwd )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
is_admin = ( trans.user_is_admin() and cntrller == 'library_admin' )
current_user_roles = trans.get_current_user_roles()
try:
@@ -2138,7 +2122,7 @@
cntrller=cntrller,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
info_association, inherited = item.get_info_association( restrict=True )
if info_association:
@@ -2157,7 +2141,7 @@
folder_id=folder_id,
id=id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
@web.expose
def move_library_item( self, trans, cntrller, item_type, item_id, source_library_id='', make_target_current=True, **kwd ):
@@ -2168,11 +2152,10 @@
# 'ldda' and item_id is a comma separated string of ldda ids )
# - a menu option for a library dataset search result set ( item_type is 'ldda' and item_id is a
# comma separated string of ldda ids )
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
make_target_current = util.string_as_bool( make_target_current )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
user = trans.get_user()
@@ -2186,14 +2169,14 @@
else:
# Request sent from the library_dataset_search_results page.
source_library = None
- target_library_id = params.get( 'target_library_id', '' )
+ target_library_id = kwd.get( 'target_library_id', '' )
if target_library_id not in [ '', 'none', None ]:
target_library = trans.sa_session.query( trans.model.Library ).get( trans.security.decode_id( target_library_id ) )
elif make_target_current:
target_library = source_library
else:
target_library = None
- target_folder_id = params.get( 'target_folder_id', '' )
+ target_folder_id = kwd.get( 'target_folder_id', '' )
if target_folder_id not in [ '', 'none', None ]:
target_folder = trans.sa_session.query( trans.model.LibraryFolder ).get( trans.security.decode_id( target_folder_id ) )
if target_library is None:
@@ -2208,7 +2191,7 @@
elif item_type == 'folder':
move_folder_id = item_id
move_folder = trans.sa_session.query( trans.model.LibraryFolder ).get( trans.security.decode_id( move_folder_id ) )
- if params.get( 'move_library_item_button', False ):
+ if kwd.get( 'move_library_item_button', False ):
if not ( move_ldda_ids or move_folder_id ) or target_folder_id in [ '', 'none', None ]:
message = "You must select a source folder or one or more source datasets, and a target folder."
status = 'error'
@@ -2372,8 +2355,8 @@
target_folder_id_select_field=target_folder_id_select_field,
show_deleted=show_deleted,
use_panels=use_panels,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def delete_library_item( self, trans, cntrller, library_id, item_id, item_type, **kwd ):
# This action will handle deleting all types of library items. State is saved for libraries and
@@ -2541,7 +2524,7 @@
action='browse_libraries',
cntrller=cntrller,
use_panels=use_panels,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
return trans.response.send_redirect( web.url_for( controller='library_common',
action='browse_library',
@@ -2549,7 +2532,7 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
def _check_add( self, trans, cntrller, is_admin, item, current_user_roles, use_panels, library_id, show_deleted ):
# Deny access if the user is not an admin and does not have the LIBRARY_ADD permission.
@@ -2564,7 +2547,7 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
def _check_manage( self, trans, cntrller, is_admin, item, current_user_roles, use_panels, library_id, show_deleted ):
if isinstance( item, trans.model.LibraryDataset ):
@@ -2580,7 +2563,7 @@
id=library_id,
cntrller=cntrller,
use_panels=use_panels,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
# Deny access if the user is not an admin and does not have the LIBRARY_MANAGE permission.
if not ( is_admin or trans.app.security_agent.can_manage_library_item( current_user_roles, item ) ):
@@ -2592,7 +2575,7 @@
id=library_id,
cntrller=cntrller,
use_panels=use_panels,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
def _check_modify( self, trans, cntrller, is_admin, item, current_user_roles, use_panels, library_id, show_deleted ):
# Deny modification if the user is not an admin and does not have the LIBRARY_MODIFY permission.
@@ -2606,7 +2589,7 @@
id=library_id,
use_panels=use_panels,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
# ---- Utility methods -------------------------------------------------------
@@ -2740,9 +2723,8 @@
return map( operator.getitem, intermed, ( -1, ) * len( intermed ) )
def lucene_search( trans, cntrller, search_term, search_url, **kwd ):
"""Return display of results from a full-text lucene search of data libraries."""
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
full_url = "%s/find?%s" % ( search_url, urllib.urlencode( { "kwd" : search_term } ) )
response = urllib2.urlopen( full_url )
ldda_ids = util.json.loads( response.read() )[ "ids" ]
@@ -2751,9 +2733,8 @@
return status, message, get_sorted_accessible_library_items( trans, cntrller, lddas, 'name' )
def whoosh_search( trans, cntrller, search_term, **kwd ):
"""Return display of results from a full-text whoosh search of data libraries."""
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
ok = True
if whoosh_search_enabled:
whoosh_index_dir = trans.app.config.whoosh_index_dir
diff -r ed67b05dc0402bf9ff9fbf35198ea2cac92e2c22 -r 0f0cca84c94ac88e6e9d347a57fd08b99d7c9176 templates/webapps/galaxy/library/common/browse_library.mako
--- a/templates/webapps/galaxy/library/common/browse_library.mako
+++ b/templates/webapps/galaxy/library/common/browse_library.mako
@@ -236,29 +236,29 @@
%if current_version and ( not ldda.library_dataset.deleted or show_deleted ):
<tr class="datasetRow"
%if parent is not None:
- parent="${parent}"
+ parent="${parent | h}"
%endif
- id="libraryItem-${ldda.id}">
+ id="libraryItem-${ldda.id | h}"><td style="padding-left: ${pad+20}px;">
- <input style="float: left;" type="checkbox" name="ldda_ids" id="${trans.security.encode_id( ldda.id )}" value="${trans.security.encode_id( ldda.id )}"
+ <input style="float: left;" type="checkbox" name="ldda_ids" id="${trans.security.encode_id( ldda.id ) | h}" value="${trans.security.encode_id( ldda.id ) | h}"
%if selected:
checked="checked"
%endif
/>
%if simple:
- <label for="${trans.security.encode_id( ldda.id )}">${ util.unicodify( ldda.name )}</label>
+ <label for="${trans.security.encode_id( ldda.id ) | h}">${ util.unicodify( ldda.name ) | h}</label>
%else:
- <div style="float: left; margin-left: 1px;" class="menubutton split popup" id="dataset-${ldda.id}-popup">
+ <div style="float: left; margin-left: 1px;" class="menubutton split popup" id="dataset-${ldda.id | h}-popup"><a class="view-info" href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">
%if ldda.library_dataset.deleted:
- <div class="libraryItem-error">${util.unicodify( ldda.name )}</div>
+ <div class="libraryItem-error">${util.unicodify( ldda.name ) | h}</div>
%else:
- ${util.unicodify( ldda.name )}
+ ${util.unicodify( ldda.name ) | h}
%endif
</a></div>
%if not library.deleted:
- <div popupmenu="dataset-${ldda.id}-popup">
+ <div popupmenu="dataset-${ldda.id | h}-popup">
%if not branch_deleted( folder ) and not ldda.library_dataset.deleted and can_modify:
<a class="action-button" href="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit information</a><a class="action-button" href="${h.url_for( controller='library_common', action='move_library_item', cntrller=cntrller, item_type='ldda', item_id=trans.security.encode_id( ldda.id ), source_library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Move this dataset</a>
@@ -287,7 +287,7 @@
%endif
%if can_modify:
%if not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.deleted:
- <a class="action-button" confirm="Click OK to delete dataset '${util.unicodify( ldda.name )}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Delete this dataset</a>
+ <a class="action-button" confirm="Click OK to delete dataset '${util.unicodify( ldda.name ) | h}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Delete this dataset</a>
%elif not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.purged and ldda.library_dataset.deleted:
<a class="action-button" href="${h.url_for( controller='library_common', action='undelete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Undelete this dataset</a>
%endif
@@ -298,10 +298,10 @@
</td>
% if not simple:
<td id="libraryItemInfo">${render_library_item_info( ldda )}</td>
- <td>${ldda.extension}</td>
+ <td>${ldda.extension | h}</td>
% endif
- <td>${ldda.create_time.strftime( trans.app.config.pretty_datetime_format )}</td>
- <td>${ldda.get_size( nice_size=True )}</td>
+ <td>${ldda.create_time.strftime( trans.app.config.pretty_datetime_format ) | h}</td>
+ <td>${ldda.get_size( nice_size=True ) | h}</td></tr><%
my_row = row_counter.count
@@ -355,28 +355,28 @@
%>
%if not root_folder and ( not folder.deleted or show_deleted ):
<% encoded_id = trans.security.encode_id( folder.id ) %>
- <tr id="folder-${encoded_id}" class="folderRow libraryOrFolderRow"
+ <tr id="folder-${encoded_id | h}" class="folderRow libraryOrFolderRow"
%if parent is not None:
- parent="${parent}"
+ parent="${parent | h}"
style="display: none;"
%endif
>
- <td style="padding-left: ${folder_pad}px;">
+ <td style="padding-left: ${folder_pad | h}px;"><input type="checkbox" class="folderCheckbox"/>
- <span class="expandLink folder-${encoded_id}-click">
- <div style="float: left; margin-left: 2px;" class="menubutton split popup" id="folder_img-${folder.id}-popup">
- <a class="folder-${encoded_id}-click" href="javascript:void(0);">
+ <span class="expandLink folder-${encoded_id | h}-click">
+ <div style="float: left; margin-left: 2px;" class="menubutton split popup" id="folder_img-${folder.id | h}-popup">
+ <a class="folder-${encoded_id | h}-click" href="javascript:void(0);"><span class="rowIcon"></span>
%if folder.deleted:
- <div class="libraryItem-error">${folder.name}</div>
+ <div class="libraryItem-error">${folder.name | h}</div>
%else:
- ${folder.name}
+ ${folder.name | h}
%endif
</a></div></span>
%if not library.deleted:
- <div popupmenu="folder_img-${folder.id}-popup">
+ <div popupmenu="folder_img-${folder.id | h}-popup">
%if not branch_deleted( folder ) and can_add:
<a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add datasets</a><a class="action-button" href="${h.url_for( controller='library_common', action='create_folder', cntrller=cntrller, parent_id=trans.security.encode_id( folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add sub-folder</a>
@@ -407,7 +407,7 @@
%endif
%if can_modify:
%if not library.deleted and not folder.deleted:
- <a class="action-button" confirm="Click OK to delete the folder '${folder.name}.'" href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Delete this folder</a>
+ <a class="action-button" confirm="Click OK to delete the folder '${folder.name | h}.'" href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Delete this folder</a>
%elif not library.deleted and folder.deleted and not folder.purged:
<a class="action-button" href="${h.url_for( controller='library_common', action='undelete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Undelete this folder</a>
%endif
@@ -416,7 +416,7 @@
%endif
<td>
%if folder.description:
- ${folder.description}
+ ${folder.description | h}
%endif
<td colspan="3"></td></tr>
@@ -504,7 +504,7 @@
return str( self.count )
%>
- <h2>Data Library “${library.name}”</h2>
+ <h2>Data Library “${library.name | h}”</h2><ul class="manage-table-actions">
%if not library.deleted and ( is_admin or can_add ):
@@ -517,7 +517,7 @@
%if not library.deleted:
%if can_modify:
<a class="action-button" href="${h.url_for( controller='library_common', action='library_info', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit information</a>
- <a class="action-button" confirm="Click OK to delete the library named '${library.name}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library' )}">Delete this data library</a>
+ <a class="action-button" confirm="Click OK to delete the library named '${library.name | h}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library' )}">Delete this data library</a>
%if show_deleted:
<a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=False )}">Hide deleted items</a>
%else:
@@ -555,7 +555,7 @@
%if library.synopsis not in [ '', 'None', None ]:
<div class="libraryItemBody">
- ${library.synopsis}
+ ${library.synopsis | h}
</div>
%endif
@@ -610,6 +610,6 @@
${render_compression_types_help( comptypes )}
%endif
%if not has_accessible_folders:
- The data library '${library.name}' does not contain any datasets that you can access.
+ The data library '${library.name | h}' does not contain any datasets that you can access.
%endif
</%def>
diff -r ed67b05dc0402bf9ff9fbf35198ea2cac92e2c22 -r 0f0cca84c94ac88e6e9d347a57fd08b99d7c9176 templates/webapps/galaxy/library/common/browse_library_opt.mako
--- a/templates/webapps/galaxy/library/common/browse_library_opt.mako
+++ b/templates/webapps/galaxy/library/common/browse_library_opt.mako
@@ -228,29 +228,29 @@
%if current_version and ( not ldda.library_dataset.deleted or show_deleted ):
<tr class="datasetRow"
%if parent is not None:
- parent="${parent}"
+ parent="${parent | h}"
%endif
- id="libraryItem-${ldda.id}">
+ id="libraryItem-${ldda.id | h}"><td style="padding-left: ${pad+20}px;">
- <input style="float: left;" type="checkbox" name="ldda_ids" id="${trans.security.encode_id( ldda.id )}" value="${trans.security.encode_id( ldda.id )}"
+ <input style="float: left;" type="checkbox" name="ldda_ids" id="${trans.security.encode_id( ldda.id ) | h}" value="${trans.security.encode_id( ldda.id ) | h}"
%if selected:
checked="checked"
%endif
/>
%if simple:
- <label for="${trans.security.encode_id( ldda.id )}">${ util.unicodify( ldda.name )}</label>
+ <label for="${trans.security.encode_id( ldda.id ) | h}">${ util.unicodify( ldda.name ) | h}</label>
%else:
- <div style="float: left; margin-left: 1px;" class="menubutton split popup" id="dataset-${ldda.id}-popup">
+ <div style="float: left; margin-left: 1px;" class="menubutton split popup" id="dataset-${ldda.id | h}-popup"><a class="view-info" href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">
%if ldda.library_dataset.deleted:
- <div class="libraryItem-error">${util.unicodify( ldda.name )}</div>
+ <div class="libraryItem-error">${util.unicodify( ldda.name ) | h}</div>
%else:
- ${util.unicodify( ldda.name )}
+ ${util.unicodify( ldda.name ) | h}
%endif
</a></div>
%if not library.deleted:
- <div popupmenu="dataset-${ldda.id}-popup">
+ <div popupmenu="dataset-${ldda.id | h}-popup">
%if not branch_deleted( folder ) and not ldda.library_dataset.deleted and can_modify:
<a class="action-button" href="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit information</a><a class="action-button" href="${h.url_for( controller='library_common', action='move_library_item', cntrller=cntrller, item_type='ldda', item_id=trans.security.encode_id( ldda.id ), source_library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Move this dataset</a>
@@ -279,7 +279,7 @@
%endif
%if can_modify:
%if not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.deleted:
- <a class="action-button" confirm="Click OK to delete dataset '${util.unicodify( ldda.name )}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Delete this dataset</a>
+ <a class="action-button" confirm="Click OK to delete dataset '${util.unicodify( ldda.name ) | h}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Delete this dataset</a>
%elif not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.purged and ldda.library_dataset.deleted:
<a class="action-button" href="${h.url_for( controller='library_common', action='undelete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Undelete this dataset</a>
%endif
@@ -290,10 +290,10 @@
</td>
% if not simple:
<td id="libraryItemInfo">${render_library_item_info( ldda )}</td>
- <td>${ldda.extension}</td>
+ <td>${ldda.extension | h}</td>
% endif
- <td>${ldda.create_time.strftime( "%Y-%m-%d" )}</td>
- <td>${ldda.get_size( nice_size=True )}</td>
+ <td>${ldda.create_time.strftime( "%Y-%m-%d" ) | h}</td>
+ <td>${ldda.get_size( nice_size=True ) | h}</td></tr><%
my_row = row_counter.count
@@ -362,28 +362,28 @@
%>
%if not root_folder and ( not folder.deleted or show_deleted ):
<% encoded_id = trans.security.encode_id( folder.id ) %>
- <tr id="folder-${encoded_id}" class="folderRow libraryOrFolderRow"
+ <tr id="folder-${encoded_id | h}" class="folderRow libraryOrFolderRow"
%if parent is not None:
- parent="${parent}"
+ parent="${parent | h}"
style="display: none;"
%endif
>
- <td style="padding-left: ${folder_pad}px;">
+ <td style="padding-left: ${folder_pad | h}px;"><input type="checkbox" class="folderCheckbox"/>
- <span class="expandLink folder-${encoded_id}-click">
- <div style="float: left; margin-left: 2px;" class="menubutton split popup" id="folder_img-${folder.id}-popup">
- <a class="folder-${encoded_id}-click" href="javascript:void(0);">
+ <span class="expandLink folder-${encoded_id | h}-click">
+ <div style="float: left; margin-left: 2px;" class="menubutton split popup" id="folder_img-${folder.id | h}-popup">
+ <a class="folder-${encoded_id | h}-click" href="javascript:void(0);"><span class="rowIcon"></span>
%if folder.deleted:
- <div class="libraryItem-error">${folder.name}</div>
+ <div class="libraryItem-error">${folder.name | h}</div>
%else:
- ${folder.name}
+ ${folder.name | h}
%endif
</a></div></span>
%if not library.deleted:
- <div popupmenu="folder_img-${folder.id}-popup">
+ <div popupmenu="folder_img-${folder.id | h}-popup">
%if not branch_deleted( folder ) and can_add:
<a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add datasets</a><a class="action-button" href="${h.url_for( controller='library_common', action='create_folder', cntrller=cntrller, parent_id=trans.security.encode_id( folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add sub-folder</a>
@@ -414,7 +414,7 @@
%endif
%if can_modify:
%if not library.deleted and not folder.deleted:
- <a class="action-button" confirm="Click OK to delete the folder '${folder.name}.'" href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Delete this folder</a>
+ <a class="action-button" confirm="Click OK to delete the folder '${folder.name | h}.'" href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Delete this folder</a>
%elif not library.deleted and folder.deleted and not folder.purged:
<a class="action-button" href="${h.url_for( controller='library_common', action='undelete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Undelete this folder</a>
%endif
@@ -423,7 +423,7 @@
%endif
<td>
%if folder.description:
- ${folder.description}
+ ${folder.description | h}
%endif
<td colspan="3"></td></tr>
@@ -515,12 +515,12 @@
<li><a class="action-button" href="${h.url_for( controller='library_common', action='create_folder', cntrller=cntrller, parent_id=trans.security.encode_id( library.root_folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add folder</a></li>
%endif
%if ( ( not library.deleted ) and ( can_modify or can_manage ) ) or ( can_modify and not library.purged ) or ( library.purged ):
- <li><a class="action-button" id="library-${library.id}-popup" class="menubutton">Library Actions</a></li>
- <div popupmenu="library-${library.id}-popup">
+ <li><a class="action-button" id="library-${library.id | h}-popup" class="menubutton">Library Actions</a></li>
+ <div popupmenu="library-${library.id | h}-popup">
%if not library.deleted:
%if can_modify:
<a class="action-button" href="${h.url_for( controller='library_common', action='library_info', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit information</a>
- <a class="action-button" confirm="Click OK to delete the library named '${library.name}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library' )}">Delete this data library</a>
+ <a class="action-button" confirm="Click OK to delete the library named '${library.name | h}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library' )}">Delete this data library</a>
%if show_deleted:
<a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=False )}">Hide deleted items</a>
%else:
@@ -558,7 +558,7 @@
%if library.synopsis not in [ '', 'None', None ]:
<div class="libraryItemBody">
- ${library.synopsis}
+ ${library.synopsis | h}
</div>
%endif
@@ -616,6 +616,6 @@
${render_compression_types_help( comptypes )}
%endif
%if not has_accessible_folders:
- The data library '${library.name}' does not contain any datasets that you can access.
+ The data library '${library.name | h}' does not contain any datasets that you can access.
%endif
</%def>
diff -r ed67b05dc0402bf9ff9fbf35198ea2cac92e2c22 -r 0f0cca84c94ac88e6e9d347a57fd08b99d7c9176 templates/webapps/galaxy/library/common/common.mako
--- a/templates/webapps/galaxy/library/common/common.mako
+++ b/templates/webapps/galaxy/library/common/common.mako
@@ -88,19 +88,19 @@
else:
tool_form_title = 'Upload files'
%>
- <div class="toolFormTitle">${tool_form_title}</div>
+ <div class="toolFormTitle">${tool_form_title | h}</div><div class="toolFormBody"><form name="upload_library_dataset" id="upload_library_dataset" action="${action}" enctype="multipart/form-data" method="post"><input type="hidden" name="tool_id" value="upload1"/><input type="hidden" name="tool_state" value="None"/>
- <input type="hidden" name="cntrller" value="${cntrller}"/>
- <input type="hidden" name="library_id" value="${library_id}"/>
- <input type="hidden" name="folder_id" value="${folder_id}"/>
- <input type="hidden" name="show_deleted" value="${show_deleted}"/>
+ <input type="hidden" name="cntrller" value="${cntrller | h}"/>
+ <input type="hidden" name="library_id" value="${library_id | h}"/>
+ <input type="hidden" name="folder_id" value="${folder_id | h}"/>
+ <input type="hidden" name="show_deleted" value="${show_deleted | h}"/>
%if replace_dataset not in [ None, 'None' ]:
- <input type="hidden" name="replace_id" value="${trans.security.encode_id( replace_dataset.id )}"/>
+ <input type="hidden" name="replace_id" value="${trans.security.encode_id( replace_dataset.id ) | h}"/><div class="form-row">
- You are currently selecting a new file to replace '<a href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=library_id, folder_id=folder_id, id=trans.security.encode_id( replace_dataset.library_dataset_dataset_association.id ) )}">${util.unicodify( replace_dataset.name )}</a>'.
+ You are currently selecting a new file to replace '<a href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=library_id, folder_id=folder_id, id=trans.security.encode_id( replace_dataset.library_dataset_dataset_association.id ) )}">${util.unicodify( replace_dataset.name ) | h}</a>'.
<div style="clear: both"></div></div>
%endif
@@ -120,7 +120,7 @@
<select name="file_type"><option value="auto" selected>Auto-detect</option>
%for file_format in file_formats:
- <option value="${file_format}">${file_format}</option>
+ <option value="${file_format | h}">${file_format | h}</option>
%endfor
</select></div>
@@ -176,23 +176,23 @@
%for entry in os.listdir( import_dir ):
## Do not include entries that are not directories
%if os.path.isdir( os.path.join( import_dir, entry ) ):
- <option>${entry}</option>
+ <option>${entry | h}</option>
%endif
%endfor
%else:
%if ( trans.user_is_admin() and cntrller == 'library_admin' ):
- <option>${import_dir}</option>
+ <option>${import_dir | h}</option>
%else:
- <option>${trans.user.email}</option>
+ <option>${trans.user.email | h}</option>
%endif
%endif
</select></div><div class="toolParamHelp" style="clear: both;">
%if contains_directories:
- Upload all files in a sub-directory of <strong>${import_dir}</strong> on the Galaxy server.
+ Upload all files in a sub-directory of <strong>${import_dir | h}</strong> on the Galaxy server.
%else:
- Upload all files in <strong>${import_dir}</strong> on the Galaxy server.
+ Upload all files in <strong>${import_dir | h}</strong> on the Galaxy server.
%endif
</div><div style="clear: both"></div>
@@ -282,9 +282,9 @@
%>
%for dbkey in dbkeys:
%if dbkey[1] == default_selected:
- <option value="${dbkey[1]}" selected>${dbkey[0]}</option>
+ <option value="${dbkey[1] | h}" selected>${dbkey[0] | h}</option>
%else:
- <option value="${dbkey[1]}">${dbkey[0]}</option>
+ <option value="${dbkey[1] | h}">${dbkey[0] | h}</option>
%endif
%endfor
</select>
@@ -295,7 +295,7 @@
<label>Message:</label><div class="form-row-input">
%if ldda_message:
- <textarea name="ldda_message" rows="3" cols="35">${ldda_message}</textarea>
+ <textarea name="ldda_message" rows="3" cols="35">${ldda_message | h}</textarea>
%else:
<textarea name="ldda_message" rows="3" cols="35"></textarea>
%endif
@@ -320,13 +320,13 @@
%if widgets:
%for i, field in enumerate( widgets ):
<div class="form-row">
- <label>${field[ 'label' ]}</label>
+ <label>${field[ 'label' ] | h}</label><div class="form-row-input">
${field[ 'widget' ].get_html()}
</div><div class="toolParamHelp" style="clear: both;">
%if field[ 'helptext' ]:
- ${field[ 'helptext' ]}<br/>
+ ${field[ 'helptext' ] | h}<br/>
%endif
*Inherited template field
</div>
@@ -342,14 +342,14 @@
</div>
%elif upload_option == 'import_from_history':
<div class="toolForm">
- <div class="toolFormTitle">Active datasets in your current history (${ util.unicodify( history.name )})</div>
+ <div class="toolFormTitle">Active datasets in your current history (${ util.unicodify( history.name ) | h})</div><div class="toolFormBody">
%if history and history.active_datasets:
<form name="add_history_datasets_to_library" action="${h.url_for( controller='library_common', action='add_history_datasets_to_library', cntrller=cntrller, library_id=library_id )}" enctype="multipart/form-data" method="post">
- <input type="hidden" name="folder_id" value="${folder_id}"/>
- <input type="hidden" name="show_deleted" value="${show_deleted}"/>
+ <input type="hidden" name="folder_id" value="${folder_id | h}"/>
+ <input type="hidden" name="show_deleted" value="${show_deleted | h}"/><input type="hidden" name="upload_option" value="import_from_history"/>
- <input type="hidden" name="ldda_message" value="${ldda_message}"/>
+ <input type="hidden" name="ldda_message" value="${ldda_message | h}"/><%
role_ids_selected = ''
if roles_select_list:
@@ -357,32 +357,32 @@
if selected:
role_ids_selected = ','.join( selected )
%>
- <input type="hidden" name="roles" value="${role_ids_selected}"/>
+ <input type="hidden" name="roles" value="${role_ids_selected | h}"/>
%if replace_dataset not in [ None, 'None' ]:
- <input type="hidden" name="replace_id" value="${trans.security.encode_id( replace_dataset.id )}"/>
+ <input type="hidden" name="replace_id" value="${trans.security.encode_id( replace_dataset.id ) | h}"/><div class="form-row">
- You are currently selecting a new file to replace '<a href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=library_id, folder_id=folder_id, id=trans.security.encode_id( replace_dataset.library_dataset_dataset_association.id ) )}">${ util.unicodify( replace_dataset.name )}</a>'.
+ You are currently selecting a new file to replace '<a href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=library_id, folder_id=folder_id, id=trans.security.encode_id( replace_dataset.library_dataset_dataset_association.id ) )}">${ util.unicodify( replace_dataset.name ) | h}</a>'.
<div style="clear: both"></div></div>
%endif
%for hda in history.visible_datasets:
<% encoded_id = trans.security.encode_id( hda.id ) %><div class="form-row">
- <input name="hda_ids" id="hist_${encoded_id}" value="${encoded_id}" type="checkbox"/>
- <label for="hist_${encoded_id}" style="display: inline;font-weight:normal;">${hda.hid}: ${ util.unicodify( hda.name )}</label>
+ <input name="hda_ids" id="hist_${encoded_id | h}" value="${encoded_id | h}" type="checkbox"/>
+ <label for="hist_${encoded_id | h}" style="display: inline;font-weight:normal;">${hda.hid | h}: ${ util.unicodify( hda.name ) | h}</label></div>
%endfor
%if widgets:
- <input type="hidden" name="template_id" value="${template_id}"/>
+ <input type="hidden" name="template_id" value="${template_id | h}"/>
%for i, field in enumerate( widgets ):
<div class="form-row">
- <label>${field[ 'label' ]}</label>
+ <label>${field[ 'label' ] | h}</label><div class="form-row-input">
${field[ 'widget' ].get_html()}
</div><div class="toolParamHelp" style="clear: both;">
%if field[ 'helptext' ]:
- ${field[ 'helptext' ]}<br/>
+ ${field[ 'helptext' ] | h}<br/>
%endif
*Inherited template field
</div>
diff -r ed67b05dc0402bf9ff9fbf35198ea2cac92e2c22 -r 0f0cca84c94ac88e6e9d347a57fd08b99d7c9176 templates/webapps/galaxy/library/common/import_datasets_to_histories.mako
--- a/templates/webapps/galaxy/library/common/import_datasets_to_histories.mako
+++ b/templates/webapps/galaxy/library/common/import_datasets_to_histories.mako
@@ -34,8 +34,8 @@
checked = " checked='checked'"
%><div class="form-row">
- <input type="checkbox" name="ldda_ids" id="dataset_${encoded_id}" value="${encoded_id}" ${checked}/>
- <label for="dataset_${encoded_id}" style="display: inline;font-weight:normal;">${util.unicodify( source_ldda.name )}</label>
+ <input type="checkbox" name="ldda_ids" id="dataset_${encoded_id | h}" value="${encoded_id | h}" ${checked}/>
+ <label for="dataset_${encoded_id | h}" style="display: inline;font-weight:normal;">${util.unicodify( source_ldda.name ) | h}</label></div>
%endfor
%else:
@@ -61,7 +61,7 @@
else:
current_history_text = ""
%>
- <option value="${encoded_id}"${selected_text}>${i + 1}: ${h.truncate( util.unicodify( target_history.name ), 30 )}${current_history_text}</option>
+ <option value="${encoded_id | h}"${selected_text}>${i + 1}: ${h.truncate( util.unicodify( target_history.name ), 30 ) | h}${current_history_text | h}</option>
%endfor
</select><br/><br/>
@@ -77,8 +77,8 @@
current_history_text = ""
%><div class="form-row">
- <input type="checkbox" name="target_history_ids" id="target_history_${encoded_id}" value="${encoded_id}"/>
- <label for="target_history_${encoded_id}" style="display: inline; font-weight:normal;">${i + 1}: ${util.unicodify( target_history.name )}${current_history_text}</label>
+ <input type="checkbox" name="target_history_ids" id="target_history_${encoded_id | h}" value="${encoded_id | h}"/>
+ <label for="target_history_${encoded_id | h}" style="display: inline; font-weight:normal;">${i + 1}: ${util.unicodify( target_history.name ) | h}${current_history_text | h}</label></div>
%endfor
</div>
diff -r ed67b05dc0402bf9ff9fbf35198ea2cac92e2c22 -r 0f0cca84c94ac88e6e9d347a57fd08b99d7c9176 templates/webapps/galaxy/library/common/ldda_edit_info.mako
--- a/templates/webapps/galaxy/library/common/ldda_edit_info.mako
+++ b/templates/webapps/galaxy/library/common/ldda_edit_info.mako
@@ -34,9 +34,9 @@
<select name="datatype">
%for ext in file_formats:
%if ldda.ext == ext:
- <option value="${ext}" selected="yes">${ext}</option>
+ <option value="${ext | h}" selected="yes">${ext | h}</option>
%else:
- <option value="${ext}">${ext}</option>
+ <option value="${ext | h}">${ext | h}</option>
%endif
%endfor
</select>
@@ -44,24 +44,24 @@
%if ( trans.user_is_admin() and cntrller=='library_admin' ) or trans.app.security_agent.can_modify_library_item( current_user_roles, ldda.library_dataset ):
<div class="toolForm">
- <div class="toolFormTitle">Edit attributes of ${util.unicodify( ldda.name )}</div>
+ <div class="toolFormTitle">Edit attributes of ${util.unicodify( ldda.name ) | h}</div><div class="toolFormBody"><form name="edit_attributes" action="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=library_id, folder_id=trans.security.encode_id( ldda.library_dataset.folder.id ), use_panels=use_panels, show_deleted=show_deleted, )}" method="post">
- <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id )}"/>
+ <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id ) | h}"/><div class="form-row"><label>Name:</label>
- <input type="text" name="name" value="${util.unicodify( ldda.name )}" size="40"/>
+ <input type="text" name="name" value="${util.unicodify( ldda.name ) | h}" size="40"/><div style="clear: both"></div></div><div class="form-row"><label>Info:</label>
- <input type="text" name="info" value="${util.unicodify( ldda.info )}" size="40"/>
+ <input type="text" name="info" value="${util.unicodify( ldda.info ) | h}" size="40"/><div style="clear: both"></div></div><div class="form-row"><label>Message:</label>
%if ldda.message:
- <textarea name="message" rows="3" cols="35">${ldda.message}</textarea>
+ <textarea name="message" rows="3" cols="35">${ldda.message | h}</textarea>
%else:
<textarea name="message" rows="3" cols="35"></textarea>
%endif
@@ -73,7 +73,7 @@
%for name, spec in ldda.metadata.spec.items():
%if spec.visible:
<div class="form-row">
- <label>${spec.desc}:</label>
+ <label>${spec.desc | h}:</label>
${ldda.metadata.get_html_by_name( name, trans=trans )}
<div style="clear: both"></div></div>
@@ -85,7 +85,7 @@
</form><form name="auto_detect" action="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=library_id, folder_id=trans.security.encode_id( ldda.library_dataset.folder.id ), use_panels=use_panels, show_deleted=show_deleted, )}" method="post"><div class="form-row">
- <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id )}"/>
+ <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id ) | h}"/><input type="submit" name="detect" value="Auto-detect"/><div class="toolParamHelp" style="clear: both;">
This will inspect the dataset and attempt to correct the above column values if they are not accurate.
@@ -101,7 +101,7 @@
%if ldda.datatype.allow_datatype_change:
<form name="change_datatype" action="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=library_id, folder_id=trans.security.encode_id( ldda.library_dataset.folder.id ), use_panels=use_panels, show_deleted=show_deleted, )}" method="post"><div class="form-row">
- <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id )}"/>
+ <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id ) | h}"/><label>New Type:</label>
${datatype( ldda, file_formats )}
<div class="toolParamHelp" style="clear: both;">
@@ -129,10 +129,10 @@
<div class="form-row"><label>Extended Metadata:</label></div>
- <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id )}"/>
+ <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id ) | h}"/><div class="form-row">
%if ldda.extended_metadata:
- <textarea name="extended_metadata" rows="15" cols="35">${util.pretty_print_json(ldda.extended_metadata.data)}</textarea>
+ <textarea name="extended_metadata" rows="15" cols="35">${util.pretty_print_json(ldda.extended_metadata.data) | h}</textarea>
%else:
<textarea name="extended_metadata" rows="15" cols="35"></textarea>
%endif
@@ -147,28 +147,28 @@
<p/>
%else:
<div class="toolForm">
- <div class="toolFormTitle">View information about ${util.unicodify( ldda.name )}</div>
+ <div class="toolFormTitle">View information about ${util.unicodify( ldda.name ) | h}</div><div class="toolFormBody"><div class="form-row"><label>Name:</label>
- ${util.unicodify( ldda.name )}
+ ${util.unicodify( ldda.name ) | h}
<div style="clear: both"></div></div><div class="form-row"><label>Info:</label>
- ${util.unicodify( ldda.info )}
+ ${util.unicodify( ldda.info ) | h}
<div style="clear: both"></div></div><div class="form-row"><label>Data Format:</label>
- ${ldda.ext}
+ ${ldda.ext | h}
<div style="clear: both"></div></div>
%for name, spec in ldda.metadata.spec.items():
%if spec.visible:
<div class="form-row">
- <label>${spec.desc}:</label>
- ${ldda.metadata.get( name )}
+ <label>${spec.desc | h}:</label>
+ ${ldda.metadata.get( name ) | h}
<div style="clear: both"></div></div>
%endif
This diff is so big that we needed to truncate the remainder.
https://bitbucket.org/galaxy/galaxy-central/commits/07404a82972d/
Changeset: 07404a82972d
Branch: stable
User: dannon
Date: 2014-12-08 20:22:53+00:00
Summary: Merged in dan/galaxy-central-prs/stable (pull request #597)
[STABLE] HTML escape user-settable values in Data Libraries. Update tests to reflect that e.g. quotes are now html escaped within pages. Eliminate the unnecessary use of Params() object for these controllers.
Affected #: 17 files
diff -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a -r 07404a82972d877b5529fffaeb3e7e05b69a02a3 lib/galaxy/webapps/galaxy/controllers/library.py
--- a/lib/galaxy/webapps/galaxy/controllers/library.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library.py
@@ -3,7 +3,7 @@
from galaxy import web
from galaxy.model.orm import and_, not_, or_
from galaxy.web.base.controller import BaseUIController
-from galaxy.web.framework.helpers import grids
+from galaxy.web.framework.helpers import escape, grids
from library_common import get_comptypes, lucene_search, whoosh_search
@@ -79,7 +79,6 @@
@web.expose
def list( self, trans, **kwd ):
- params = util.Params( kwd )
# define app configuration for generic mako template
app = {
'jscript' : "galaxy.library"
@@ -89,10 +88,9 @@
@web.expose
def index( self, trans, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- default_action = params.get( 'default_action', None )
+ message = escape( kwd.get( 'message', '' ) )
+ status = escape( kwd.get( 'status', 'done' ) )
+ default_action = kwd.get( 'default_action', None )
return trans.fill_template( "/library/index.mako",
default_action=default_action,
message=message,
diff -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a -r 07404a82972d877b5529fffaeb3e7e05b69a02a3 lib/galaxy/webapps/galaxy/controllers/library_admin.py
--- a/lib/galaxy/webapps/galaxy/controllers/library_admin.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library_admin.py
@@ -5,7 +5,7 @@
from galaxy import web
from galaxy.web.base.controller import BaseUIController
-from galaxy.web.framework.helpers import grids, time_ago
+from galaxy.web.framework.helpers import escape, grids, time_ago
from library_common import get_comptypes, lucene_search, whoosh_search
# from galaxy.model.orm import *
@@ -141,20 +141,19 @@
lddas=lddas,
show_deleted=show_deleted,
use_panels=use_panels,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
# Render the list view
return self.library_list_grid( trans, **kwd )
@web.expose
@web.require_admin
def create_library( self, trans, **kwd ):
- params = galaxy.util.Params( kwd )
- message = galaxy.util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- if params.get( 'create_library_button', False ):
- name = galaxy.util.restore_text( params.get( 'name', 'No name' ) )
- description = galaxy.util.restore_text( params.get( 'description', '' ) )
- synopsis = galaxy.util.restore_text( params.get( 'synopsis', '' ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ if kwd.get( 'create_library_button', False ):
+ name = kwd.get( 'name', 'No name' )
+ description = kwd.get( 'description', '' )
+ synopsis = kwd.get( 'synopsis', '' )
if synopsis in [ 'None', None ]:
synopsis = ''
library = trans.app.model.Library( name=name, description=description, synopsis=synopsis )
@@ -167,9 +166,9 @@
action='browse_library',
cntrller='library_admin',
id=trans.security.encode_id( library.id ),
- message=galaxy.util.sanitize_text( message ),
+ message=message,
status='done' ) )
- return trans.fill_template( '/admin/library/new_library.mako', message=message, status=status )
+ return trans.fill_template( '/admin/library/new_library.mako', message=escape( message ), status=escape( status ) )
@web.expose
@web.require_admin
def delete_library( self, trans, id, **kwd ):
@@ -196,8 +195,7 @@
# TODO: change this function to purge_library_item, behaving similar to delete_library_item
# assuming we want the ability to purge libraries.
# This function is currently only used by the functional tests.
- params = galaxy.util.Params( kwd )
- library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( params.id ) )
+ library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( kwd.get( 'id' ) ) )
def purge_folder( library_folder ):
for lf in library_folder.folders:
purge_folder( lf )
@@ -226,7 +224,7 @@
message = "Library '%s' has not been marked deleted, so it cannot be purged" % ( library.name )
return trans.response.send_redirect( web.url_for( controller='library_admin',
action='browse_libraries',
- message=galaxy.util.sanitize_text( message ),
+ message=message,
status='error' ) )
else:
purge_folder( library.root_folder )
@@ -236,5 +234,5 @@
message = "Library '%s' and all of its contents have been purged, datasets will be removed from disk via the cleanup_datasets script" % library.name
return trans.response.send_redirect( web.url_for( controller='library_admin',
action='browse_libraries',
- message=galaxy.util.sanitize_text( message ),
+ message=message,
status='done' ) )
diff -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a -r 07404a82972d877b5529fffaeb3e7e05b69a02a3 lib/galaxy/webapps/galaxy/controllers/library_common.py
--- a/lib/galaxy/webapps/galaxy/controllers/library_common.py
+++ b/lib/galaxy/webapps/galaxy/controllers/library_common.py
@@ -20,6 +20,7 @@
from galaxy.util.streamball import StreamBall
from galaxy.web.base.controller import BaseUIController, UsesFormDefinitionsMixin, UsesExtendedMetadataMixin, UsesLibraryMixinItems
from galaxy.web.form_builder import AddressField, CheckboxField, SelectField, build_select_field
+from galaxy.web.framework.helpers import escape
from galaxy.model.orm import and_, eagerload_all
# Whoosh is compatible with Python 2.5+ Try to import Whoosh and set flag to indicate whether tool search is enabled.
@@ -92,14 +93,13 @@
@web.expose
def browse_library( self, trans, cntrller='library', **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
# If use_panels is True, the library is being accessed via an external link
# which did not originate from within the Galaxy instance, and the library will
# be displayed correctly with the mast head.
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- library_id = params.get( 'id', None )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ library_id = kwd.get( 'id', None )
if not library_id:
# To handle bots
message = "You must specify a library id."
@@ -116,9 +116,9 @@
message = "Invalid library id ( %s ) specified." % str( library_id )
status = 'error'
else:
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- created_ldda_ids = params.get( 'created_ldda_ids', '' )
- hidden_folder_ids = util.listify( params.get( 'hidden_folder_ids', '' ) )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ created_ldda_ids = kwd.get( 'created_ldda_ids', '' )
+ hidden_folder_ids = util.listify( kwd.get( 'hidden_folder_ids', '' ) )
if created_ldda_ids and not message:
message = "%d datasets are uploading in the background to the library '%s' (each is selected). " % \
( len( created_ldda_ids.split( ',' ) ), library.name )
@@ -137,8 +137,8 @@
show_deleted=show_deleted,
comptypes=comptypes,
current_user_roles=current_user_roles,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
else:
return trans.fill_template( 'library/common/browse_library.mako',
cntrller=cntrller,
@@ -149,44 +149,43 @@
show_deleted=show_deleted,
comptypes=comptypes,
current_user_roles=current_user_roles,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
except Exception, e:
message = 'Error attempting to display contents of library (%s): %s.' % ( str( library.name ), str( e ) )
status = 'error'
- default_action = params.get( 'default_action', None )
+ default_action = kwd.get( 'default_action', None )
return trans.response.send_redirect( web.url_for( use_panels=use_panels,
controller=cntrller,
action='browse_libraries',
default_action=default_action,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
@web.expose
def library_info( self, trans, cntrller, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
- library_id = params.get( 'id', None )
+ library_id = kwd.get( 'id', None )
try:
library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) )
except:
library = None
self._check_access( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'library_info_button', False ):
+ if kwd.get( 'library_info_button', False ):
self._check_modify( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted )
old_name = library.name
- new_name = util.restore_text( params.get( 'name', 'No name' ) )
+ new_name = kwd.get( 'name', 'No name' )
if not new_name:
message = 'Enter a valid name'
status='error'
else:
- new_description = util.restore_text( params.get( 'description', '' ) )
- new_synopsis = util.restore_text( params.get( 'synopsis', '' ) )
+ new_description = kwd.get( 'description', '' )
+ new_synopsis = kwd.get( 'synopsis', '' )
if new_synopsis in [ None, 'None' ]:
new_synopsis = ''
library.name = new_name
@@ -204,7 +203,7 @@
use_panels=use_panels,
id=trans.security.encode_id( library.id ),
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
# See if we have any associated templates
info_association, inherited = library.get_info_association()
@@ -220,29 +219,28 @@
show_deleted=show_deleted,
info_association=info_association,
inherited=inherited,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def library_permissions( self, trans, cntrller, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
- library_id = params.get( 'id', None )
+ library_id = kwd.get( 'id', None )
try:
library = trans.sa_session.query( trans.app.model.Library ).get( trans.security.decode_id( library_id ) )
except:
library = None
self._check_access( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted )
self._check_manage( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'update_roles_button', False ):
+ if kwd.get( 'update_roles_button', False ):
# The user clicked the Save button on the 'Associate With Roles' form
permissions = {}
for k, v in trans.app.model.Library.permitted_actions.items():
- in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( params.get( k + '_in', [] ) ) ]
+ in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( x ) for x in util.listify( kwd.get( k + '_in', [] ) ) ]
permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
trans.app.security_agent.set_all_library_permissions( trans, library, permissions )
trans.sa_session.refresh( library )
@@ -255,7 +253,7 @@
use_panels=use_panels,
id=trans.security.encode_id( library.id ),
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
roles = trans.app.security_agent.get_legitimate_roles( trans, library, cntrller )
all_roles = trans.app.security_agent.get_all_roles( trans, cntrller )
@@ -267,15 +265,14 @@
roles=roles,
all_roles=all_roles,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def create_folder( self, trans, cntrller, parent_id, library_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller in ( 'library_admin', 'api' )
current_user_roles = trans.get_current_user_roles()
try:
@@ -288,9 +285,9 @@
parent_library = parent_folder.parent_library
self._check_access( trans, cntrller, is_admin, parent_folder, current_user_roles, use_panels, library_id, show_deleted )
self._check_add( trans, cntrller, is_admin, parent_folder, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'new_folder_button', False ) or cntrller == 'api':
- new_folder = trans.app.model.LibraryFolder( name=util.restore_text( params.name ),
- description=util.restore_text( params.description ) )
+ if kwd.get( 'new_folder_button', False ) or cntrller == 'api':
+ new_folder = trans.app.model.LibraryFolder( name=kwd.get( 'name', '' ),
+ description=kwd.get( 'description', '' ) )
# We are associating the last used genome build with folders, so we will always
# initialize a new folder with the first dbkey in genome builds list which is currently
# ? unspecified (?)
@@ -322,7 +319,7 @@
show_deleted=show_deleted,
info_association=info_association,
inherited=inherited,
- message=message,
+ message=escape( message ),
status='done' )
# If not inheritable info_association, redirect to the library.
message = "The new folder named '%s' has been added to the data library." % new_folder.name
@@ -334,7 +331,7 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
# We do not render any template widgets on creation pages since saving the info_association
# cannot occur before the associated item is saved.
@@ -344,15 +341,14 @@
library_id=library_id,
folder=parent_folder,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def folder_info( self, trans, cntrller, id, library_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
try:
@@ -360,11 +356,11 @@
except:
folder = None
self._check_access( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'rename_folder_button', False ):
+ if kwd.get( 'rename_folder_button', False ):
self._check_modify( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted )
old_name = folder.name
- new_name = util.restore_text( params.name )
- new_description = util.restore_text( params.description )
+ new_name = kwd.get( 'name', '' )
+ new_description = kwd.get( 'description', '' )
if not new_name:
message = 'Enter a valid name'
status='error'
@@ -381,7 +377,7 @@
id=id,
library_id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
# See if we have any associated templates
widgets = []
@@ -401,15 +397,14 @@
show_deleted=show_deleted,
info_association=info_association,
inherited=inherited,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def folder_permissions( self, trans, cntrller, id, library_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
try:
@@ -418,14 +413,14 @@
folder = None
self._check_access( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted )
self._check_manage( trans, cntrller, is_admin, folder, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'update_roles_button', False ):
+ if kwd.get( 'update_roles_button', False ):
# The user clicked the Save button on the 'Associate With Roles' form
permissions = {}
for k, v in trans.app.model.Library.permitted_actions.items():
if k != 'LIBRARY_ACCESS':
# LIBRARY_ACCESS is a special permission set only at the library level
# and it is not inherited.
- in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( int( x ) ) for x in util.listify( params.get( k + '_in', [] ) ) ]
+ in_roles = [ trans.sa_session.query( trans.app.model.Role ).get( int( x ) ) for x in util.listify( kwd.get( k + '_in', [] ) ) ]
permissions[ trans.app.security_agent.get_action( v.action ) ] = in_roles
trans.app.security_agent.set_all_library_permissions( trans, folder, permissions )
trans.sa_session.refresh( folder )
@@ -437,7 +432,7 @@
id=trans.security.encode_id( folder.id ),
library_id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
# If the library is public all roles are legitimate, but if the library
# is restricted, only those roles associated with the LIBRARY_ACCESS
@@ -451,15 +446,14 @@
current_user_roles=current_user_roles,
roles=roles,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def ldda_edit_info( self, trans, cntrller, library_id, folder_id, id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
try:
@@ -468,7 +462,7 @@
ldda = None
self._check_access( trans, cntrller, is_admin, ldda, current_user_roles, use_panels, library_id, show_deleted )
self._check_modify( trans, cntrller, is_admin, ldda, current_user_roles, use_panels, library_id, show_deleted )
- dbkey = params.get( 'dbkey', '?' )
+ dbkey = kwd.get( 'dbkey', '?' )
if isinstance( dbkey, list ):
dbkey = dbkey[0]
file_formats = [ dtype_name for dtype_name, dtype_value in trans.app.datatypes_registry.datatypes_by_extension.iteritems() if dtype_value.allow_datatype_change ]
@@ -492,26 +486,26 @@
info_association, inherited = ldda.get_info_association()
if info_association and ( not( inherited ) or info_association.inheritable ):
widgets = ldda.get_template_widgets( trans )
- if params.get( 'change', False ):
+ if kwd.get( 'change', False ):
# The user clicked the Save button on the 'Change data type' form
if __ok_to_edit_metadata( ldda.id ):
- if ldda.datatype.allow_datatype_change and trans.app.datatypes_registry.get_datatype_by_extension( params.datatype ).allow_datatype_change:
- trans.app.datatypes_registry.change_datatype( ldda, params.datatype )
+ if ldda.datatype.allow_datatype_change and trans.app.datatypes_registry.get_datatype_by_extension( kwd.get( 'datatype' ) ).allow_datatype_change:
+ trans.app.datatypes_registry.change_datatype( ldda, kwd.get( 'datatype' ) )
trans.sa_session.flush()
message = "Data type changed for library dataset '%s'." % ldda.name
status = 'done'
else:
- message = "You are unable to change datatypes in this manner. Changing %s to %s is not allowed." % ( ldda.extension, params.datatype )
+ message = "You are unable to change datatypes in this manner. Changing %s to %s is not allowed." % ( ldda.extension, kwd.get( 'datatype' ) )
status = 'error'
else:
message = "This dataset is currently being used as input or output. You cannot change datatype until the jobs have completed or you have canceled them."
status = "error"
- elif params.get( 'save', False ):
+ elif kwd.get( 'save', False ):
# The user clicked the Save button on the 'Edit Attributes' form
old_name = ldda.name
- new_name = util.restore_text( params.get( 'name', '' ) )
- new_info = util.restore_text( params.get( 'info', '' ) )
- new_message = util.restore_text( params.get( 'message', '' ) )
+ new_name = kwd.get( 'name', '' )
+ new_info = kwd.get( 'info', '' )
+ new_message = kwd.get( 'message', '' )
if not new_name:
message = 'Enter a valid name'
status = 'error'
@@ -524,12 +518,12 @@
for name, spec in ldda.datatype.metadata_spec.items():
if spec.get("readonly"):
continue
- optional = params.get( "is_" + name, None )
+ optional = kwd.get( "is_" + name, None )
if optional and optional == 'true':
# optional element... == 'true' actually means it is NOT checked (and therefore ommitted)
setattr( ldda.metadata, name, None )
else:
- setattr( ldda.metadata, name, spec.unwrap( params.get ( name, None ) ) )
+ setattr( ldda.metadata, name, spec.unwrap( kwd.get( name, None ) ) )
ldda.metadata.dbkey = dbkey
ldda.datatype.after_setting_metadata( ldda )
message = "Attributes updated for library dataset '%s'." % ldda.name
@@ -538,7 +532,7 @@
message = "Attributes updated, but metadata could not be changed because this dataset is currently being used as input or output. You must cancel or wait for these jobs to complete before changing metadata."
status = 'warning'
trans.sa_session.flush()
- elif params.get( 'detect', False ):
+ elif kwd.get( 'detect', False ):
# The user clicked the Auto-detect button on the 'Edit Attributes' form
if __ok_to_edit_metadata( ldda.id ):
for name, spec in ldda.datatype.metadata_spec.items():
@@ -553,8 +547,8 @@
message = "This dataset is currently being used as input or output. You cannot change metadata until the jobs have completed or you have canceled them."
status = 'error'
trans.sa_session.flush()
- elif params.get( 'change_extended_metadata', False):
- em_string = util.restore_text( params.get("extended_metadata", "") )
+ elif kwd.get( 'change_extended_metadata', False):
+ em_string = kwd.get("extended_metadata", "" )
if len(em_string):
payload = None
try:
@@ -604,16 +598,15 @@
show_deleted=show_deleted,
info_association=info_association,
inherited=inherited,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def ldda_info( self, trans, cntrller, library_id, folder_id, id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- show_associated_hdas_and_lddas = util.string_as_bool( params.get( 'show_associated_hdas_and_lddas', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ show_associated_hdas_and_lddas = util.string_as_bool( kwd.get( 'show_associated_hdas_and_lddas', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
ldda = trans.sa_session.query( trans.app.model.LibraryDatasetDatasetAssociation ).get( trans.security.decode_id( id ) )
@@ -653,15 +646,14 @@
current_user_roles=current_user_roles,
info_association=info_association,
inherited=inherited,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def ldda_permissions( self, trans, cntrller, library_id, folder_id, id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
ids = util.listify( id )
lddas = []
libraries = []
@@ -685,7 +677,7 @@
id=library_id,
cntrller=cntrller,
use_panels=use_panels,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
# If access to the dataset is restricted, then use the roles associated with the DATASET_ACCESS permission to
# determine the legitimate roles. If the dataset is public, see if access to the library is restricted. If
@@ -698,7 +690,7 @@
roles = trans.app.security_agent.get_legitimate_roles( trans, library, cntrller )
else:
roles = trans.app.security_agent.get_legitimate_roles( trans, ldda.dataset, cntrller )
- if params.get( 'update_roles_button', False ):
+ if kwd.get( 'update_roles_button', False ):
# Dataset permissions
access_action = trans.app.security_agent.get_action( trans.app.security_agent.permitted_actions.DATASET_ACCESS.action )
manage_permissions_action = trans.app.security_agent.get_action( trans.app.security_agent.permitted_actions.DATASET_MANAGE_PERMISSIONS.action )
@@ -755,8 +747,8 @@
library_id=library_id,
roles=roles,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
if len( ids ) > 1:
# Ensure that the permissions across all library items are identical, otherwise we can't update them together.
check_list = []
@@ -781,7 +773,7 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
# Display permission form, permissions will be updated for all lddas simultaneously.
return trans.fill_template( "/library/common/ldda_permissions.mako",
@@ -791,31 +783,30 @@
library_id=library_id,
roles=roles,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def upload_library_dataset( self, trans, cntrller, library_id, folder_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- ldda_message = util.restore_text( params.get( 'ldda_message', '' ) )
- deleted = util.string_as_bool( params.get( 'deleted', False ) )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- replace_id = params.get( 'replace_id', None )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ ldda_message = kwd.get( 'ldda_message', '' )
+ deleted = util.string_as_bool( kwd.get( 'deleted', False ) )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ replace_id = kwd.get( 'replace_id', None )
replace_dataset = None
- upload_option = params.get( 'upload_option', 'upload_file' )
- if params.get( 'files_0|space_to_tab', False ):
- space_to_tab = params.get( 'files_0|space_to_tab', '' )
+ upload_option = kwd.get( 'upload_option', 'upload_file' )
+ if kwd.get( 'files_0|space_to_tab', False ):
+ space_to_tab = kwd.get( 'files_0|space_to_tab', '' )
else:
- space_to_tab = params.get( 'space_to_tab', '' )
- link_data_only = params.get( 'link_data_only', 'copy_files' )
- dbkey = params.get( 'dbkey', '?' )
+ space_to_tab = kwd.get( 'space_to_tab', '' )
+ link_data_only = kwd.get( 'link_data_only', 'copy_files' )
+ dbkey = kwd.get( 'dbkey', '?' )
if isinstance( dbkey, list ):
last_used_build = dbkey[0]
else:
last_used_build = dbkey
- roles = params.get( 'roles', '' )
+ roles = kwd.get( 'roles', '' )
is_admin = trans.user_is_admin() and cntrller in ( 'library_admin', 'api' )
current_user_roles = trans.get_current_user_roles()
widgets = []
@@ -844,7 +835,7 @@
library = folder.parent_library
if folder and last_used_build in [ 'None', None, '?' ]:
last_used_build = folder.genome_build
- if params.get( 'runtool_btn', False ) or params.get( 'ajax_upload', False ) or cntrller == 'api':
+ if kwd.get( 'runtool_btn', False ) or kwd.get( 'ajax_upload', False ) or cntrller == 'api':
error = False
if upload_option == 'upload_paths' and not trans.app.config.allow_library_path_paste:
error = True
@@ -869,7 +860,7 @@
replace_id=replace_id,
upload_option=upload_option,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
else:
# See if we have any inherited templates.
@@ -883,7 +874,7 @@
for index, widget_dict in enumerate( widgets ):
widget = widget_dict[ 'widget' ]
if isinstance( widget, AddressField ):
- value = util.restore_text( params.get( widget.name, '' ) )
+ value = kwd.get( widget.name, '' )
if value == 'new':
if self.field_param_values_ok( widget.name, 'AddressField', **kwd ):
# Save the new address
@@ -966,7 +957,7 @@
default_action=default_action,
created_ldda_ids=created_ldda_ids,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
else:
created_ldda_ids = ''
@@ -981,7 +972,7 @@
id=library_id,
created_ldda_ids=created_ldda_ids,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
# Note: if the upload form was submitted due to refresh_on_change for a form field, we cannot re-populate
# the field for the selected file ( files_0|file_data ) if the user selected one. This is because the value
@@ -1040,8 +1031,8 @@
link_data_only=link_data_only,
show_deleted=show_deleted,
ldda_message=ldda_message,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
def upload_dataset( self, trans, cntrller, library_id, folder_id, replace_dataset=None, **kwd ):
# Set up the traditional tool state/params
tool_id = 'upload1'
@@ -1054,16 +1045,15 @@
if input.type == "upload_dataset":
dataset_upload_inputs.append( input )
# Library-specific params
- params = util.Params( kwd ) # is this filetoolparam safe?
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- server_dir = util.restore_text( params.get( 'server_dir', '' ) )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ server_dir = kwd.get( 'server_dir', '' )
if replace_dataset not in [ None, 'None' ]:
replace_id = trans.security.encode_id( replace_dataset.id )
else:
replace_id = None
- upload_option = params.get( 'upload_option', 'upload_file' )
+ upload_option = kwd.get( 'upload_option', 'upload_file' )
response_code = 200
if upload_option == 'upload_directory':
if server_dir in [ None, 'None', '' ]:
@@ -1092,7 +1082,7 @@
try:
# FIXME: instead of passing params here ( which have been processed by util.Params(), the original kwd
# should be passed so that complex objects that may have been included in the initial request remain.
- library_bunch = upload_common.handle_library_params( trans, params, folder_id, replace_dataset )
+ library_bunch = upload_common.handle_library_params( trans, kwd, folder_id, replace_dataset )
except:
response_code = 500
message = "Unable to parse upload parameters, please report this error."
@@ -1103,9 +1093,9 @@
tool_params = upload_common.persist_uploads( tool_params )
uploaded_datasets = upload_common.get_uploaded_datasets( trans, cntrller, tool_params, precreated_datasets, dataset_upload_inputs, library_bunch=library_bunch )
elif upload_option == 'upload_directory':
- uploaded_datasets, response_code, message = self.get_server_dir_uploaded_datasets( trans, cntrller, params, full_dir, import_dir_desc, library_bunch, response_code, message )
+ uploaded_datasets, response_code, message = self.get_server_dir_uploaded_datasets( trans, cntrller, kwd, full_dir, import_dir_desc, library_bunch, response_code, message )
elif upload_option == 'upload_paths':
- uploaded_datasets, response_code, message = self.get_path_paste_uploaded_datasets( trans, cntrller, params, library_bunch, response_code, message )
+ uploaded_datasets, response_code, message = self.get_path_paste_uploaded_datasets( trans, cntrller, kwd, library_bunch, response_code, message )
upload_common.cleanup_unused_precreated_datasets( precreated_datasets )
if upload_option == 'upload_file' and not uploaded_datasets:
response_code = 400
@@ -1121,7 +1111,7 @@
replace_id=replace_id,
upload_option=upload_option,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
json_file_path = upload_common.create_paramfile( trans, uploaded_datasets )
data_list = [ ud.data for ud in uploaded_datasets ]
@@ -1135,7 +1125,7 @@
def make_library_uploaded_dataset( self, trans, cntrller, params, name, path, type, library_bunch, in_folder=None ):
link_data_only = params.get( 'link_data_only', 'copy_files' )
uuid_str = params.get( 'uuid', None )
- file_type = params.file_type
+ file_type = params.get( 'file_type' )
library_bunch.replace_dataset = None # not valid for these types of upload
uploaded_dataset = util.bunch.Bunch()
new_name = name
@@ -1151,8 +1141,8 @@
uploaded_dataset.type = type
uploaded_dataset.ext = None
uploaded_dataset.file_type = file_type
- uploaded_dataset.dbkey = params.dbkey
- uploaded_dataset.space_to_tab = params.space_to_tab
+ uploaded_dataset.dbkey = params.get( 'dbkey' )
+ uploaded_dataset.space_to_tab = params.get( 'space_to_tab' )
if in_folder:
uploaded_dataset.in_folder = in_folder
uploaded_dataset.data = upload_common.new_upload( trans, cntrller, uploaded_dataset, library_bunch )
@@ -1247,7 +1237,7 @@
files_and_folders.append((file_path, file, in_folder))
return files_and_folders
def _paths_list(self, params):
- return [ (l.strip(), os.path.abspath(l.strip())) for l in params.filesystem_paths.splitlines() if l.strip() ]
+ return [ (l.strip(), os.path.abspath(l.strip())) for l in params.get( 'filesystem_paths', '' ).splitlines() if l.strip() ]
def _check_path_paste_params(self, params):
if params.get( 'filesystem_paths', '' ) == '':
@@ -1259,33 +1249,32 @@
if not os.path.exists( path ):
bad_paths.append( path )
if bad_paths:
- message = "Invalid paths:<br><ul><li>%s</li></ul>" % "</li><li>".join( bad_paths )
+ message = 'Invalid paths: "%s".' % '", "'.join( bad_paths )
response_code = 400
return None, response_code, message
return None
@web.expose
def add_history_datasets_to_library( self, trans, cntrller, library_id, folder_id, hda_ids='', **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- ldda_message = util.restore_text( params.get( 'ldda_message', '' ) )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- replace_id = params.get( 'replace_id', None )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ ldda_message = kwd.get( 'ldda_message', '' )
+ show_deleted = kwd.get( 'show_deleted', False )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ replace_id = kwd.get( 'replace_id', None )
replace_dataset = None
- upload_option = params.get( 'upload_option', 'import_from_history' )
- if params.get( 'files_0|space_to_tab', False ):
- space_to_tab = params.get( 'files_0|space_to_tab', '' )
+ upload_option = kwd.get( 'upload_option', 'import_from_history' )
+ if kwd.get( 'files_0|space_to_tab', False ):
+ space_to_tab = kwd.get( 'files_0|space_to_tab', '' )
else:
- space_to_tab = params.get( 'space_to_tab', '' )
- link_data_only = params.get( 'link_data_only', 'copy_files' )
- dbkey = params.get( 'dbkey', '?' )
+ space_to_tab = kwd.get( 'space_to_tab', '' )
+ link_data_only = kwd.get( 'link_data_only', 'copy_files' )
+ dbkey = kwd.get( 'dbkey', '?' )
if isinstance( dbkey, list ):
last_used_build = dbkey[0]
else:
last_used_build = dbkey
- roles = params.get( 'roles', '' )
+ roles = kwd.get( 'roles', '' )
is_admin = trans.user_is_admin() and cntrller in ( 'library_admin', 'api' )
current_user_roles = trans.get_current_user_roles()
widgets = []
@@ -1323,9 +1312,9 @@
cntrller=cntrller,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
- if params.get( 'add_history_datasets_to_library_button', False ):
+ if kwd.get( 'add_history_datasets_to_library_button', False ):
hda_ids = util.listify( hda_ids )
if hda_ids:
dataset_names = []
@@ -1354,7 +1343,7 @@
trans.app.security_agent.copy_library_permissions( trans, folder, ldda )
trans.app.security_agent.copy_library_permissions( trans, folder, ldda.library_dataset )
else:
- library_bunch = upload_common.handle_library_params( trans, params, folder_id, replace_dataset )
+ library_bunch = upload_common.handle_library_params( trans, kwd, folder_id, replace_dataset )
if library_bunch.template and library_bunch.template_field_contents:
# Since information templates are inherited, the template fields can be displayed on the upload form.
# If the user has added field contents, we'll need to create a new form_values and info_association
@@ -1422,12 +1411,12 @@
id=library_id,
created_ldda_ids=created_ldda_ids,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
else:
message = 'Select at least one dataset from the list of active datasets in your current history'
status = 'error'
- upload_option = params.get( 'upload_option', 'import_from_history' )
+ upload_option = kwd.get( 'upload_option', 'import_from_history' )
widgets = self._get_populated_widgets( folder )
# Send list of data formats to the upload form so the "extension" select list can be populated dynamically
file_formats = trans.app.datatypes_registry.upload_file_formats
@@ -1461,8 +1450,8 @@
link_data_only=link_data_only,
show_deleted=show_deleted,
ldda_message=ldda_message,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
def _build_roles_select_list( self, trans, cntrller, library, selected_role_ids=[] ):
# Get the list of legitimate roles to display on the upload form. If the library is public,
# all active roles are legitimate. If the library is restricted by the LIBRARY_ACCESS permission, only
@@ -1521,8 +1510,7 @@
def download_dataset_from_folder( self, trans, cntrller, id, library_id=None, **kwd ):
"""Catches the dataset id and displays file contents as directed"""
show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
- params = util.Params( kwd )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
try:
@@ -1555,15 +1543,14 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
@web.expose
def library_dataset_info( self, trans, cntrller, id, library_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
try:
@@ -1571,11 +1558,11 @@
except:
library_dataset = None
self._check_access( trans, cntrller, is_admin, library_dataset, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'edit_attributes_button', False ):
+ if kwd.get( 'edit_attributes_button', False ):
self._check_modify( trans, cntrller, is_admin, library_dataset, current_user_roles, use_panels, library_id, show_deleted )
old_name = library_dataset.name
- new_name = util.restore_text( params.get( 'name', '' ) )
- new_info = util.restore_text( params.get( 'info', '' ) )
+ new_name = kwd.get( 'name', '' )
+ new_info = kwd.get( 'info', '' )
if not new_name:
message = 'Enter a valid name'
status = 'error'
@@ -1604,15 +1591,14 @@
widgets=widgets,
widget_fields_have_contents=widget_fields_have_contents,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def library_dataset_permissions( self, trans, cntrller, id, library_id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
current_user_roles = trans.get_current_user_roles()
try:
@@ -1621,7 +1607,7 @@
library_dataset = None
self._check_access( trans, cntrller, is_admin, library_dataset, current_user_roles, use_panels, library_id, show_deleted )
self._check_manage( trans, cntrller, is_admin, library_dataset, current_user_roles, use_panels, library_id, show_deleted )
- if params.get( 'update_roles_button', False ):
+ if kwd.get( 'update_roles_button', False ):
# The user clicked the Save button on the 'Associate With Roles' form
permissions = {}
for k, v in trans.app.model.Library.permitted_actions.items():
@@ -1652,22 +1638,21 @@
roles=roles,
current_user_roles=current_user_roles,
show_deleted=show_deleted,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def make_library_item_public( self, trans, cntrller, library_id, item_type, id, **kwd ):
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
current_user_roles = trans.get_current_user_roles()
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
if item_type == 'library':
library = trans.sa_session.query( trans.model.Library ).get( trans.security.decode_id( id ) )
self._check_access( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted )
self._check_manage( trans, cntrller, is_admin, library, current_user_roles, use_panels, library_id, show_deleted )
- contents = util.string_as_bool( params.get( 'contents', 'False' ) )
+ contents = util.string_as_bool( kwd.get( 'contents', 'False' ) )
trans.app.security_agent.make_library_public( library, contents=contents )
if contents:
message = "The data library (%s) and all its contents have been made publicly accessible." % library.name
@@ -1694,7 +1679,7 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
@web.expose
def act_on_multiple_datasets( self, trans, cntrller, library_id=None, ldda_ids='', **kwd ):
@@ -1718,12 +1703,11 @@
rval += '%s %i %s%s %s\r\n' % ( crc, size, self.url_base, quoted_fname, relpath )
return rval
# Perform an action on a list of library datasets.
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- action = params.get( 'do_action', None )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ action = kwd.get( 'do_action', None )
lddas = []
error = False
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
@@ -1738,7 +1722,7 @@
else:
if action in [ 'import_to_current_history', 'import_to_histories' ]:
new_kwd = {}
- if action == 'import_to_current_history':
+ if current_history is not None and action == 'import_to_current_history':
encoded_current_history_id = trans.security.encode_id( current_history.id )
selected_history_id = encoded_current_history_id
new_kwd[ 'do_action' ] = action
@@ -1809,7 +1793,7 @@
folder_id=folder_id,
id=",".join( encoded_ldda_ids ),
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
else:
message = "You are not authorized to manage permissions on any of the selected datasets."
@@ -1970,11 +1954,11 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
else:
# We arrived here from the library_dataset_search_results page, so redirect there.
- search_term = params.get( 'search_term', '' )
+ search_term = kwd.get( 'search_term', '' )
comptypes = get_comptypes( trans )
return trans.fill_template( '/library/common/library_dataset_search_results.mako',
cntrller=cntrller,
@@ -1984,8 +1968,8 @@
lddas=lddas,
show_deleted=show_deleted,
use_panels=use_panels,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def import_datasets_to_histories( self, trans, cntrller, library_id='', folder_id='', ldda_ids='', target_history_id='', target_history_ids='', new_history_name='', **kwd ):
@@ -1995,12 +1979,11 @@
# - a select list option for acting on multiple selected datasets within a library
# ( ldda_ids is a comma separated string of ldda ids )
# - a menu option for a library dataset search result set ( ldda_ids is a comma separated string of ldda ids )
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- action = params.get( 'do_action', None )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ action = kwd.get( 'do_action', None )
user = trans.get_user()
current_history = trans.get_history()
if library_id:
@@ -2019,7 +2002,7 @@
target_history_ids = set( [ trans.security.decode_id( target_history_id ) for target_history_id in target_history_ids if target_history_id ] )
elif target_history_id:
target_history_ids = [ trans.security.decode_id( target_history_id ) ]
- if params.get( 'import_datasets_to_histories_button', False ):
+ if kwd.get( 'import_datasets_to_histories_button', False ):
invalid_datasets = 0
if not ldda_ids or not ( target_history_ids or new_history_name ):
message = "You must provide one or more source library datasets and one or more target histories."
@@ -2083,11 +2066,13 @@
# to the lddas in order for the menu optin to be available.
ldda = trans.sa_session.query( trans.model.LibraryDatasetDatasetAssociation ).get( ldda_id )
source_lddas.append( ldda )
+ if current_history is None:
+ current_history = trans.get_history( create=True )
if current_history is not None:
target_histories = [ current_history ]
else:
target_histories = []
- message = 'You must have a history before you can import datasets. You can do this by <a href="%s" target="_top">loading the analysis interface</a>.' % url_for(controller='root')
+ message = 'You must have a history before you can import datasets. You can do this by loading the analysis interface.'
status = 'error'
if user:
target_histories = user.active_histories
@@ -2097,7 +2082,7 @@
action='browse_library',
cntrller=cntrller,
id=library_id,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
return trans.fill_template( "/library/common/import_datasets_to_histories.mako",
cntrller=cntrller,
@@ -2111,15 +2096,14 @@
new_history_name=new_history_name,
show_deleted=show_deleted,
use_panels=use_panels,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def manage_template_inheritance( self, trans, cntrller, item_type, library_id, folder_id=None, ldda_id=None, **kwd ):
- params = util.Params( kwd )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
is_admin = ( trans.user_is_admin() and cntrller == 'library_admin' )
current_user_roles = trans.get_current_user_roles()
try:
@@ -2138,7 +2122,7 @@
cntrller=cntrller,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
info_association, inherited = item.get_info_association( restrict=True )
if info_association:
@@ -2157,7 +2141,7 @@
folder_id=folder_id,
id=id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
@web.expose
def move_library_item( self, trans, cntrller, item_type, item_id, source_library_id='', make_target_current=True, **kwd ):
@@ -2168,11 +2152,10 @@
# 'ldda' and item_id is a comma separated string of ldda ids )
# - a menu option for a library dataset search result set ( item_type is 'ldda' and item_id is a
# comma separated string of ldda ids )
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
- show_deleted = util.string_as_bool( params.get( 'show_deleted', False ) )
- use_panels = util.string_as_bool( params.get( 'use_panels', False ) )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
+ show_deleted = util.string_as_bool( kwd.get( 'show_deleted', False ) )
+ use_panels = util.string_as_bool( kwd.get( 'use_panels', False ) )
make_target_current = util.string_as_bool( make_target_current )
is_admin = trans.user_is_admin() and cntrller == 'library_admin'
user = trans.get_user()
@@ -2186,14 +2169,14 @@
else:
# Request sent from the library_dataset_search_results page.
source_library = None
- target_library_id = params.get( 'target_library_id', '' )
+ target_library_id = kwd.get( 'target_library_id', '' )
if target_library_id not in [ '', 'none', None ]:
target_library = trans.sa_session.query( trans.model.Library ).get( trans.security.decode_id( target_library_id ) )
elif make_target_current:
target_library = source_library
else:
target_library = None
- target_folder_id = params.get( 'target_folder_id', '' )
+ target_folder_id = kwd.get( 'target_folder_id', '' )
if target_folder_id not in [ '', 'none', None ]:
target_folder = trans.sa_session.query( trans.model.LibraryFolder ).get( trans.security.decode_id( target_folder_id ) )
if target_library is None:
@@ -2208,7 +2191,7 @@
elif item_type == 'folder':
move_folder_id = item_id
move_folder = trans.sa_session.query( trans.model.LibraryFolder ).get( trans.security.decode_id( move_folder_id ) )
- if params.get( 'move_library_item_button', False ):
+ if kwd.get( 'move_library_item_button', False ):
if not ( move_ldda_ids or move_folder_id ) or target_folder_id in [ '', 'none', None ]:
message = "You must select a source folder or one or more source datasets, and a target folder."
status = 'error'
@@ -2372,8 +2355,8 @@
target_folder_id_select_field=target_folder_id_select_field,
show_deleted=show_deleted,
use_panels=use_panels,
- message=message,
- status=status )
+ message=escape( message ),
+ status=escape( status ) )
@web.expose
def delete_library_item( self, trans, cntrller, library_id, item_id, item_type, **kwd ):
# This action will handle deleting all types of library items. State is saved for libraries and
@@ -2541,7 +2524,7 @@
action='browse_libraries',
cntrller=cntrller,
use_panels=use_panels,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
return trans.response.send_redirect( web.url_for( controller='library_common',
action='browse_library',
@@ -2549,7 +2532,7 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
def _check_add( self, trans, cntrller, is_admin, item, current_user_roles, use_panels, library_id, show_deleted ):
# Deny access if the user is not an admin and does not have the LIBRARY_ADD permission.
@@ -2564,7 +2547,7 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
def _check_manage( self, trans, cntrller, is_admin, item, current_user_roles, use_panels, library_id, show_deleted ):
if isinstance( item, trans.model.LibraryDataset ):
@@ -2580,7 +2563,7 @@
id=library_id,
cntrller=cntrller,
use_panels=use_panels,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
# Deny access if the user is not an admin and does not have the LIBRARY_MANAGE permission.
if not ( is_admin or trans.app.security_agent.can_manage_library_item( current_user_roles, item ) ):
@@ -2592,7 +2575,7 @@
id=library_id,
cntrller=cntrller,
use_panels=use_panels,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
def _check_modify( self, trans, cntrller, is_admin, item, current_user_roles, use_panels, library_id, show_deleted ):
# Deny modification if the user is not an admin and does not have the LIBRARY_MODIFY permission.
@@ -2606,7 +2589,7 @@
id=library_id,
use_panels=use_panels,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
# ---- Utility methods -------------------------------------------------------
@@ -2740,9 +2723,8 @@
return map( operator.getitem, intermed, ( -1, ) * len( intermed ) )
def lucene_search( trans, cntrller, search_term, search_url, **kwd ):
"""Return display of results from a full-text lucene search of data libraries."""
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
full_url = "%s/find?%s" % ( search_url, urllib.urlencode( { "kwd" : search_term } ) )
response = urllib2.urlopen( full_url )
ldda_ids = util.json.loads( response.read() )[ "ids" ]
@@ -2751,9 +2733,8 @@
return status, message, get_sorted_accessible_library_items( trans, cntrller, lddas, 'name' )
def whoosh_search( trans, cntrller, search_term, **kwd ):
"""Return display of results from a full-text whoosh search of data libraries."""
- params = util.Params( kwd )
- message = util.restore_text( params.get( 'message', '' ) )
- status = params.get( 'status', 'done' )
+ message = kwd.get( 'message', '' )
+ status = kwd.get( 'status', 'done' )
ok = True
if whoosh_search_enabled:
whoosh_index_dir = trans.app.config.whoosh_index_dir
diff -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a -r 07404a82972d877b5529fffaeb3e7e05b69a02a3 templates/webapps/galaxy/library/common/browse_library.mako
--- a/templates/webapps/galaxy/library/common/browse_library.mako
+++ b/templates/webapps/galaxy/library/common/browse_library.mako
@@ -236,29 +236,29 @@
%if current_version and ( not ldda.library_dataset.deleted or show_deleted ):
<tr class="datasetRow"
%if parent is not None:
- parent="${parent}"
+ parent="${parent | h}"
%endif
- id="libraryItem-${ldda.id}">
+ id="libraryItem-${ldda.id | h}"><td style="padding-left: ${pad+20}px;">
- <input style="float: left;" type="checkbox" name="ldda_ids" id="${trans.security.encode_id( ldda.id )}" value="${trans.security.encode_id( ldda.id )}"
+ <input style="float: left;" type="checkbox" name="ldda_ids" id="${trans.security.encode_id( ldda.id ) | h}" value="${trans.security.encode_id( ldda.id ) | h}"
%if selected:
checked="checked"
%endif
/>
%if simple:
- <label for="${trans.security.encode_id( ldda.id )}">${ util.unicodify( ldda.name )}</label>
+ <label for="${trans.security.encode_id( ldda.id ) | h}">${ util.unicodify( ldda.name ) | h}</label>
%else:
- <div style="float: left; margin-left: 1px;" class="menubutton split popup" id="dataset-${ldda.id}-popup">
+ <div style="float: left; margin-left: 1px;" class="menubutton split popup" id="dataset-${ldda.id | h}-popup"><a class="view-info" href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">
%if ldda.library_dataset.deleted:
- <div class="libraryItem-error">${util.unicodify( ldda.name )}</div>
+ <div class="libraryItem-error">${util.unicodify( ldda.name ) | h}</div>
%else:
- ${util.unicodify( ldda.name )}
+ ${util.unicodify( ldda.name ) | h}
%endif
</a></div>
%if not library.deleted:
- <div popupmenu="dataset-${ldda.id}-popup">
+ <div popupmenu="dataset-${ldda.id | h}-popup">
%if not branch_deleted( folder ) and not ldda.library_dataset.deleted and can_modify:
<a class="action-button" href="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit information</a><a class="action-button" href="${h.url_for( controller='library_common', action='move_library_item', cntrller=cntrller, item_type='ldda', item_id=trans.security.encode_id( ldda.id ), source_library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Move this dataset</a>
@@ -287,7 +287,7 @@
%endif
%if can_modify:
%if not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.deleted:
- <a class="action-button" confirm="Click OK to delete dataset '${util.unicodify( ldda.name )}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Delete this dataset</a>
+ <a class="action-button" confirm="Click OK to delete dataset '${util.unicodify( ldda.name ) | h}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Delete this dataset</a>
%elif not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.purged and ldda.library_dataset.deleted:
<a class="action-button" href="${h.url_for( controller='library_common', action='undelete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Undelete this dataset</a>
%endif
@@ -298,10 +298,10 @@
</td>
% if not simple:
<td id="libraryItemInfo">${render_library_item_info( ldda )}</td>
- <td>${ldda.extension}</td>
+ <td>${ldda.extension | h}</td>
% endif
- <td>${ldda.create_time.strftime( trans.app.config.pretty_datetime_format )}</td>
- <td>${ldda.get_size( nice_size=True )}</td>
+ <td>${ldda.create_time.strftime( trans.app.config.pretty_datetime_format ) | h}</td>
+ <td>${ldda.get_size( nice_size=True ) | h}</td></tr><%
my_row = row_counter.count
@@ -355,28 +355,28 @@
%>
%if not root_folder and ( not folder.deleted or show_deleted ):
<% encoded_id = trans.security.encode_id( folder.id ) %>
- <tr id="folder-${encoded_id}" class="folderRow libraryOrFolderRow"
+ <tr id="folder-${encoded_id | h}" class="folderRow libraryOrFolderRow"
%if parent is not None:
- parent="${parent}"
+ parent="${parent | h}"
style="display: none;"
%endif
>
- <td style="padding-left: ${folder_pad}px;">
+ <td style="padding-left: ${folder_pad | h}px;"><input type="checkbox" class="folderCheckbox"/>
- <span class="expandLink folder-${encoded_id}-click">
- <div style="float: left; margin-left: 2px;" class="menubutton split popup" id="folder_img-${folder.id}-popup">
- <a class="folder-${encoded_id}-click" href="javascript:void(0);">
+ <span class="expandLink folder-${encoded_id | h}-click">
+ <div style="float: left; margin-left: 2px;" class="menubutton split popup" id="folder_img-${folder.id | h}-popup">
+ <a class="folder-${encoded_id | h}-click" href="javascript:void(0);"><span class="rowIcon"></span>
%if folder.deleted:
- <div class="libraryItem-error">${folder.name}</div>
+ <div class="libraryItem-error">${folder.name | h}</div>
%else:
- ${folder.name}
+ ${folder.name | h}
%endif
</a></div></span>
%if not library.deleted:
- <div popupmenu="folder_img-${folder.id}-popup">
+ <div popupmenu="folder_img-${folder.id | h}-popup">
%if not branch_deleted( folder ) and can_add:
<a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add datasets</a><a class="action-button" href="${h.url_for( controller='library_common', action='create_folder', cntrller=cntrller, parent_id=trans.security.encode_id( folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add sub-folder</a>
@@ -407,7 +407,7 @@
%endif
%if can_modify:
%if not library.deleted and not folder.deleted:
- <a class="action-button" confirm="Click OK to delete the folder '${folder.name}.'" href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Delete this folder</a>
+ <a class="action-button" confirm="Click OK to delete the folder '${folder.name | h}.'" href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Delete this folder</a>
%elif not library.deleted and folder.deleted and not folder.purged:
<a class="action-button" href="${h.url_for( controller='library_common', action='undelete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Undelete this folder</a>
%endif
@@ -416,7 +416,7 @@
%endif
<td>
%if folder.description:
- ${folder.description}
+ ${folder.description | h}
%endif
<td colspan="3"></td></tr>
@@ -504,7 +504,7 @@
return str( self.count )
%>
- <h2>Data Library “${library.name}”</h2>
+ <h2>Data Library “${library.name | h}”</h2><ul class="manage-table-actions">
%if not library.deleted and ( is_admin or can_add ):
@@ -517,7 +517,7 @@
%if not library.deleted:
%if can_modify:
<a class="action-button" href="${h.url_for( controller='library_common', action='library_info', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit information</a>
- <a class="action-button" confirm="Click OK to delete the library named '${library.name}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library' )}">Delete this data library</a>
+ <a class="action-button" confirm="Click OK to delete the library named '${library.name | h}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library' )}">Delete this data library</a>
%if show_deleted:
<a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=False )}">Hide deleted items</a>
%else:
@@ -555,7 +555,7 @@
%if library.synopsis not in [ '', 'None', None ]:
<div class="libraryItemBody">
- ${library.synopsis}
+ ${library.synopsis | h}
</div>
%endif
@@ -610,6 +610,6 @@
${render_compression_types_help( comptypes )}
%endif
%if not has_accessible_folders:
- The data library '${library.name}' does not contain any datasets that you can access.
+ The data library '${library.name | h}' does not contain any datasets that you can access.
%endif
</%def>
diff -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a -r 07404a82972d877b5529fffaeb3e7e05b69a02a3 templates/webapps/galaxy/library/common/browse_library_opt.mako
--- a/templates/webapps/galaxy/library/common/browse_library_opt.mako
+++ b/templates/webapps/galaxy/library/common/browse_library_opt.mako
@@ -228,29 +228,29 @@
%if current_version and ( not ldda.library_dataset.deleted or show_deleted ):
<tr class="datasetRow"
%if parent is not None:
- parent="${parent}"
+ parent="${parent | h}"
%endif
- id="libraryItem-${ldda.id}">
+ id="libraryItem-${ldda.id | h}"><td style="padding-left: ${pad+20}px;">
- <input style="float: left;" type="checkbox" name="ldda_ids" id="${trans.security.encode_id( ldda.id )}" value="${trans.security.encode_id( ldda.id )}"
+ <input style="float: left;" type="checkbox" name="ldda_ids" id="${trans.security.encode_id( ldda.id ) | h}" value="${trans.security.encode_id( ldda.id ) | h}"
%if selected:
checked="checked"
%endif
/>
%if simple:
- <label for="${trans.security.encode_id( ldda.id )}">${ util.unicodify( ldda.name )}</label>
+ <label for="${trans.security.encode_id( ldda.id ) | h}">${ util.unicodify( ldda.name ) | h}</label>
%else:
- <div style="float: left; margin-left: 1px;" class="menubutton split popup" id="dataset-${ldda.id}-popup">
+ <div style="float: left; margin-left: 1px;" class="menubutton split popup" id="dataset-${ldda.id | h}-popup"><a class="view-info" href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">
%if ldda.library_dataset.deleted:
- <div class="libraryItem-error">${util.unicodify( ldda.name )}</div>
+ <div class="libraryItem-error">${util.unicodify( ldda.name ) | h}</div>
%else:
- ${util.unicodify( ldda.name )}
+ ${util.unicodify( ldda.name ) | h}
%endif
</a></div>
%if not library.deleted:
- <div popupmenu="dataset-${ldda.id}-popup">
+ <div popupmenu="dataset-${ldda.id | h}-popup">
%if not branch_deleted( folder ) and not ldda.library_dataset.deleted and can_modify:
<a class="action-button" href="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), id=trans.security.encode_id( ldda.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit information</a><a class="action-button" href="${h.url_for( controller='library_common', action='move_library_item', cntrller=cntrller, item_type='ldda', item_id=trans.security.encode_id( ldda.id ), source_library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Move this dataset</a>
@@ -279,7 +279,7 @@
%endif
%if can_modify:
%if not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.deleted:
- <a class="action-button" confirm="Click OK to delete dataset '${util.unicodify( ldda.name )}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Delete this dataset</a>
+ <a class="action-button" confirm="Click OK to delete dataset '${util.unicodify( ldda.name ) | h}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Delete this dataset</a>
%elif not library.deleted and not branch_deleted( folder ) and not ldda.library_dataset.purged and ldda.library_dataset.deleted:
<a class="action-button" href="${h.url_for( controller='library_common', action='undelete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library_dataset.id ), item_type='library_dataset', show_deleted=show_deleted )}">Undelete this dataset</a>
%endif
@@ -290,10 +290,10 @@
</td>
% if not simple:
<td id="libraryItemInfo">${render_library_item_info( ldda )}</td>
- <td>${ldda.extension}</td>
+ <td>${ldda.extension | h}</td>
% endif
- <td>${ldda.create_time.strftime( "%Y-%m-%d" )}</td>
- <td>${ldda.get_size( nice_size=True )}</td>
+ <td>${ldda.create_time.strftime( "%Y-%m-%d" ) | h}</td>
+ <td>${ldda.get_size( nice_size=True ) | h}</td></tr><%
my_row = row_counter.count
@@ -362,28 +362,28 @@
%>
%if not root_folder and ( not folder.deleted or show_deleted ):
<% encoded_id = trans.security.encode_id( folder.id ) %>
- <tr id="folder-${encoded_id}" class="folderRow libraryOrFolderRow"
+ <tr id="folder-${encoded_id | h}" class="folderRow libraryOrFolderRow"
%if parent is not None:
- parent="${parent}"
+ parent="${parent | h}"
style="display: none;"
%endif
>
- <td style="padding-left: ${folder_pad}px;">
+ <td style="padding-left: ${folder_pad | h}px;"><input type="checkbox" class="folderCheckbox"/>
- <span class="expandLink folder-${encoded_id}-click">
- <div style="float: left; margin-left: 2px;" class="menubutton split popup" id="folder_img-${folder.id}-popup">
- <a class="folder-${encoded_id}-click" href="javascript:void(0);">
+ <span class="expandLink folder-${encoded_id | h}-click">
+ <div style="float: left; margin-left: 2px;" class="menubutton split popup" id="folder_img-${folder.id | h}-popup">
+ <a class="folder-${encoded_id | h}-click" href="javascript:void(0);"><span class="rowIcon"></span>
%if folder.deleted:
- <div class="libraryItem-error">${folder.name}</div>
+ <div class="libraryItem-error">${folder.name | h}</div>
%else:
- ${folder.name}
+ ${folder.name | h}
%endif
</a></div></span>
%if not library.deleted:
- <div popupmenu="folder_img-${folder.id}-popup">
+ <div popupmenu="folder_img-${folder.id | h}-popup">
%if not branch_deleted( folder ) and can_add:
<a class="action-button" href="${h.url_for( controller='library_common', action='upload_library_dataset', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), folder_id=trans.security.encode_id( folder.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add datasets</a><a class="action-button" href="${h.url_for( controller='library_common', action='create_folder', cntrller=cntrller, parent_id=trans.security.encode_id( folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add sub-folder</a>
@@ -414,7 +414,7 @@
%endif
%if can_modify:
%if not library.deleted and not folder.deleted:
- <a class="action-button" confirm="Click OK to delete the folder '${folder.name}.'" href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Delete this folder</a>
+ <a class="action-button" confirm="Click OK to delete the folder '${folder.name | h}.'" href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Delete this folder</a>
%elif not library.deleted and folder.deleted and not folder.purged:
<a class="action-button" href="${h.url_for( controller='library_common', action='undelete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( folder.id ), item_type='folder', show_deleted=show_deleted )}">Undelete this folder</a>
%endif
@@ -423,7 +423,7 @@
%endif
<td>
%if folder.description:
- ${folder.description}
+ ${folder.description | h}
%endif
<td colspan="3"></td></tr>
@@ -515,12 +515,12 @@
<li><a class="action-button" href="${h.url_for( controller='library_common', action='create_folder', cntrller=cntrller, parent_id=trans.security.encode_id( library.root_folder.id ), library_id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Add folder</a></li>
%endif
%if ( ( not library.deleted ) and ( can_modify or can_manage ) ) or ( can_modify and not library.purged ) or ( library.purged ):
- <li><a class="action-button" id="library-${library.id}-popup" class="menubutton">Library Actions</a></li>
- <div popupmenu="library-${library.id}-popup">
+ <li><a class="action-button" id="library-${library.id | h}-popup" class="menubutton">Library Actions</a></li>
+ <div popupmenu="library-${library.id | h}-popup">
%if not library.deleted:
%if can_modify:
<a class="action-button" href="${h.url_for( controller='library_common', action='library_info', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=show_deleted )}">Edit information</a>
- <a class="action-button" confirm="Click OK to delete the library named '${library.name}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library' )}">Delete this data library</a>
+ <a class="action-button" confirm="Click OK to delete the library named '${library.name | h}'." href="${h.url_for( controller='library_common', action='delete_library_item', cntrller=cntrller, library_id=trans.security.encode_id( library.id ), item_id=trans.security.encode_id( library.id ), item_type='library' )}">Delete this data library</a>
%if show_deleted:
<a class="action-button" href="${h.url_for( controller='library_common', action='browse_library', cntrller=cntrller, id=trans.security.encode_id( library.id ), use_panels=use_panels, show_deleted=False )}">Hide deleted items</a>
%else:
@@ -558,7 +558,7 @@
%if library.synopsis not in [ '', 'None', None ]:
<div class="libraryItemBody">
- ${library.synopsis}
+ ${library.synopsis | h}
</div>
%endif
@@ -616,6 +616,6 @@
${render_compression_types_help( comptypes )}
%endif
%if not has_accessible_folders:
- The data library '${library.name}' does not contain any datasets that you can access.
+ The data library '${library.name | h}' does not contain any datasets that you can access.
%endif
</%def>
diff -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a -r 07404a82972d877b5529fffaeb3e7e05b69a02a3 templates/webapps/galaxy/library/common/common.mako
--- a/templates/webapps/galaxy/library/common/common.mako
+++ b/templates/webapps/galaxy/library/common/common.mako
@@ -88,19 +88,19 @@
else:
tool_form_title = 'Upload files'
%>
- <div class="toolFormTitle">${tool_form_title}</div>
+ <div class="toolFormTitle">${tool_form_title | h}</div><div class="toolFormBody"><form name="upload_library_dataset" id="upload_library_dataset" action="${action}" enctype="multipart/form-data" method="post"><input type="hidden" name="tool_id" value="upload1"/><input type="hidden" name="tool_state" value="None"/>
- <input type="hidden" name="cntrller" value="${cntrller}"/>
- <input type="hidden" name="library_id" value="${library_id}"/>
- <input type="hidden" name="folder_id" value="${folder_id}"/>
- <input type="hidden" name="show_deleted" value="${show_deleted}"/>
+ <input type="hidden" name="cntrller" value="${cntrller | h}"/>
+ <input type="hidden" name="library_id" value="${library_id | h}"/>
+ <input type="hidden" name="folder_id" value="${folder_id | h}"/>
+ <input type="hidden" name="show_deleted" value="${show_deleted | h}"/>
%if replace_dataset not in [ None, 'None' ]:
- <input type="hidden" name="replace_id" value="${trans.security.encode_id( replace_dataset.id )}"/>
+ <input type="hidden" name="replace_id" value="${trans.security.encode_id( replace_dataset.id ) | h}"/><div class="form-row">
- You are currently selecting a new file to replace '<a href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=library_id, folder_id=folder_id, id=trans.security.encode_id( replace_dataset.library_dataset_dataset_association.id ) )}">${util.unicodify( replace_dataset.name )}</a>'.
+ You are currently selecting a new file to replace '<a href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=library_id, folder_id=folder_id, id=trans.security.encode_id( replace_dataset.library_dataset_dataset_association.id ) )}">${util.unicodify( replace_dataset.name ) | h}</a>'.
<div style="clear: both"></div></div>
%endif
@@ -120,7 +120,7 @@
<select name="file_type"><option value="auto" selected>Auto-detect</option>
%for file_format in file_formats:
- <option value="${file_format}">${file_format}</option>
+ <option value="${file_format | h}">${file_format | h}</option>
%endfor
</select></div>
@@ -176,23 +176,23 @@
%for entry in os.listdir( import_dir ):
## Do not include entries that are not directories
%if os.path.isdir( os.path.join( import_dir, entry ) ):
- <option>${entry}</option>
+ <option>${entry | h}</option>
%endif
%endfor
%else:
%if ( trans.user_is_admin() and cntrller == 'library_admin' ):
- <option>${import_dir}</option>
+ <option>${import_dir | h}</option>
%else:
- <option>${trans.user.email}</option>
+ <option>${trans.user.email | h}</option>
%endif
%endif
</select></div><div class="toolParamHelp" style="clear: both;">
%if contains_directories:
- Upload all files in a sub-directory of <strong>${import_dir}</strong> on the Galaxy server.
+ Upload all files in a sub-directory of <strong>${import_dir | h}</strong> on the Galaxy server.
%else:
- Upload all files in <strong>${import_dir}</strong> on the Galaxy server.
+ Upload all files in <strong>${import_dir | h}</strong> on the Galaxy server.
%endif
</div><div style="clear: both"></div>
@@ -282,9 +282,9 @@
%>
%for dbkey in dbkeys:
%if dbkey[1] == default_selected:
- <option value="${dbkey[1]}" selected>${dbkey[0]}</option>
+ <option value="${dbkey[1] | h}" selected>${dbkey[0] | h}</option>
%else:
- <option value="${dbkey[1]}">${dbkey[0]}</option>
+ <option value="${dbkey[1] | h}">${dbkey[0] | h}</option>
%endif
%endfor
</select>
@@ -295,7 +295,7 @@
<label>Message:</label><div class="form-row-input">
%if ldda_message:
- <textarea name="ldda_message" rows="3" cols="35">${ldda_message}</textarea>
+ <textarea name="ldda_message" rows="3" cols="35">${ldda_message | h}</textarea>
%else:
<textarea name="ldda_message" rows="3" cols="35"></textarea>
%endif
@@ -320,13 +320,13 @@
%if widgets:
%for i, field in enumerate( widgets ):
<div class="form-row">
- <label>${field[ 'label' ]}</label>
+ <label>${field[ 'label' ] | h}</label><div class="form-row-input">
${field[ 'widget' ].get_html()}
</div><div class="toolParamHelp" style="clear: both;">
%if field[ 'helptext' ]:
- ${field[ 'helptext' ]}<br/>
+ ${field[ 'helptext' ] | h}<br/>
%endif
*Inherited template field
</div>
@@ -342,14 +342,14 @@
</div>
%elif upload_option == 'import_from_history':
<div class="toolForm">
- <div class="toolFormTitle">Active datasets in your current history (${ util.unicodify( history.name )})</div>
+ <div class="toolFormTitle">Active datasets in your current history (${ util.unicodify( history.name ) | h})</div><div class="toolFormBody">
%if history and history.active_datasets:
<form name="add_history_datasets_to_library" action="${h.url_for( controller='library_common', action='add_history_datasets_to_library', cntrller=cntrller, library_id=library_id )}" enctype="multipart/form-data" method="post">
- <input type="hidden" name="folder_id" value="${folder_id}"/>
- <input type="hidden" name="show_deleted" value="${show_deleted}"/>
+ <input type="hidden" name="folder_id" value="${folder_id | h}"/>
+ <input type="hidden" name="show_deleted" value="${show_deleted | h}"/><input type="hidden" name="upload_option" value="import_from_history"/>
- <input type="hidden" name="ldda_message" value="${ldda_message}"/>
+ <input type="hidden" name="ldda_message" value="${ldda_message | h}"/><%
role_ids_selected = ''
if roles_select_list:
@@ -357,32 +357,32 @@
if selected:
role_ids_selected = ','.join( selected )
%>
- <input type="hidden" name="roles" value="${role_ids_selected}"/>
+ <input type="hidden" name="roles" value="${role_ids_selected | h}"/>
%if replace_dataset not in [ None, 'None' ]:
- <input type="hidden" name="replace_id" value="${trans.security.encode_id( replace_dataset.id )}"/>
+ <input type="hidden" name="replace_id" value="${trans.security.encode_id( replace_dataset.id ) | h}"/><div class="form-row">
- You are currently selecting a new file to replace '<a href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=library_id, folder_id=folder_id, id=trans.security.encode_id( replace_dataset.library_dataset_dataset_association.id ) )}">${ util.unicodify( replace_dataset.name )}</a>'.
+ You are currently selecting a new file to replace '<a href="${h.url_for( controller='library_common', action='ldda_info', cntrller=cntrller, library_id=library_id, folder_id=folder_id, id=trans.security.encode_id( replace_dataset.library_dataset_dataset_association.id ) )}">${ util.unicodify( replace_dataset.name ) | h}</a>'.
<div style="clear: both"></div></div>
%endif
%for hda in history.visible_datasets:
<% encoded_id = trans.security.encode_id( hda.id ) %><div class="form-row">
- <input name="hda_ids" id="hist_${encoded_id}" value="${encoded_id}" type="checkbox"/>
- <label for="hist_${encoded_id}" style="display: inline;font-weight:normal;">${hda.hid}: ${ util.unicodify( hda.name )}</label>
+ <input name="hda_ids" id="hist_${encoded_id | h}" value="${encoded_id | h}" type="checkbox"/>
+ <label for="hist_${encoded_id | h}" style="display: inline;font-weight:normal;">${hda.hid | h}: ${ util.unicodify( hda.name ) | h}</label></div>
%endfor
%if widgets:
- <input type="hidden" name="template_id" value="${template_id}"/>
+ <input type="hidden" name="template_id" value="${template_id | h}"/>
%for i, field in enumerate( widgets ):
<div class="form-row">
- <label>${field[ 'label' ]}</label>
+ <label>${field[ 'label' ] | h}</label><div class="form-row-input">
${field[ 'widget' ].get_html()}
</div><div class="toolParamHelp" style="clear: both;">
%if field[ 'helptext' ]:
- ${field[ 'helptext' ]}<br/>
+ ${field[ 'helptext' ] | h}<br/>
%endif
*Inherited template field
</div>
diff -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a -r 07404a82972d877b5529fffaeb3e7e05b69a02a3 templates/webapps/galaxy/library/common/import_datasets_to_histories.mako
--- a/templates/webapps/galaxy/library/common/import_datasets_to_histories.mako
+++ b/templates/webapps/galaxy/library/common/import_datasets_to_histories.mako
@@ -34,8 +34,8 @@
checked = " checked='checked'"
%><div class="form-row">
- <input type="checkbox" name="ldda_ids" id="dataset_${encoded_id}" value="${encoded_id}" ${checked}/>
- <label for="dataset_${encoded_id}" style="display: inline;font-weight:normal;">${util.unicodify( source_ldda.name )}</label>
+ <input type="checkbox" name="ldda_ids" id="dataset_${encoded_id | h}" value="${encoded_id | h}" ${checked}/>
+ <label for="dataset_${encoded_id | h}" style="display: inline;font-weight:normal;">${util.unicodify( source_ldda.name ) | h}</label></div>
%endfor
%else:
@@ -61,7 +61,7 @@
else:
current_history_text = ""
%>
- <option value="${encoded_id}"${selected_text}>${i + 1}: ${h.truncate( util.unicodify( target_history.name ), 30 )}${current_history_text}</option>
+ <option value="${encoded_id | h}"${selected_text}>${i + 1}: ${h.truncate( util.unicodify( target_history.name ), 30 ) | h}${current_history_text | h}</option>
%endfor
</select><br/><br/>
@@ -77,8 +77,8 @@
current_history_text = ""
%><div class="form-row">
- <input type="checkbox" name="target_history_ids" id="target_history_${encoded_id}" value="${encoded_id}"/>
- <label for="target_history_${encoded_id}" style="display: inline; font-weight:normal;">${i + 1}: ${util.unicodify( target_history.name )}${current_history_text}</label>
+ <input type="checkbox" name="target_history_ids" id="target_history_${encoded_id | h}" value="${encoded_id | h}"/>
+ <label for="target_history_${encoded_id | h}" style="display: inline; font-weight:normal;">${i + 1}: ${util.unicodify( target_history.name ) | h}${current_history_text | h}</label></div>
%endfor
</div>
diff -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a -r 07404a82972d877b5529fffaeb3e7e05b69a02a3 templates/webapps/galaxy/library/common/ldda_edit_info.mako
--- a/templates/webapps/galaxy/library/common/ldda_edit_info.mako
+++ b/templates/webapps/galaxy/library/common/ldda_edit_info.mako
@@ -34,9 +34,9 @@
<select name="datatype">
%for ext in file_formats:
%if ldda.ext == ext:
- <option value="${ext}" selected="yes">${ext}</option>
+ <option value="${ext | h}" selected="yes">${ext | h}</option>
%else:
- <option value="${ext}">${ext}</option>
+ <option value="${ext | h}">${ext | h}</option>
%endif
%endfor
</select>
@@ -44,24 +44,24 @@
%if ( trans.user_is_admin() and cntrller=='library_admin' ) or trans.app.security_agent.can_modify_library_item( current_user_roles, ldda.library_dataset ):
<div class="toolForm">
- <div class="toolFormTitle">Edit attributes of ${util.unicodify( ldda.name )}</div>
+ <div class="toolFormTitle">Edit attributes of ${util.unicodify( ldda.name ) | h}</div><div class="toolFormBody"><form name="edit_attributes" action="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=library_id, folder_id=trans.security.encode_id( ldda.library_dataset.folder.id ), use_panels=use_panels, show_deleted=show_deleted, )}" method="post">
- <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id )}"/>
+ <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id ) | h}"/><div class="form-row"><label>Name:</label>
- <input type="text" name="name" value="${util.unicodify( ldda.name )}" size="40"/>
+ <input type="text" name="name" value="${util.unicodify( ldda.name ) | h}" size="40"/><div style="clear: both"></div></div><div class="form-row"><label>Info:</label>
- <input type="text" name="info" value="${util.unicodify( ldda.info )}" size="40"/>
+ <input type="text" name="info" value="${util.unicodify( ldda.info ) | h}" size="40"/><div style="clear: both"></div></div><div class="form-row"><label>Message:</label>
%if ldda.message:
- <textarea name="message" rows="3" cols="35">${ldda.message}</textarea>
+ <textarea name="message" rows="3" cols="35">${ldda.message | h}</textarea>
%else:
<textarea name="message" rows="3" cols="35"></textarea>
%endif
@@ -73,7 +73,7 @@
%for name, spec in ldda.metadata.spec.items():
%if spec.visible:
<div class="form-row">
- <label>${spec.desc}:</label>
+ <label>${spec.desc | h}:</label>
${ldda.metadata.get_html_by_name( name, trans=trans )}
<div style="clear: both"></div></div>
@@ -85,7 +85,7 @@
</form><form name="auto_detect" action="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=library_id, folder_id=trans.security.encode_id( ldda.library_dataset.folder.id ), use_panels=use_panels, show_deleted=show_deleted, )}" method="post"><div class="form-row">
- <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id )}"/>
+ <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id ) | h}"/><input type="submit" name="detect" value="Auto-detect"/><div class="toolParamHelp" style="clear: both;">
This will inspect the dataset and attempt to correct the above column values if they are not accurate.
@@ -101,7 +101,7 @@
%if ldda.datatype.allow_datatype_change:
<form name="change_datatype" action="${h.url_for( controller='library_common', action='ldda_edit_info', cntrller=cntrller, library_id=library_id, folder_id=trans.security.encode_id( ldda.library_dataset.folder.id ), use_panels=use_panels, show_deleted=show_deleted, )}" method="post"><div class="form-row">
- <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id )}"/>
+ <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id ) | h}"/><label>New Type:</label>
${datatype( ldda, file_formats )}
<div class="toolParamHelp" style="clear: both;">
@@ -129,10 +129,10 @@
<div class="form-row"><label>Extended Metadata:</label></div>
- <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id )}"/>
+ <input type="hidden" name="id" value="${trans.security.encode_id( ldda.id ) | h}"/><div class="form-row">
%if ldda.extended_metadata:
- <textarea name="extended_metadata" rows="15" cols="35">${util.pretty_print_json(ldda.extended_metadata.data)}</textarea>
+ <textarea name="extended_metadata" rows="15" cols="35">${util.pretty_print_json(ldda.extended_metadata.data) | h}</textarea>
%else:
<textarea name="extended_metadata" rows="15" cols="35"></textarea>
%endif
@@ -147,28 +147,28 @@
<p/>
%else:
<div class="toolForm">
- <div class="toolFormTitle">View information about ${util.unicodify( ldda.name )}</div>
+ <div class="toolFormTitle">View information about ${util.unicodify( ldda.name ) | h}</div><div class="toolFormBody"><div class="form-row"><label>Name:</label>
- ${util.unicodify( ldda.name )}
+ ${util.unicodify( ldda.name ) | h}
<div style="clear: both"></div></div><div class="form-row"><label>Info:</label>
- ${util.unicodify( ldda.info )}
+ ${util.unicodify( ldda.info ) | h}
<div style="clear: both"></div></div><div class="form-row"><label>Data Format:</label>
- ${ldda.ext}
+ ${ldda.ext | h}
<div style="clear: both"></div></div>
%for name, spec in ldda.metadata.spec.items():
%if spec.visible:
<div class="form-row">
- <label>${spec.desc}:</label>
- ${ldda.metadata.get( name )}
+ <label>${spec.desc | h}:</label>
+ ${ldda.metadata.get( name ) | h}
<div style="clear: both"></div></div>
%endif
This diff is so big that we needed to truncate the remainder.
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: Client build: factor dropDownSelect into filter control
by commits-noreply@bitbucket.org 08 Dec '14
by commits-noreply@bitbucket.org 08 Dec '14
08 Dec '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/864fdca5b06e/
Changeset: 864fdca5b06e
User: carlfeberhard
Date: 2014-12-08 20:18:12+00:00
Summary: Client build: factor dropDownSelect into filter control
Affected #: 6 files
diff -r a667776c4a1bc13cf1645c9ddd5af8892cc32b28 -r 864fdca5b06e6f15040298a316746448d09cdb40 client/galaxy/scripts/jq-plugins/ui/filter-control.js
--- a/client/galaxy/scripts/jq-plugins/ui/filter-control.js
+++ b/client/galaxy/scripts/jq-plugins/ui/filter-control.js
@@ -10,6 +10,61 @@
}
}(function ($) {
+ //==============================================================================
+ /**
+ * Template function that produces a bootstrap dropdown to replace the
+ * vanilla HTML select input. Pass in an array of options and an initial selection:
+ * $( '.my-div' ).append( dropDownSelect( [ 'option1', 'option2' ], 'option2' );
+ *
+ * When the user changes the selected option a 'change.dropdown-select' event will
+ * fire with both the jq event and the new selection text as arguments.
+ *
+ * Get the currently selected choice using:
+ * var userChoice = $( '.my-div .dropdown-select .dropdown-select-selected' ).text();
+ *
+ */
+ function dropDownSelect( options, selected ){
+ // replacement for vanilla select element using bootstrap dropdowns instead
+ selected = selected || (( !_.isEmpty( options ) )?( options[0] ):( '' ));
+ var $select = $([
+ '<div class="dropdown-select btn-group">',
+ '<button type="button" class="btn btn-default">',
+ '<span class="dropdown-select-selected">' + selected + '</span>',
+ '</button>',
+ '</div>'
+ ].join( '\n' ));
+
+ // if there's only one option, do not style/create as buttons, dropdown - use simple span
+ // otherwise, a dropdown displaying the current selection
+ if( options && options.length > 1 ){
+ $select.find( 'button' )
+ .addClass( 'dropdown-toggle' ).attr( 'data-toggle', 'dropdown' )
+ .append( ' <span class="caret"></span>' );
+ $select.append([
+ '<ul class="dropdown-menu" role="menu">',
+ _.map( options, function( option ){
+ return [
+ '<li><a href="javascript:void(0)">', option, '</a></li>'
+ ].join( '' );
+ }).join( '\n' ),
+ '</ul>'
+ ].join( '\n' ));
+ }
+
+ // trigger 'change.dropdown-select' when a new selection is made using the dropdown
+ function selectThis( event ){
+ var $this = $( this ),
+ $select = $this.parents( '.dropdown-select' ),
+ newSelection = $this.text();
+ $select.find( '.dropdown-select-selected' ).text( newSelection );
+ $select.trigger( 'change.dropdown-select', newSelection );
+ }
+
+ $select.find( 'a' ).click( selectThis );
+ return $select;
+ }
+
+ //==============================================================================
/**
* Creates a three part bootstrap button group (key, op, value) meant to
* allow the user control of filters (e.g. { key: 'name', op: 'contains', value: 'my_history' })
diff -r a667776c4a1bc13cf1645c9ddd5af8892cc32b28 -r 864fdca5b06e6f15040298a316746448d09cdb40 client/galaxy/scripts/mvc/ui.js
--- a/client/galaxy/scripts/mvc/ui.js
+++ b/client/galaxy/scripts/mvc/ui.js
@@ -930,200 +930,3 @@
}
});
}());
-
-
-//==============================================================================
-/**
- * Template function that produces a bootstrap dropdown to replace the
- * vanilla HTML select input. Pass in an array of options and an initial selection:
- * $( '.my-div' ).append( dropDownSelect( [ 'option1', 'option2' ], 'option2' );
- *
- * When the user changes the selected option a 'change.dropdown-select' event will
- * fire with both the jq event and the new selection text as arguments.
- *
- * Get the currently selected choice using:
- * var userChoice = $( '.my-div .dropdown-select .dropdown-select-selected' ).text();
- *
- */
-function dropDownSelect( options, selected ){
- // replacement for vanilla select element using bootstrap dropdowns instead
- selected = selected || (( !_.isEmpty( options ) )?( options[0] ):( '' ));
- var $select = $([
- '<div class="dropdown-select btn-group">',
- '<button type="button" class="btn btn-default">',
- '<span class="dropdown-select-selected">' + selected + '</span>',
- '</button>',
- '</div>'
- ].join( '\n' ));
-
- // if there's only one option, do not style/create as buttons, dropdown - use simple span
- // otherwise, a dropdown displaying the current selection
- if( options && options.length > 1 ){
- $select.find( 'button' )
- .addClass( 'dropdown-toggle' ).attr( 'data-toggle', 'dropdown' )
- .append( ' <span class="caret"></span>' );
- $select.append([
- '<ul class="dropdown-menu" role="menu">',
- _.map( options, function( option ){
- return [
- '<li><a href="javascript:void(0)">', option, '</a></li>'
- ].join( '' );
- }).join( '\n' ),
- '</ul>'
- ].join( '\n' ));
- }
-
- // trigger 'change.dropdown-select' when a new selection is made using the dropdown
- function selectThis( event ){
- var $this = $( this ),
- $select = $this.parents( '.dropdown-select' ),
- newSelection = $this.text();
- $select.find( '.dropdown-select-selected' ).text( newSelection );
- $select.trigger( 'change.dropdown-select', newSelection );
- }
-
- $select.find( 'a' ).click( selectThis );
- return $select;
-}
-
-
-//==============================================================================
-(function(){
- /**
- * Creates a three part bootstrap button group (key, op, value) meant to
- * allow the user control of filters (e.g. { key: 'name', op: 'contains', value: 'my_history' })
- *
- * Each field uses a dropDownSelect (from ui.js) to allow selection
- * (with the 'value' field appearing as an input when set to do so).
- *
- * Any change or update in any of the fields will trigger a 'change.filter-control'
- * event which will be passed an object containing those fields (as the example above).
- *
- * Pass in an array of possible filter objects to control what the user can select.
- * Each filter object should have:
- * key : generally the attribute name on which to filter something
- * ops : an array of 1 or more filter operations (e.g. [ 'is', '<', 'contains', '!=' ])
- * values (optional) : an array of possible values for the filter (e.g. [ 'true', 'false' ])
- * @example:
- * $( '.my-div' ).filterControl({
- * filters : [
- * { key: 'name', ops: [ 'is exactly', 'contains' ] }
- * { key: 'deleted', ops: [ 'is' ], values: [ 'true', 'false' ] }
- * ]
- * });
- * // after initialization, you can prog. get the current value using:
- * $( '.my-div' ).filterControl( 'val' )
- *
- */
- function FilterControl( element, options ){
- return this.init( element, options );
- }
- /** the data key that this object will be stored under in the DOM element */
- FilterControl.prototype.DATA_KEY = 'filter-control';
-
- /** parses options, sets up instance vars, and does initial render */
- FilterControl.prototype.init = function _init( element, options ){
- options = options || { filters: [] };
- this.$element = $( element ).addClass( 'filter-control btn-group' );
- this.options = jQuery.extend( true, {}, this.defaults, options );
-
- this.currFilter = this.options.filters[0];
- return this.render();
- };
-
- /** render (or re-render) the controls on the element */
- FilterControl.prototype.render = function _render(){
- this.$element.empty()
- .append([ this._renderKeySelect(), this._renderOpSelect(), this._renderValueInput() ]);
- return this;
- };
-
- /** render the key dropDownSelect, bind a change event to it, and return it */
- FilterControl.prototype._renderKeySelect = function __renderKeySelect(){
- var filterControl = this;
- var keys = this.options.filters.map( function( filter ){
- return filter.key;
- });
- this.$keySelect = dropDownSelect( keys, this.currFilter.key )
- .addClass( 'filter-control-key' )
- .on( 'change.dropdown-select', function( event, selection ){
- filterControl.currFilter = _.findWhere( filterControl.options.filters, { key: selection });
- // when the filter/key changes, re-render the control entirely
- filterControl.render()._triggerChange();
- });
- return this.$keySelect;
- };
-
- /** render the op dropDownSelect, bind a change event to it, and return it */
- FilterControl.prototype._renderOpSelect = function __renderOpSelect(){
- var filterControl = this,
- ops = this.currFilter.ops;
- //TODO: search for currOp in avail. ops: use that for selected if there; otherwise: first op
- this.$opSelect = dropDownSelect( ops, ops[0] )
- .addClass( 'filter-control-op' )
- .on( 'change.dropdown-select', function( event, selection ){
- filterControl._triggerChange();
- });
- return this.$opSelect;
- };
-
- /** render the value control, bind a change event to it, and return it */
- FilterControl.prototype._renderValueInput = function __renderValueInput(){
- var filterControl = this;
- // if a values attribute is prov. on the filter - make this a dropdown; otherwise, use an input
- if( this.currFilter.values ){
- this.$valueSelect = dropDownSelect( this.currFilter.values, this.currFilter.values[0] )
- .on( 'change.dropdown-select', function( event, selection ){
- filterControl._triggerChange();
- });
- } else {
- //TODO: allow setting a value type (mainly for which html5 input to use: range, number, etc.)
- this.$valueSelect = $( '<input/>' ).addClass( 'form-control' )
- .on( 'change', function( event, value ){
- filterControl._triggerChange();
- });
- }
- this.$valueSelect.addClass( 'filter-control-value' );
- return this.$valueSelect;
- };
-
- /** return the current state/setting for the filter as a three key object: key, op, value */
- FilterControl.prototype.val = function _val(){
- var key = this.$element.find( '.filter-control-key .dropdown-select-selected' ).text(),
- op = this.$element.find( '.filter-control-op .dropdown-select-selected' ).text(),
- // handle either a dropdown or plain input
- $value = this.$element.find( '.filter-control-value' ),
- value = ( $value.hasClass( 'dropdown-select' ) )?( $value.find( '.dropdown-select-selected' ).text() )
- :( $value.val() );
- return { key: key, op: op, value: value };
- };
-
- // single point of change for change event
- FilterControl.prototype._triggerChange = function __triggerChange(){
- this.$element.trigger( 'change.filter-control', this.val() );
- };
-
-
- // as jq plugin
- jQuery.fn.extend({
- filterControl : function $filterControl( options ){
- var nonOptionsArgs = jQuery.makeArray( arguments ).slice( 1 );
- return this.map( function(){
- var $this = $( this ),
- data = $this.data( FilterControl.prototype.DATA_KEY );
-
- if( jQuery.type( options ) === 'object' ){
- data = new FilterControl( $this, options );
- $this.data( FilterControl.prototype.DATA_KEY, data );
- }
- if( data && jQuery.type( options ) === 'string' ){
- var fn = data[ options ];
- if( jQuery.type( fn ) === 'function' ){
- return fn.apply( data, nonOptionsArgs );
- }
- }
- return this;
- });
- }
- });
-}());
diff -r a667776c4a1bc13cf1645c9ddd5af8892cc32b28 -r 864fdca5b06e6f15040298a316746448d09cdb40 static/scripts/jq-plugins/ui/filter-control.js
--- a/static/scripts/jq-plugins/ui/filter-control.js
+++ b/static/scripts/jq-plugins/ui/filter-control.js
@@ -10,6 +10,61 @@
}
}(function ($) {
+ //==============================================================================
+ /**
+ * Template function that produces a bootstrap dropdown to replace the
+ * vanilla HTML select input. Pass in an array of options and an initial selection:
+ * $( '.my-div' ).append( dropDownSelect( [ 'option1', 'option2' ], 'option2' );
+ *
+ * When the user changes the selected option a 'change.dropdown-select' event will
+ * fire with both the jq event and the new selection text as arguments.
+ *
+ * Get the currently selected choice using:
+ * var userChoice = $( '.my-div .dropdown-select .dropdown-select-selected' ).text();
+ *
+ */
+ function dropDownSelect( options, selected ){
+ // replacement for vanilla select element using bootstrap dropdowns instead
+ selected = selected || (( !_.isEmpty( options ) )?( options[0] ):( '' ));
+ var $select = $([
+ '<div class="dropdown-select btn-group">',
+ '<button type="button" class="btn btn-default">',
+ '<span class="dropdown-select-selected">' + selected + '</span>',
+ '</button>',
+ '</div>'
+ ].join( '\n' ));
+
+ // if there's only one option, do not style/create as buttons, dropdown - use simple span
+ // otherwise, a dropdown displaying the current selection
+ if( options && options.length > 1 ){
+ $select.find( 'button' )
+ .addClass( 'dropdown-toggle' ).attr( 'data-toggle', 'dropdown' )
+ .append( ' <span class="caret"></span>' );
+ $select.append([
+ '<ul class="dropdown-menu" role="menu">',
+ _.map( options, function( option ){
+ return [
+ '<li><a href="javascript:void(0)">', option, '</a></li>'
+ ].join( '' );
+ }).join( '\n' ),
+ '</ul>'
+ ].join( '\n' ));
+ }
+
+ // trigger 'change.dropdown-select' when a new selection is made using the dropdown
+ function selectThis( event ){
+ var $this = $( this ),
+ $select = $this.parents( '.dropdown-select' ),
+ newSelection = $this.text();
+ $select.find( '.dropdown-select-selected' ).text( newSelection );
+ $select.trigger( 'change.dropdown-select', newSelection );
+ }
+
+ $select.find( 'a' ).click( selectThis );
+ return $select;
+ }
+
+ //==============================================================================
/**
* Creates a three part bootstrap button group (key, op, value) meant to
* allow the user control of filters (e.g. { key: 'name', op: 'contains', value: 'my_history' })
diff -r a667776c4a1bc13cf1645c9ddd5af8892cc32b28 -r 864fdca5b06e6f15040298a316746448d09cdb40 static/scripts/mvc/ui.js
--- a/static/scripts/mvc/ui.js
+++ b/static/scripts/mvc/ui.js
@@ -930,200 +930,3 @@
}
});
}());
-
-
-//==============================================================================
-/**
- * Template function that produces a bootstrap dropdown to replace the
- * vanilla HTML select input. Pass in an array of options and an initial selection:
- * $( '.my-div' ).append( dropDownSelect( [ 'option1', 'option2' ], 'option2' );
- *
- * When the user changes the selected option a 'change.dropdown-select' event will
- * fire with both the jq event and the new selection text as arguments.
- *
- * Get the currently selected choice using:
- * var userChoice = $( '.my-div .dropdown-select .dropdown-select-selected' ).text();
- *
- */
-function dropDownSelect( options, selected ){
- // replacement for vanilla select element using bootstrap dropdowns instead
- selected = selected || (( !_.isEmpty( options ) )?( options[0] ):( '' ));
- var $select = $([
- '<div class="dropdown-select btn-group">',
- '<button type="button" class="btn btn-default">',
- '<span class="dropdown-select-selected">' + selected + '</span>',
- '</button>',
- '</div>'
- ].join( '\n' ));
-
- // if there's only one option, do not style/create as buttons, dropdown - use simple span
- // otherwise, a dropdown displaying the current selection
- if( options && options.length > 1 ){
- $select.find( 'button' )
- .addClass( 'dropdown-toggle' ).attr( 'data-toggle', 'dropdown' )
- .append( ' <span class="caret"></span>' );
- $select.append([
- '<ul class="dropdown-menu" role="menu">',
- _.map( options, function( option ){
- return [
- '<li><a href="javascript:void(0)">', option, '</a></li>'
- ].join( '' );
- }).join( '\n' ),
- '</ul>'
- ].join( '\n' ));
- }
-
- // trigger 'change.dropdown-select' when a new selection is made using the dropdown
- function selectThis( event ){
- var $this = $( this ),
- $select = $this.parents( '.dropdown-select' ),
- newSelection = $this.text();
- $select.find( '.dropdown-select-selected' ).text( newSelection );
- $select.trigger( 'change.dropdown-select', newSelection );
- }
-
- $select.find( 'a' ).click( selectThis );
- return $select;
-}
-
-
-//==============================================================================
-(function(){
- /**
- * Creates a three part bootstrap button group (key, op, value) meant to
- * allow the user control of filters (e.g. { key: 'name', op: 'contains', value: 'my_history' })
- *
- * Each field uses a dropDownSelect (from ui.js) to allow selection
- * (with the 'value' field appearing as an input when set to do so).
- *
- * Any change or update in any of the fields will trigger a 'change.filter-control'
- * event which will be passed an object containing those fields (as the example above).
- *
- * Pass in an array of possible filter objects to control what the user can select.
- * Each filter object should have:
- * key : generally the attribute name on which to filter something
- * ops : an array of 1 or more filter operations (e.g. [ 'is', '<', 'contains', '!=' ])
- * values (optional) : an array of possible values for the filter (e.g. [ 'true', 'false' ])
- * @example:
- * $( '.my-div' ).filterControl({
- * filters : [
- * { key: 'name', ops: [ 'is exactly', 'contains' ] }
- * { key: 'deleted', ops: [ 'is' ], values: [ 'true', 'false' ] }
- * ]
- * });
- * // after initialization, you can prog. get the current value using:
- * $( '.my-div' ).filterControl( 'val' )
- *
- */
- function FilterControl( element, options ){
- return this.init( element, options );
- }
- /** the data key that this object will be stored under in the DOM element */
- FilterControl.prototype.DATA_KEY = 'filter-control';
-
- /** parses options, sets up instance vars, and does initial render */
- FilterControl.prototype.init = function _init( element, options ){
- options = options || { filters: [] };
- this.$element = $( element ).addClass( 'filter-control btn-group' );
- this.options = jQuery.extend( true, {}, this.defaults, options );
-
- this.currFilter = this.options.filters[0];
- return this.render();
- };
-
- /** render (or re-render) the controls on the element */
- FilterControl.prototype.render = function _render(){
- this.$element.empty()
- .append([ this._renderKeySelect(), this._renderOpSelect(), this._renderValueInput() ]);
- return this;
- };
-
- /** render the key dropDownSelect, bind a change event to it, and return it */
- FilterControl.prototype._renderKeySelect = function __renderKeySelect(){
- var filterControl = this;
- var keys = this.options.filters.map( function( filter ){
- return filter.key;
- });
- this.$keySelect = dropDownSelect( keys, this.currFilter.key )
- .addClass( 'filter-control-key' )
- .on( 'change.dropdown-select', function( event, selection ){
- filterControl.currFilter = _.findWhere( filterControl.options.filters, { key: selection });
- // when the filter/key changes, re-render the control entirely
- filterControl.render()._triggerChange();
- });
- return this.$keySelect;
- };
-
- /** render the op dropDownSelect, bind a change event to it, and return it */
- FilterControl.prototype._renderOpSelect = function __renderOpSelect(){
- var filterControl = this,
- ops = this.currFilter.ops;
- //TODO: search for currOp in avail. ops: use that for selected if there; otherwise: first op
- this.$opSelect = dropDownSelect( ops, ops[0] )
- .addClass( 'filter-control-op' )
- .on( 'change.dropdown-select', function( event, selection ){
- filterControl._triggerChange();
- });
- return this.$opSelect;
- };
-
- /** render the value control, bind a change event to it, and return it */
- FilterControl.prototype._renderValueInput = function __renderValueInput(){
- var filterControl = this;
- // if a values attribute is prov. on the filter - make this a dropdown; otherwise, use an input
- if( this.currFilter.values ){
- this.$valueSelect = dropDownSelect( this.currFilter.values, this.currFilter.values[0] )
- .on( 'change.dropdown-select', function( event, selection ){
- filterControl._triggerChange();
- });
- } else {
- //TODO: allow setting a value type (mainly for which html5 input to use: range, number, etc.)
- this.$valueSelect = $( '<input/>' ).addClass( 'form-control' )
- .on( 'change', function( event, value ){
- filterControl._triggerChange();
- });
- }
- this.$valueSelect.addClass( 'filter-control-value' );
- return this.$valueSelect;
- };
-
- /** return the current state/setting for the filter as a three key object: key, op, value */
- FilterControl.prototype.val = function _val(){
- var key = this.$element.find( '.filter-control-key .dropdown-select-selected' ).text(),
- op = this.$element.find( '.filter-control-op .dropdown-select-selected' ).text(),
- // handle either a dropdown or plain input
- $value = this.$element.find( '.filter-control-value' ),
- value = ( $value.hasClass( 'dropdown-select' ) )?( $value.find( '.dropdown-select-selected' ).text() )
- :( $value.val() );
- return { key: key, op: op, value: value };
- };
-
- // single point of change for change event
- FilterControl.prototype._triggerChange = function __triggerChange(){
- this.$element.trigger( 'change.filter-control', this.val() );
- };
-
-
- // as jq plugin
- jQuery.fn.extend({
- filterControl : function $filterControl( options ){
- var nonOptionsArgs = jQuery.makeArray( arguments ).slice( 1 );
- return this.map( function(){
- var $this = $( this ),
- data = $this.data( FilterControl.prototype.DATA_KEY );
-
- if( jQuery.type( options ) === 'object' ){
- data = new FilterControl( $this, options );
- $this.data( FilterControl.prototype.DATA_KEY, data );
- }
- if( data && jQuery.type( options ) === 'string' ){
- var fn = data[ options ];
- if( jQuery.type( fn ) === 'function' ){
- return fn.apply( data, nonOptionsArgs );
- }
- }
- return this;
- });
- }
- });
-}());
diff -r a667776c4a1bc13cf1645c9ddd5af8892cc32b28 -r 864fdca5b06e6f15040298a316746448d09cdb40 static/scripts/packed/jq-plugins/ui/filter-control.js
--- a/static/scripts/packed/jq-plugins/ui/filter-control.js
+++ b/static/scripts/packed/jq-plugins/ui/filter-control.js
@@ -1,1 +1,1 @@
-(function(a){if(typeof define==="function"&&define.amd){define(["jquery"],a)}else{a(jQuery)}}(function(c){function f(l,k){return this.init(l,k)}f.prototype.DATA_KEY="filter-control";f.prototype.init=function i(l,k){k=k||{filters:[]};this.$element=c(l).addClass("filter-control btn-group");this.options=jQuery.extend(true,{},this.defaults,k);this.currFilter=this.options.filters[0];return this.render()};f.prototype.render=function e(){this.$element.empty().append([this._renderKeySelect(),this._renderOpSelect(),this._renderValueInput()]);return this};f.prototype._renderKeySelect=function a(){var k=this;var l=this.options.filters.map(function(m){return m.key});this.$keySelect=dropDownSelect(l,this.currFilter.key).addClass("filter-control-key").on("change.dropdown-select",function(n,m){k.currFilter=_.findWhere(k.options.filters,{key:m});k.render()._triggerChange()});return this.$keySelect};f.prototype._renderOpSelect=function j(){var k=this,l=this.currFilter.ops;this.$opSelect=dropDownSelect(l,l[0]).addClass("filter-control-op").on("change.dropdown-select",function(n,m){k._triggerChange()});return this.$opSelect};f.prototype._renderValueInput=function d(){var k=this;if(this.currFilter.values){this.$valueSelect=dropDownSelect(this.currFilter.values,this.currFilter.values[0]).on("change.dropdown-select",function(m,l){k._triggerChange()})}else{this.$valueSelect=c("<input/>").addClass("form-control").on("change",function(l,m){k._triggerChange()})}this.$valueSelect.addClass("filter-control-value");return this.$valueSelect};f.prototype.val=function b(){var l=this.$element.find(".filter-control-key .dropdown-select-selected").text(),n=this.$element.find(".filter-control-op .dropdown-select-selected").text(),k=this.$element.find(".filter-control-value"),m=(k.hasClass("dropdown-select"))?(k.find(".dropdown-select-selected").text()):(k.val());return{key:l,op:n,value:m}};f.prototype._triggerChange=function h(){this.$element.trigger("change.filter-control",this.val())};jQuery.fn.extend({filterControl:function g(l){var k=jQuery.makeArray(arguments).slice(1);return this.map(function(){var o=c(this),n=o.data(f.prototype.DATA_KEY);if(jQuery.type(l)==="object"){n=new f(o,l);o.data(f.prototype.DATA_KEY,n)}if(n&&jQuery.type(l)==="string"){var m=n[l];if(jQuery.type(m)==="function"){return m.apply(n,k)}}return this})}})}));
\ No newline at end of file
+(function(a){if(typeof define==="function"&&define.amd){define(["jquery"],a)}else{a(jQuery)}}(function(c){function j(m,n){n=n||((!_.isEmpty(m))?(m[0]):(""));var l=c(['<div class="dropdown-select btn-group">','<button type="button" class="btn btn-default">','<span class="dropdown-select-selected">'+n+"</span>","</button>","</div>"].join("\n"));if(m&&m.length>1){l.find("button").addClass("dropdown-toggle").attr("data-toggle","dropdown").append(' <span class="caret"></span>');l.append(['<ul class="dropdown-menu" role="menu">',_.map(m,function(p){return['<li><a href="javascript:void(0)">',p,"</a></li>"].join("")}).join("\n"),"</ul>"].join("\n"))}function o(r){var s=c(this),q=s.parents(".dropdown-select"),p=s.text();q.find(".dropdown-select-selected").text(p);q.trigger("change.dropdown-select",p)}l.find("a").click(o);return l}function f(m,l){return this.init(m,l)}f.prototype.DATA_KEY="filter-control";f.prototype.init=function i(m,l){l=l||{filters:[]};this.$element=c(m).addClass("filter-control btn-group");this.options=jQuery.extend(true,{},this.defaults,l);this.currFilter=this.options.filters[0];return this.render()};f.prototype.render=function e(){this.$element.empty().append([this._renderKeySelect(),this._renderOpSelect(),this._renderValueInput()]);return this};f.prototype._renderKeySelect=function a(){var l=this;var m=this.options.filters.map(function(n){return n.key});this.$keySelect=j(m,this.currFilter.key).addClass("filter-control-key").on("change.dropdown-select",function(o,n){l.currFilter=_.findWhere(l.options.filters,{key:n});l.render()._triggerChange()});return this.$keySelect};f.prototype._renderOpSelect=function k(){var l=this,m=this.currFilter.ops;this.$opSelect=j(m,m[0]).addClass("filter-control-op").on("change.dropdown-select",function(o,n){l._triggerChange()});return this.$opSelect};f.prototype._renderValueInput=function d(){var l=this;if(this.currFilter.values){this.$valueSelect=j(this.currFilter.values,this.currFilter.values[0]).on("change.dropdown-select",function(n,m){l._triggerChange()})}else{this.$valueSelect=c("<input/>").addClass("form-control").on("change",function(m,n){l._triggerChange()})}this.$valueSelect.addClass("filter-control-value");return this.$valueSelect};f.prototype.val=function b(){var m=this.$element.find(".filter-control-key .dropdown-select-selected").text(),o=this.$element.find(".filter-control-op .dropdown-select-selected").text(),l=this.$element.find(".filter-control-value"),n=(l.hasClass("dropdown-select"))?(l.find(".dropdown-select-selected").text()):(l.val());return{key:m,op:o,value:n}};f.prototype._triggerChange=function h(){this.$element.trigger("change.filter-control",this.val())};jQuery.fn.extend({filterControl:function g(m){var l=jQuery.makeArray(arguments).slice(1);return this.map(function(){var p=c(this),o=p.data(f.prototype.DATA_KEY);if(jQuery.type(m)==="object"){o=new f(p,m);p.data(f.prototype.DATA_KEY,o)}if(o&&jQuery.type(m)==="string"){var n=o[m];if(jQuery.type(n)==="function"){return n.apply(o,l)}}return this})}})}));
\ No newline at end of file
diff -r a667776c4a1bc13cf1645c9ddd5af8892cc32b28 -r 864fdca5b06e6f15040298a316746448d09cdb40 static/scripts/packed/mvc/ui.js
--- a/static/scripts/packed/mvc/ui.js
+++ b/static/scripts/packed/mvc/ui.js
@@ -1,1 +1,1 @@
-var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:true,id:null,href:null,target:null,enabled:true,visible:true,tooltip_config:{}}});var IconButtonView=Backbone.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"};this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var a=this.template(this.model.toJSON());a.tooltip(this.model.get("tooltip_config"));this.$el.replaceWith(a);this.setElement(a);return this},events:{click:"click"},click:function(a){if(_.isFunction(this.model.get("on_click"))){this.model.get("on_click")(a);return false}return true},template:function(b){var a='title="'+b.title+'" class="icon-button';if(b.is_menu_button){a+=" menu-button"}a+=" "+b.icon_class;if(!b.enabled){a+="_disabled"}a+='"';if(b.id){a+=' id="'+b.id+'"'}a+=' href="'+b.href+'"';if(b.target){a+=' target="'+b.target+'"'}if(!b.visible){a+=' style="display: none;"'}if(b.enabled){a="<a "+a+"/>"}else{a="<span "+a+"/>"}return $(a)}});var IconButtonCollection=Backbone.Collection.extend({model:IconButton});var IconButtonMenuView=Backbone.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var a=this;this.collection.each(function(d){var b=$("<a/>").attr("href","javascript:void(0)").attr("title",d.attributes.title).addClass("icon-button menu-button").addClass(d.attributes.icon_class).appendTo(a.$el).click(d.attributes.on_click);if(d.attributes.tooltip_config){b.tooltip(d.attributes.tooltip_config)}var c=d.get("options");if(c){make_popupmenu(b,c)}});return this}});var create_icon_buttons_menu=function(b,a){if(!a){a={}}var c=new IconButtonCollection(_.map(b,function(d){return new IconButton(_.extend(d,a))}));return new IconButtonMenuView({collection:c})};var Grid=Backbone.Collection.extend({});var GridView=Backbone.View.extend({});var PopupMenu=Backbone.View.extend({initialize:function(b,a){this.$button=b;if(!this.$button.size()){this.$button=$("<div/>")}this.options=a||[];var c=this;this.$button.click(function(d){$(".popmenu-wrapper").remove();c._renderAndShow(d);return false})},_renderAndShow:function(a){this.render();this.$el.appendTo("body").css(this._getShownPosition(a)).show();this._setUpCloseBehavior()},render:function(){this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options));if(this.options.length){var a=this;this.$el.find("li").each(function(c,b){var d=a.options[c];if(d.func){$(this).children("a.popupmenu-option").click(function(e){d.func.call(a,e,d)})}})}return this},template:function(b,a){return['<ul id="',b,'-menu" class="dropdown-menu">',this._templateOptions(a),"</ul>"].join("")},_templateOptions:function(a){if(!a.length){return"<li>(no options)</li>"}return _.map(a,function(d){if(d.divider){return'<li class="divider"></li>'}else{if(d.header){return['<li class="head"><a href="javascript:void(0);">',d.html,"</a></li>"].join("")}}var c=d.href||"javascript:void(0);",e=(d.target)?(' target="'+d.target+'"'):(""),b=(d.checked)?('<span class="fa fa-check"></span>'):("");return['<li><a class="popupmenu-option" href="',c,'"',e,">",b,d.html,"</a></li>"].join("")}).join("")},_getShownPosition:function(b){var c=this.$el.width();var a=b.pageX-c/2;a=Math.min(a,$(document).scrollLeft()+$(window).width()-c-5);a=Math.max(a,$(document).scrollLeft()+5);return{top:b.pageY,left:a}},_setUpCloseBehavior:function(){var c=this;function a(e){$(document).off("click.close_popup");if(window.parent!==window){try{$(window.parent.document).off("click.close_popup")}catch(d){}}else{try{$("iframe#galaxy_main").contents().off("click.close_popup")}catch(d){}}c.remove()}$("html").one("click.close_popup",a);if(window.parent!==window){try{$(window.parent.document).find("html").one("click.close_popup",a)}catch(b){}}else{try{$("iframe#galaxy_main").contents().one("click.close_popup",a)}catch(b){}}},addItem:function(b,a){a=(a>=0)?a:this.options.length;this.options.splice(a,0,b);return this},removeItem:function(a){if(a>=0){this.options.splice(a,1)}return this},findIndexByHtml:function(b){for(var a=0;a<this.options.length;a++){if(_.has(this.options[a],"html")&&(this.options[a].html===b)){return a}}return null},findItemByHtml:function(a){return this.options[(this.findIndexByHtml(a))]},toString:function(){return"PopupMenu"}});PopupMenu.create=function _create(b,a){return new PopupMenu(b,a)};PopupMenu.make_popupmenu=function(b,c){var a=[];_.each(c,function(f,d){var e={html:d};if(f===null){e.header=true}else{if(jQuery.type(f)==="function"){e.func=f}}a.push(e)});return new PopupMenu($(b),a)};PopupMenu.convertLinksToOptions=function(c,a){c=$(c);a=a||"a";var b=[];c.find(a).each(function(g,e){var f={},d=$(g);f.html=d.text();if(d.attr("href")){var j=d.attr("href"),k=d.attr("target"),h=d.attr("confirm");f.func=function(){if((h)&&(!confirm(h))){return}switch(k){case"_parent":window.parent.location=j;break;case"_top":window.top.location=j;break;default:window.location=j}}}b.push(f)});return b};PopupMenu.fromExistingDom=function(d,c,a){d=$(d);c=$(c);var b=PopupMenu.convertLinksToOptions(c,a);c.remove();return new PopupMenu(d,b)};PopupMenu.make_popup_menus=function(c,b,d){c=c||document;b=b||"div[popupmenu]";d=d||function(e,f){return"#"+e.attr("popupmenu")};var a=[];$(c).find(b).each(function(){var e=$(this),f=$(c).find(d(e,c));a.push(PopupMenu.fromDom(f,e));f.addClass("popup")});return a};var faIconButton=function(a){a=a||{};a.tooltipConfig=a.tooltipConfig||{placement:"bottom"};a.classes=["icon-btn"].concat(a.classes||[]);if(a.disabled){a.classes.push("disabled")}var b=['<a class="',a.classes.join(" "),'"',((a.title)?(' title="'+a.title+'"'):("")),((!a.disabled&&a.target)?(' target="'+a.target+'"'):("")),' href="',((!a.disabled&&a.href)?(a.href):("javascript:void(0);")),'">','<span class="fa ',a.faIcon,'"></span>',"</a>"].join("");var c=$(b).tooltip(a.tooltipConfig);if(_.isFunction(a.onclick)){c.click(a.onclick)}return c};function LoadingIndicator(a,c){var b=this;c=jQuery.extend({cover:false},c||{});function d(){var e=['<div class="loading-indicator">','<div class="loading-indicator-text">','<span class="fa fa-spinner fa-spin fa-lg"></span>','<span class="loading-indicator-message">loading...</span>',"</div>","</div>"].join("\n");var g=$(e).hide().css(c.css||{position:"fixed"}),f=g.children(".loading-indicator-text");if(c.cover){g.css({"z-index":2,top:a.css("top"),bottom:a.css("bottom"),left:a.css("left"),right:a.css("right"),opacity:0.5,"background-color":"white","text-align":"center"});f=g.children(".loading-indicator-text").css({"margin-top":"20px"})}else{f=g.children(".loading-indicator-text").css({margin:"12px 0px 0px 10px",opacity:"0.85",color:"grey"});f.children(".loading-indicator-message").css({margin:"0px 8px 0px 0px","font-style":"italic"})}return g}b.show=function(f,e,g){f=f||"loading...";e=e||"fast";a.parent().find(".loading-indicator").remove();b.$indicator=d().insertBefore(a);b.message(f);b.$indicator.fadeIn(e,g);return b};b.message=function(e){b.$indicator.find("i").text(e)};b.hide=function(e,f){e=e||"fast";if(b.$indicator&&b.$indicator.size()){b.$indicator.fadeOut(e,function(){b.$indicator.remove();if(f){f()}})}else{if(f){f()}}return b};return b}(function(){var b=window._l||function(d){return d};function a(k,q){var e=27,n=13,d=$(k),f=true,h={initialVal:"",name:"search",placeholder:"search",classes:"",onclear:function(){},onfirstsearch:null,onsearch:function(r){},minSearchLen:0,escWillClear:true,oninit:function(){}};function j(r){var s=$(this).parent().children("input");s.focus().val("").trigger("clear:searchInput");q.onclear()}function p(s,r){$(this).trigger("search:searchInput",r);if(typeof q.onfirstsearch==="function"&&f){f=false;q.onfirstsearch(r)}else{q.onsearch(r)}}function g(){return['<input type="text" name="',q.name,'" placeholder="',q.placeholder,'" ','class="search-query ',q.classes,'" ',"/>"].join("")}function m(){return $(g()).focus(function(r){$(this).select()}).keyup(function(s){s.preventDefault();s.stopPropagation();if(!$(this).val()){$(this).blur()}if(s.which===e&&q.escWillClear){j.call(this,s)}else{var r=$(this).val();if((s.which===n)||(q.minSearchLen&&r.length>=q.minSearchLen)){p.call(this,s,r)}else{if(!r.length){j.call(this,s)}}}}).on("change",function(r){p.call(this,r,$(this).val())}).val(q.initialVal)}function l(){return $(['<span class="search-clear fa fa-times-circle" ','title="',b("clear search (esc)"),'"></span>'].join("")).tooltip({placement:"bottom"}).click(function(r){j.call(this,r)})}function o(){return $(['<span class="search-loading fa fa-spinner fa-spin" ','title="',b("loading..."),'"></span>'].join("")).hide().tooltip({placement:"bottom"})}function i(){d.find(".search-loading").toggle();d.find(".search-clear").toggle()}if(jQuery.type(q)==="string"){if(q==="toggle-loading"){i()}return d}if(jQuery.type(q)==="object"){q=jQuery.extend(true,{},h,q)}return d.addClass("search-input").prepend([m(),l(),o()])}jQuery.fn.extend({searchInput:function c(d){return this.each(function(){return a(this,d)})}})}());(function(){function c(o,n){this.currModeIndex=0;return this._init(o,n)}c.prototype.DATA_KEY="mode-button";c.prototype.defaults={switchModesOnClick:true};c.prototype._init=function j(o,n){n=n||{};this.$element=$(o);this.options=jQuery.extend(true,{},this.defaults,n);if(!n.modes){throw new Error('ModeButton requires a "modes" array')}var q=this;this.$element.click(function p(r){q.callModeFn();if(q.options.switchModesOnClick){q._incModeIndex()}$(this).html(q.options.modes[q.currModeIndex].html)});return this.reset()};c.prototype._incModeIndex=function l(){this.currModeIndex+=1;if(this.currModeIndex>=this.options.modes.length){this.currModeIndex=0}return this};c.prototype._getModeIndex=function f(n){for(var o=0;o<this.options.modes.length;o+=1){if(this.options.modes[o].mode===n){return o}}throw new Error("mode not found: "+n)};c.prototype._setModeByIndex=function m(n){var o=this.options.modes[n];if(!o){throw new Error("mode index not found: "+n)}this.currModeIndex=n;if(o.html){this.$element.html(o.html)}return this};c.prototype.currentMode=function d(){return this.options.modes[this.currModeIndex]};c.prototype.current=function i(){return this.currentMode().mode};c.prototype.getMode=function g(n){if(!n){return this.currentMode()}return this.options.modes[(this._getModeIndex(n))]};c.prototype.hasMode=function b(n){try{return !!this.getMode(n)}catch(o){}return false};c.prototype.setMode=function a(n){return this._setModeByIndex(this._getModeIndex(n))};c.prototype.reset=function h(){this.currModeIndex=0;if(this.options.initialMode){this.currModeIndex=this._getModeIndex(this.options.initialMode)}return this._setModeByIndex(this.currModeIndex)};c.prototype.callModeFn=function e(n){var o=this.getMode(n).onclick;if(o&&jQuery.type(o==="function")){return o.call(this.$element.get(0))}return undefined};jQuery.fn.extend({modeButton:function k(n){if(!this.size()){return this}if(jQuery.type(n)==="object"){return this.map(function(){var r=$(this);r.data("mode-button",new c(r,n));return this})}var p=$(this[0]),o=p.data("mode-button");if(!o){throw new Error("modeButton needs an options object or string name of a function")}if(o&&jQuery.type(n)==="string"){var q=n;if(o&&jQuery.type(o[q])==="function"){return o[q].apply(o,jQuery.makeArray(arguments).slice(1))}}return o}})}());function dropDownSelect(b,c){c=c||((!_.isEmpty(b))?(b[0]):(""));var a=$(['<div class="dropdown-select btn-group">','<button type="button" class="btn btn-default">','<span class="dropdown-select-selected">'+c+"</span>","</button>","</div>"].join("\n"));if(b&&b.length>1){a.find("button").addClass("dropdown-toggle").attr("data-toggle","dropdown").append(' <span class="caret"></span>');a.append(['<ul class="dropdown-menu" role="menu">',_.map(b,function(e){return['<li><a href="javascript:void(0)">',e,"</a></li>"].join("")}).join("\n"),"</ul>"].join("\n"))}function d(g){var h=$(this),f=h.parents(".dropdown-select"),e=h.text();f.find(".dropdown-select-selected").text(e);f.trigger("change.dropdown-select",e)}a.find("a").click(d);return a}(function(){function e(k,j){return this.init(k,j)}e.prototype.DATA_KEY="filter-control";e.prototype.init=function g(k,j){j=j||{filters:[]};this.$element=$(k).addClass("filter-control btn-group");this.options=jQuery.extend(true,{},this.defaults,j);this.currFilter=this.options.filters[0];return this.render()};e.prototype.render=function d(){this.$element.empty().append([this._renderKeySelect(),this._renderOpSelect(),this._renderValueInput()]);return this};e.prototype._renderKeySelect=function a(){var j=this;var k=this.options.filters.map(function(l){return l.key});this.$keySelect=dropDownSelect(k,this.currFilter.key).addClass("filter-control-key").on("change.dropdown-select",function(m,l){j.currFilter=_.findWhere(j.options.filters,{key:l});j.render()._triggerChange()});return this.$keySelect};e.prototype._renderOpSelect=function i(){var j=this,k=this.currFilter.ops;this.$opSelect=dropDownSelect(k,k[0]).addClass("filter-control-op").on("change.dropdown-select",function(m,l){j._triggerChange()});return this.$opSelect};e.prototype._renderValueInput=function c(){var j=this;if(this.currFilter.values){this.$valueSelect=dropDownSelect(this.currFilter.values,this.currFilter.values[0]).on("change.dropdown-select",function(l,k){j._triggerChange()})}else{this.$valueSelect=$("<input/>").addClass("form-control").on("change",function(k,l){j._triggerChange()})}this.$valueSelect.addClass("filter-control-value");return this.$valueSelect};e.prototype.val=function b(){var k=this.$element.find(".filter-control-key .dropdown-select-selected").text(),m=this.$element.find(".filter-control-op .dropdown-select-selected").text(),j=this.$element.find(".filter-control-value"),l=(j.hasClass("dropdown-select"))?(j.find(".dropdown-select-selected").text()):(j.val());return{key:k,op:m,value:l}};e.prototype._triggerChange=function h(){this.$element.trigger("change.filter-control",this.val())};jQuery.fn.extend({filterControl:function f(k){var j=jQuery.makeArray(arguments).slice(1);return this.map(function(){var n=$(this),m=n.data(e.prototype.DATA_KEY);if(jQuery.type(k)==="object"){m=new e(n,k);n.data(e.prototype.DATA_KEY,m)}if(m&&jQuery.type(k)==="string"){var l=m[k];if(jQuery.type(l)==="function"){return l.apply(m,j)}}return this})}})}());
\ No newline at end of file
+var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:true,id:null,href:null,target:null,enabled:true,visible:true,tooltip_config:{}}});var IconButtonView=Backbone.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"};this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var a=this.template(this.model.toJSON());a.tooltip(this.model.get("tooltip_config"));this.$el.replaceWith(a);this.setElement(a);return this},events:{click:"click"},click:function(a){if(_.isFunction(this.model.get("on_click"))){this.model.get("on_click")(a);return false}return true},template:function(b){var a='title="'+b.title+'" class="icon-button';if(b.is_menu_button){a+=" menu-button"}a+=" "+b.icon_class;if(!b.enabled){a+="_disabled"}a+='"';if(b.id){a+=' id="'+b.id+'"'}a+=' href="'+b.href+'"';if(b.target){a+=' target="'+b.target+'"'}if(!b.visible){a+=' style="display: none;"'}if(b.enabled){a="<a "+a+"/>"}else{a="<span "+a+"/>"}return $(a)}});var IconButtonCollection=Backbone.Collection.extend({model:IconButton});var IconButtonMenuView=Backbone.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var a=this;this.collection.each(function(d){var b=$("<a/>").attr("href","javascript:void(0)").attr("title",d.attributes.title).addClass("icon-button menu-button").addClass(d.attributes.icon_class).appendTo(a.$el).click(d.attributes.on_click);if(d.attributes.tooltip_config){b.tooltip(d.attributes.tooltip_config)}var c=d.get("options");if(c){make_popupmenu(b,c)}});return this}});var create_icon_buttons_menu=function(b,a){if(!a){a={}}var c=new IconButtonCollection(_.map(b,function(d){return new IconButton(_.extend(d,a))}));return new IconButtonMenuView({collection:c})};var Grid=Backbone.Collection.extend({});var GridView=Backbone.View.extend({});var PopupMenu=Backbone.View.extend({initialize:function(b,a){this.$button=b;if(!this.$button.size()){this.$button=$("<div/>")}this.options=a||[];var c=this;this.$button.click(function(d){$(".popmenu-wrapper").remove();c._renderAndShow(d);return false})},_renderAndShow:function(a){this.render();this.$el.appendTo("body").css(this._getShownPosition(a)).show();this._setUpCloseBehavior()},render:function(){this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options));if(this.options.length){var a=this;this.$el.find("li").each(function(c,b){var d=a.options[c];if(d.func){$(this).children("a.popupmenu-option").click(function(e){d.func.call(a,e,d)})}})}return this},template:function(b,a){return['<ul id="',b,'-menu" class="dropdown-menu">',this._templateOptions(a),"</ul>"].join("")},_templateOptions:function(a){if(!a.length){return"<li>(no options)</li>"}return _.map(a,function(d){if(d.divider){return'<li class="divider"></li>'}else{if(d.header){return['<li class="head"><a href="javascript:void(0);">',d.html,"</a></li>"].join("")}}var c=d.href||"javascript:void(0);",e=(d.target)?(' target="'+d.target+'"'):(""),b=(d.checked)?('<span class="fa fa-check"></span>'):("");return['<li><a class="popupmenu-option" href="',c,'"',e,">",b,d.html,"</a></li>"].join("")}).join("")},_getShownPosition:function(b){var c=this.$el.width();var a=b.pageX-c/2;a=Math.min(a,$(document).scrollLeft()+$(window).width()-c-5);a=Math.max(a,$(document).scrollLeft()+5);return{top:b.pageY,left:a}},_setUpCloseBehavior:function(){var c=this;function a(e){$(document).off("click.close_popup");if(window.parent!==window){try{$(window.parent.document).off("click.close_popup")}catch(d){}}else{try{$("iframe#galaxy_main").contents().off("click.close_popup")}catch(d){}}c.remove()}$("html").one("click.close_popup",a);if(window.parent!==window){try{$(window.parent.document).find("html").one("click.close_popup",a)}catch(b){}}else{try{$("iframe#galaxy_main").contents().one("click.close_popup",a)}catch(b){}}},addItem:function(b,a){a=(a>=0)?a:this.options.length;this.options.splice(a,0,b);return this},removeItem:function(a){if(a>=0){this.options.splice(a,1)}return this},findIndexByHtml:function(b){for(var a=0;a<this.options.length;a++){if(_.has(this.options[a],"html")&&(this.options[a].html===b)){return a}}return null},findItemByHtml:function(a){return this.options[(this.findIndexByHtml(a))]},toString:function(){return"PopupMenu"}});PopupMenu.create=function _create(b,a){return new PopupMenu(b,a)};PopupMenu.make_popupmenu=function(b,c){var a=[];_.each(c,function(f,d){var e={html:d};if(f===null){e.header=true}else{if(jQuery.type(f)==="function"){e.func=f}}a.push(e)});return new PopupMenu($(b),a)};PopupMenu.convertLinksToOptions=function(c,a){c=$(c);a=a||"a";var b=[];c.find(a).each(function(g,e){var f={},d=$(g);f.html=d.text();if(d.attr("href")){var j=d.attr("href"),k=d.attr("target"),h=d.attr("confirm");f.func=function(){if((h)&&(!confirm(h))){return}switch(k){case"_parent":window.parent.location=j;break;case"_top":window.top.location=j;break;default:window.location=j}}}b.push(f)});return b};PopupMenu.fromExistingDom=function(d,c,a){d=$(d);c=$(c);var b=PopupMenu.convertLinksToOptions(c,a);c.remove();return new PopupMenu(d,b)};PopupMenu.make_popup_menus=function(c,b,d){c=c||document;b=b||"div[popupmenu]";d=d||function(e,f){return"#"+e.attr("popupmenu")};var a=[];$(c).find(b).each(function(){var e=$(this),f=$(c).find(d(e,c));a.push(PopupMenu.fromDom(f,e));f.addClass("popup")});return a};var faIconButton=function(a){a=a||{};a.tooltipConfig=a.tooltipConfig||{placement:"bottom"};a.classes=["icon-btn"].concat(a.classes||[]);if(a.disabled){a.classes.push("disabled")}var b=['<a class="',a.classes.join(" "),'"',((a.title)?(' title="'+a.title+'"'):("")),((!a.disabled&&a.target)?(' target="'+a.target+'"'):("")),' href="',((!a.disabled&&a.href)?(a.href):("javascript:void(0);")),'">','<span class="fa ',a.faIcon,'"></span>',"</a>"].join("");var c=$(b).tooltip(a.tooltipConfig);if(_.isFunction(a.onclick)){c.click(a.onclick)}return c};function LoadingIndicator(a,c){var b=this;c=jQuery.extend({cover:false},c||{});function d(){var e=['<div class="loading-indicator">','<div class="loading-indicator-text">','<span class="fa fa-spinner fa-spin fa-lg"></span>','<span class="loading-indicator-message">loading...</span>',"</div>","</div>"].join("\n");var g=$(e).hide().css(c.css||{position:"fixed"}),f=g.children(".loading-indicator-text");if(c.cover){g.css({"z-index":2,top:a.css("top"),bottom:a.css("bottom"),left:a.css("left"),right:a.css("right"),opacity:0.5,"background-color":"white","text-align":"center"});f=g.children(".loading-indicator-text").css({"margin-top":"20px"})}else{f=g.children(".loading-indicator-text").css({margin:"12px 0px 0px 10px",opacity:"0.85",color:"grey"});f.children(".loading-indicator-message").css({margin:"0px 8px 0px 0px","font-style":"italic"})}return g}b.show=function(f,e,g){f=f||"loading...";e=e||"fast";a.parent().find(".loading-indicator").remove();b.$indicator=d().insertBefore(a);b.message(f);b.$indicator.fadeIn(e,g);return b};b.message=function(e){b.$indicator.find("i").text(e)};b.hide=function(e,f){e=e||"fast";if(b.$indicator&&b.$indicator.size()){b.$indicator.fadeOut(e,function(){b.$indicator.remove();if(f){f()}})}else{if(f){f()}}return b};return b}(function(){var b=window._l||function(d){return d};function a(k,q){var e=27,n=13,d=$(k),f=true,h={initialVal:"",name:"search",placeholder:"search",classes:"",onclear:function(){},onfirstsearch:null,onsearch:function(r){},minSearchLen:0,escWillClear:true,oninit:function(){}};function j(r){var s=$(this).parent().children("input");s.focus().val("").trigger("clear:searchInput");q.onclear()}function p(s,r){$(this).trigger("search:searchInput",r);if(typeof q.onfirstsearch==="function"&&f){f=false;q.onfirstsearch(r)}else{q.onsearch(r)}}function g(){return['<input type="text" name="',q.name,'" placeholder="',q.placeholder,'" ','class="search-query ',q.classes,'" ',"/>"].join("")}function m(){return $(g()).focus(function(r){$(this).select()}).keyup(function(s){s.preventDefault();s.stopPropagation();if(!$(this).val()){$(this).blur()}if(s.which===e&&q.escWillClear){j.call(this,s)}else{var r=$(this).val();if((s.which===n)||(q.minSearchLen&&r.length>=q.minSearchLen)){p.call(this,s,r)}else{if(!r.length){j.call(this,s)}}}}).on("change",function(r){p.call(this,r,$(this).val())}).val(q.initialVal)}function l(){return $(['<span class="search-clear fa fa-times-circle" ','title="',b("clear search (esc)"),'"></span>'].join("")).tooltip({placement:"bottom"}).click(function(r){j.call(this,r)})}function o(){return $(['<span class="search-loading fa fa-spinner fa-spin" ','title="',b("loading..."),'"></span>'].join("")).hide().tooltip({placement:"bottom"})}function i(){d.find(".search-loading").toggle();d.find(".search-clear").toggle()}if(jQuery.type(q)==="string"){if(q==="toggle-loading"){i()}return d}if(jQuery.type(q)==="object"){q=jQuery.extend(true,{},h,q)}return d.addClass("search-input").prepend([m(),l(),o()])}jQuery.fn.extend({searchInput:function c(d){return this.each(function(){return a(this,d)})}})}());(function(){function c(o,n){this.currModeIndex=0;return this._init(o,n)}c.prototype.DATA_KEY="mode-button";c.prototype.defaults={switchModesOnClick:true};c.prototype._init=function j(o,n){n=n||{};this.$element=$(o);this.options=jQuery.extend(true,{},this.defaults,n);if(!n.modes){throw new Error('ModeButton requires a "modes" array')}var q=this;this.$element.click(function p(r){q.callModeFn();if(q.options.switchModesOnClick){q._incModeIndex()}$(this).html(q.options.modes[q.currModeIndex].html)});return this.reset()};c.prototype._incModeIndex=function l(){this.currModeIndex+=1;if(this.currModeIndex>=this.options.modes.length){this.currModeIndex=0}return this};c.prototype._getModeIndex=function f(n){for(var o=0;o<this.options.modes.length;o+=1){if(this.options.modes[o].mode===n){return o}}throw new Error("mode not found: "+n)};c.prototype._setModeByIndex=function m(n){var o=this.options.modes[n];if(!o){throw new Error("mode index not found: "+n)}this.currModeIndex=n;if(o.html){this.$element.html(o.html)}return this};c.prototype.currentMode=function d(){return this.options.modes[this.currModeIndex]};c.prototype.current=function i(){return this.currentMode().mode};c.prototype.getMode=function g(n){if(!n){return this.currentMode()}return this.options.modes[(this._getModeIndex(n))]};c.prototype.hasMode=function b(n){try{return !!this.getMode(n)}catch(o){}return false};c.prototype.setMode=function a(n){return this._setModeByIndex(this._getModeIndex(n))};c.prototype.reset=function h(){this.currModeIndex=0;if(this.options.initialMode){this.currModeIndex=this._getModeIndex(this.options.initialMode)}return this._setModeByIndex(this.currModeIndex)};c.prototype.callModeFn=function e(n){var o=this.getMode(n).onclick;if(o&&jQuery.type(o==="function")){return o.call(this.$element.get(0))}return undefined};jQuery.fn.extend({modeButton:function k(n){if(!this.size()){return this}if(jQuery.type(n)==="object"){return this.map(function(){var r=$(this);r.data("mode-button",new c(r,n));return this})}var p=$(this[0]),o=p.data("mode-button");if(!o){throw new Error("modeButton needs an options object or string name of a function")}if(o&&jQuery.type(n)==="string"){var q=n;if(o&&jQuery.type(o[q])==="function"){return o[q].apply(o,jQuery.makeArray(arguments).slice(1))}}return o}})}());
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: Client build: modularize filter control plugin from ui.js
by commits-noreply@bitbucket.org 08 Dec '14
by commits-noreply@bitbucket.org 08 Dec '14
08 Dec '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/a667776c4a1b/
Changeset: a667776c4a1b
User: carlfeberhard
Date: 2014-12-08 20:12:58+00:00
Summary: Client build: modularize filter control plugin from ui.js
Affected #: 3 files
diff -r c600b74bc7ce2dc669e3c34b9b8fde3ddb5ce717 -r a667776c4a1bc13cf1645c9ddd5af8892cc32b28 client/galaxy/scripts/jq-plugins/ui/filter-control.js
--- /dev/null
+++ b/client/galaxy/scripts/jq-plugins/ui/filter-control.js
@@ -0,0 +1,149 @@
+// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js
+// Uses AMD or browser globals to create a jQuery plugin.
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['jquery'], factory);
+ } else {
+ // Browser globals
+ factory(jQuery);
+ }
+
+}(function ($) {
+ /**
+ * Creates a three part bootstrap button group (key, op, value) meant to
+ * allow the user control of filters (e.g. { key: 'name', op: 'contains', value: 'my_history' })
+ *
+ * Each field uses a dropDownSelect (from ui.js) to allow selection
+ * (with the 'value' field appearing as an input when set to do so).
+ *
+ * Any change or update in any of the fields will trigger a 'change.filter-control'
+ * event which will be passed an object containing those fields (as the example above).
+ *
+ * Pass in an array of possible filter objects to control what the user can select.
+ * Each filter object should have:
+ * key : generally the attribute name on which to filter something
+ * ops : an array of 1 or more filter operations (e.g. [ 'is', '<', 'contains', '!=' ])
+ * values (optional) : an array of possible values for the filter (e.g. [ 'true', 'false' ])
+ * @example:
+ * $( '.my-div' ).filterControl({
+ * filters : [
+ * { key: 'name', ops: [ 'is exactly', 'contains' ] }
+ * { key: 'deleted', ops: [ 'is' ], values: [ 'true', 'false' ] }
+ * ]
+ * });
+ * // after initialization, you can prog. get the current value using:
+ * $( '.my-div' ).filterControl( 'val' )
+ *
+ */
+ function FilterControl( element, options ){
+ return this.init( element, options );
+ }
+ /** the data key that this object will be stored under in the DOM element */
+ FilterControl.prototype.DATA_KEY = 'filter-control';
+
+ /** parses options, sets up instance vars, and does initial render */
+ FilterControl.prototype.init = function _init( element, options ){
+ options = options || { filters: [] };
+ this.$element = $( element ).addClass( 'filter-control btn-group' );
+ this.options = jQuery.extend( true, {}, this.defaults, options );
+
+ this.currFilter = this.options.filters[0];
+ return this.render();
+ };
+
+ /** render (or re-render) the controls on the element */
+ FilterControl.prototype.render = function _render(){
+ this.$element.empty()
+ .append([ this._renderKeySelect(), this._renderOpSelect(), this._renderValueInput() ]);
+ return this;
+ };
+
+ /** render the key dropDownSelect, bind a change event to it, and return it */
+ FilterControl.prototype._renderKeySelect = function __renderKeySelect(){
+ var filterControl = this;
+ var keys = this.options.filters.map( function( filter ){
+ return filter.key;
+ });
+ this.$keySelect = dropDownSelect( keys, this.currFilter.key )
+ .addClass( 'filter-control-key' )
+ .on( 'change.dropdown-select', function( event, selection ){
+ filterControl.currFilter = _.findWhere( filterControl.options.filters, { key: selection });
+ // when the filter/key changes, re-render the control entirely
+ filterControl.render()._triggerChange();
+ });
+ return this.$keySelect;
+ };
+
+ /** render the op dropDownSelect, bind a change event to it, and return it */
+ FilterControl.prototype._renderOpSelect = function __renderOpSelect(){
+ var filterControl = this,
+ ops = this.currFilter.ops;
+ //TODO: search for currOp in avail. ops: use that for selected if there; otherwise: first op
+ this.$opSelect = dropDownSelect( ops, ops[0] )
+ .addClass( 'filter-control-op' )
+ .on( 'change.dropdown-select', function( event, selection ){
+ filterControl._triggerChange();
+ });
+ return this.$opSelect;
+ };
+
+ /** render the value control, bind a change event to it, and return it */
+ FilterControl.prototype._renderValueInput = function __renderValueInput(){
+ var filterControl = this;
+ // if a values attribute is prov. on the filter - make this a dropdown; otherwise, use an input
+ if( this.currFilter.values ){
+ this.$valueSelect = dropDownSelect( this.currFilter.values, this.currFilter.values[0] )
+ .on( 'change.dropdown-select', function( event, selection ){
+ filterControl._triggerChange();
+ });
+ } else {
+ //TODO: allow setting a value type (mainly for which html5 input to use: range, number, etc.)
+ this.$valueSelect = $( '<input/>' ).addClass( 'form-control' )
+ .on( 'change', function( event, value ){
+ filterControl._triggerChange();
+ });
+ }
+ this.$valueSelect.addClass( 'filter-control-value' );
+ return this.$valueSelect;
+ };
+
+ /** return the current state/setting for the filter as a three key object: key, op, value */
+ FilterControl.prototype.val = function _val(){
+ var key = this.$element.find( '.filter-control-key .dropdown-select-selected' ).text(),
+ op = this.$element.find( '.filter-control-op .dropdown-select-selected' ).text(),
+ // handle either a dropdown or plain input
+ $value = this.$element.find( '.filter-control-value' ),
+ value = ( $value.hasClass( 'dropdown-select' ) )?( $value.find( '.dropdown-select-selected' ).text() )
+ :( $value.val() );
+ return { key: key, op: op, value: value };
+ };
+
+ // single point of change for change event
+ FilterControl.prototype._triggerChange = function __triggerChange(){
+ this.$element.trigger( 'change.filter-control', this.val() );
+ };
+
+ // as jq plugin
+ jQuery.fn.extend({
+ filterControl : function $filterControl( options ){
+ var nonOptionsArgs = jQuery.makeArray( arguments ).slice( 1 );
+ return this.map( function(){
+ var $this = $( this ),
+ data = $this.data( FilterControl.prototype.DATA_KEY );
+
+ if( jQuery.type( options ) === 'object' ){
+ data = new FilterControl( $this, options );
+ $this.data( FilterControl.prototype.DATA_KEY, data );
+ }
+ if( data && jQuery.type( options ) === 'string' ){
+ var fn = data[ options ];
+ if( jQuery.type( fn ) === 'function' ){
+ return fn.apply( data, nonOptionsArgs );
+ }
+ }
+ return this;
+ });
+ }
+ });
+}));
diff -r c600b74bc7ce2dc669e3c34b9b8fde3ddb5ce717 -r a667776c4a1bc13cf1645c9ddd5af8892cc32b28 static/scripts/jq-plugins/ui/filter-control.js
--- /dev/null
+++ b/static/scripts/jq-plugins/ui/filter-control.js
@@ -0,0 +1,149 @@
+// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js
+// Uses AMD or browser globals to create a jQuery plugin.
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['jquery'], factory);
+ } else {
+ // Browser globals
+ factory(jQuery);
+ }
+
+}(function ($) {
+ /**
+ * Creates a three part bootstrap button group (key, op, value) meant to
+ * allow the user control of filters (e.g. { key: 'name', op: 'contains', value: 'my_history' })
+ *
+ * Each field uses a dropDownSelect (from ui.js) to allow selection
+ * (with the 'value' field appearing as an input when set to do so).
+ *
+ * Any change or update in any of the fields will trigger a 'change.filter-control'
+ * event which will be passed an object containing those fields (as the example above).
+ *
+ * Pass in an array of possible filter objects to control what the user can select.
+ * Each filter object should have:
+ * key : generally the attribute name on which to filter something
+ * ops : an array of 1 or more filter operations (e.g. [ 'is', '<', 'contains', '!=' ])
+ * values (optional) : an array of possible values for the filter (e.g. [ 'true', 'false' ])
+ * @example:
+ * $( '.my-div' ).filterControl({
+ * filters : [
+ * { key: 'name', ops: [ 'is exactly', 'contains' ] }
+ * { key: 'deleted', ops: [ 'is' ], values: [ 'true', 'false' ] }
+ * ]
+ * });
+ * // after initialization, you can prog. get the current value using:
+ * $( '.my-div' ).filterControl( 'val' )
+ *
+ */
+ function FilterControl( element, options ){
+ return this.init( element, options );
+ }
+ /** the data key that this object will be stored under in the DOM element */
+ FilterControl.prototype.DATA_KEY = 'filter-control';
+
+ /** parses options, sets up instance vars, and does initial render */
+ FilterControl.prototype.init = function _init( element, options ){
+ options = options || { filters: [] };
+ this.$element = $( element ).addClass( 'filter-control btn-group' );
+ this.options = jQuery.extend( true, {}, this.defaults, options );
+
+ this.currFilter = this.options.filters[0];
+ return this.render();
+ };
+
+ /** render (or re-render) the controls on the element */
+ FilterControl.prototype.render = function _render(){
+ this.$element.empty()
+ .append([ this._renderKeySelect(), this._renderOpSelect(), this._renderValueInput() ]);
+ return this;
+ };
+
+ /** render the key dropDownSelect, bind a change event to it, and return it */
+ FilterControl.prototype._renderKeySelect = function __renderKeySelect(){
+ var filterControl = this;
+ var keys = this.options.filters.map( function( filter ){
+ return filter.key;
+ });
+ this.$keySelect = dropDownSelect( keys, this.currFilter.key )
+ .addClass( 'filter-control-key' )
+ .on( 'change.dropdown-select', function( event, selection ){
+ filterControl.currFilter = _.findWhere( filterControl.options.filters, { key: selection });
+ // when the filter/key changes, re-render the control entirely
+ filterControl.render()._triggerChange();
+ });
+ return this.$keySelect;
+ };
+
+ /** render the op dropDownSelect, bind a change event to it, and return it */
+ FilterControl.prototype._renderOpSelect = function __renderOpSelect(){
+ var filterControl = this,
+ ops = this.currFilter.ops;
+ //TODO: search for currOp in avail. ops: use that for selected if there; otherwise: first op
+ this.$opSelect = dropDownSelect( ops, ops[0] )
+ .addClass( 'filter-control-op' )
+ .on( 'change.dropdown-select', function( event, selection ){
+ filterControl._triggerChange();
+ });
+ return this.$opSelect;
+ };
+
+ /** render the value control, bind a change event to it, and return it */
+ FilterControl.prototype._renderValueInput = function __renderValueInput(){
+ var filterControl = this;
+ // if a values attribute is prov. on the filter - make this a dropdown; otherwise, use an input
+ if( this.currFilter.values ){
+ this.$valueSelect = dropDownSelect( this.currFilter.values, this.currFilter.values[0] )
+ .on( 'change.dropdown-select', function( event, selection ){
+ filterControl._triggerChange();
+ });
+ } else {
+ //TODO: allow setting a value type (mainly for which html5 input to use: range, number, etc.)
+ this.$valueSelect = $( '<input/>' ).addClass( 'form-control' )
+ .on( 'change', function( event, value ){
+ filterControl._triggerChange();
+ });
+ }
+ this.$valueSelect.addClass( 'filter-control-value' );
+ return this.$valueSelect;
+ };
+
+ /** return the current state/setting for the filter as a three key object: key, op, value */
+ FilterControl.prototype.val = function _val(){
+ var key = this.$element.find( '.filter-control-key .dropdown-select-selected' ).text(),
+ op = this.$element.find( '.filter-control-op .dropdown-select-selected' ).text(),
+ // handle either a dropdown or plain input
+ $value = this.$element.find( '.filter-control-value' ),
+ value = ( $value.hasClass( 'dropdown-select' ) )?( $value.find( '.dropdown-select-selected' ).text() )
+ :( $value.val() );
+ return { key: key, op: op, value: value };
+ };
+
+ // single point of change for change event
+ FilterControl.prototype._triggerChange = function __triggerChange(){
+ this.$element.trigger( 'change.filter-control', this.val() );
+ };
+
+ // as jq plugin
+ jQuery.fn.extend({
+ filterControl : function $filterControl( options ){
+ var nonOptionsArgs = jQuery.makeArray( arguments ).slice( 1 );
+ return this.map( function(){
+ var $this = $( this ),
+ data = $this.data( FilterControl.prototype.DATA_KEY );
+
+ if( jQuery.type( options ) === 'object' ){
+ data = new FilterControl( $this, options );
+ $this.data( FilterControl.prototype.DATA_KEY, data );
+ }
+ if( data && jQuery.type( options ) === 'string' ){
+ var fn = data[ options ];
+ if( jQuery.type( fn ) === 'function' ){
+ return fn.apply( data, nonOptionsArgs );
+ }
+ }
+ return this;
+ });
+ }
+ });
+}));
diff -r c600b74bc7ce2dc669e3c34b9b8fde3ddb5ce717 -r a667776c4a1bc13cf1645c9ddd5af8892cc32b28 static/scripts/packed/jq-plugins/ui/filter-control.js
--- /dev/null
+++ b/static/scripts/packed/jq-plugins/ui/filter-control.js
@@ -0,0 +1,1 @@
+(function(a){if(typeof define==="function"&&define.amd){define(["jquery"],a)}else{a(jQuery)}}(function(c){function f(l,k){return this.init(l,k)}f.prototype.DATA_KEY="filter-control";f.prototype.init=function i(l,k){k=k||{filters:[]};this.$element=c(l).addClass("filter-control btn-group");this.options=jQuery.extend(true,{},this.defaults,k);this.currFilter=this.options.filters[0];return this.render()};f.prototype.render=function e(){this.$element.empty().append([this._renderKeySelect(),this._renderOpSelect(),this._renderValueInput()]);return this};f.prototype._renderKeySelect=function a(){var k=this;var l=this.options.filters.map(function(m){return m.key});this.$keySelect=dropDownSelect(l,this.currFilter.key).addClass("filter-control-key").on("change.dropdown-select",function(n,m){k.currFilter=_.findWhere(k.options.filters,{key:m});k.render()._triggerChange()});return this.$keySelect};f.prototype._renderOpSelect=function j(){var k=this,l=this.currFilter.ops;this.$opSelect=dropDownSelect(l,l[0]).addClass("filter-control-op").on("change.dropdown-select",function(n,m){k._triggerChange()});return this.$opSelect};f.prototype._renderValueInput=function d(){var k=this;if(this.currFilter.values){this.$valueSelect=dropDownSelect(this.currFilter.values,this.currFilter.values[0]).on("change.dropdown-select",function(m,l){k._triggerChange()})}else{this.$valueSelect=c("<input/>").addClass("form-control").on("change",function(l,m){k._triggerChange()})}this.$valueSelect.addClass("filter-control-value");return this.$valueSelect};f.prototype.val=function b(){var l=this.$element.find(".filter-control-key .dropdown-select-selected").text(),n=this.$element.find(".filter-control-op .dropdown-select-selected").text(),k=this.$element.find(".filter-control-value"),m=(k.hasClass("dropdown-select"))?(k.find(".dropdown-select-selected").text()):(k.val());return{key:l,op:n,value:m}};f.prototype._triggerChange=function h(){this.$element.trigger("change.filter-control",this.val())};jQuery.fn.extend({filterControl:function g(l){var k=jQuery.makeArray(arguments).slice(1);return this.map(function(){var o=c(this),n=o.data(f.prototype.DATA_KEY);if(jQuery.type(l)==="object"){n=new f(o,l);o.data(f.prototype.DATA_KEY,n)}if(n&&jQuery.type(l)==="string"){var m=n[l];if(jQuery.type(m)==="function"){return m.apply(n,k)}}return this})}})}));
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: Fix to 9b6a490: actually remove the file...
by commits-noreply@bitbucket.org 08 Dec '14
by commits-noreply@bitbucket.org 08 Dec '14
08 Dec '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/c600b74bc7ce/
Changeset: c600b74bc7ce
User: carlfeberhard
Date: 2014-12-08 20:06:30+00:00
Summary: Fix to 9b6a490: actually remove the file...
Affected #: 3 files
diff -r 9b6a490c80c67174cfa2c0cf6a78c59f54a53088 -r c600b74bc7ce2dc669e3c34b9b8fde3ddb5ce717 client/galaxy/scripts/mvc/ui.js
--- a/client/galaxy/scripts/mvc/ui.js
+++ b/client/galaxy/scripts/mvc/ui.js
@@ -1127,220 +1127,3 @@
}
});
}());
-
-
-//==============================================================================
-(function(){
- /** Builds (twitter bootstrap styled) pagination controls.
- * If the totalDataSize is not null, a horizontal list of page buttons is displayed.
- * If totalDataSize is null, two links ('Prev' and 'Next) are displayed.
- * When pages are changed, a 'pagination.page-change' event is fired
- * sending the event and the (0-based) page requested.
- */
- function Pagination( element, options ){
- /** the total number of pages */
- this.numPages = null;
- /** the current, active page */
- this.currPage = 0;
- return this.init( element, options );
- }
-
- /** data key under which this object will be stored in the element */
- Pagination.prototype.DATA_KEY = 'pagination';
- /** default options */
- Pagination.prototype.defaults = {
- /** which page to begin at */
- startingPage : 0,
- /** number of data per page */
- perPage : 20,
- /** the total number of data (null == unknown) */
- totalDataSize : null,
- /** size of current data on current page */
- currDataSize : null
- };
-
- /** init the control, calc numPages if possible, and render
- * @param {jQuery} the element that will contain the pagination control
- * @param {Object} options a map containing overrides to the pagination default options
- */
- Pagination.prototype.init = function _init( $element, options ){
- options = options || {};
- this.$element = $element;
- this.options = jQuery.extend( true, {}, this.defaults, options );
-
- this.currPage = this.options.startingPage;
- if( this.options.totalDataSize !== null ){
- this.numPages = Math.ceil( this.options.totalDataSize / this.options.perPage );
- // limit currPage by numPages
- if( this.currPage >= this.numPages ){
- this.currPage = this.numPages - 1;
- }
- }
- //console.debug( 'Pagination.prototype.init:', this.$element, this.currPage );
- //console.debug( JSON.stringify( this.options ) );
-
- // bind to data of element
- this.$element.data( Pagination.prototype.DATA_KEY, this );
-
- this._render();
- return this;
- };
-
- /** helper to create a simple li + a combo */
- function _make$Li( contents ){
- return $([
- '<li><a href="javascript:void(0);">', contents, '</a></li>'
- ].join( '' ));
- }
-
- /** render previous and next pagination buttons */
- Pagination.prototype._render = function __render(){
- // no data - no pagination
- if( this.options.totalDataSize === 0 ){ return this; }
- // only one page
- if( this.numPages === 1 ){ return this; }
-
- // when the number of pages are known, render each page as a link
- if( this.numPages > 0 ){
- this._renderPages();
- this._scrollToActivePage();
-
- // when the number of pages is not known, render previous or next
- } else {
- this._renderPrevNext();
- }
- return this;
- };
-
- /** render previous and next pagination buttons */
- Pagination.prototype._renderPrevNext = function __renderPrevNext(){
- var pagination = this,
- $prev = _make$Li( 'Prev' ),
- $next = _make$Li( 'Next' ),
- $paginationContainer = $( '<ul/>' ).addClass( 'pagination pagination-prev-next' );
-
- // disable if it either end
- if( this.currPage === 0 ){
- $prev.addClass( 'disabled' );
- } else {
- $prev.click( function(){ pagination.prevPage(); });
- }
- if( ( this.numPages && this.currPage === ( this.numPages - 1 ) )
- || ( this.options.currDataSize && this.options.currDataSize < this.options.perPage ) ){
- $next.addClass( 'disabled' );
- } else {
- $next.click( function(){ pagination.nextPage(); });
- }
-
- this.$element.html( $paginationContainer.append([ $prev, $next ]) );
- //console.debug( this.$element, this.$element.html() );
- return this.$element;
- };
-
- /** render page links for each possible page (if we can) */
- Pagination.prototype._renderPages = function __renderPages(){
- // it's better to scroll the control and let the user see all pages
- // than to force her/him to change pages in order to find the one they want (as traditional << >> does)
- var pagination = this,
- $scrollingContainer = $( '<div>' ).addClass( 'pagination-scroll-container' ),
- $paginationContainer = $( '<ul/>' ).addClass( 'pagination pagination-page-list' ),
- page$LiClick = function( ev ){
- pagination.goToPage( $( this ).data( 'page' ) );
- };
-
- for( var i=0; i<this.numPages; i+=1 ){
- // add html5 data tag 'page' for later click event handler use
- var $pageLi = _make$Li( i + 1 ).attr( 'data-page', i ).click( page$LiClick );
- // highlight the current page
- if( i === this.currPage ){
- $pageLi.addClass( 'active' );
- }
- //console.debug( '\t', $pageLi );
- $paginationContainer.append( $pageLi );
- }
- return this.$element.html( $scrollingContainer.html( $paginationContainer ) );
- };
-
- /** scroll scroll-container (if any) to show the active page */
- Pagination.prototype._scrollToActivePage = function __scrollToActivePage(){
- // scroll to show active page in center of scrollable area
- var $container = this.$element.find( '.pagination-scroll-container' );
- // no scroll container : don't scroll
- if( !$container.size() ){ return this; }
-
- var $activePage = this.$element.find( 'li.active' ),
- midpoint = $container.width() / 2;
- //console.debug( $container, $activePage, midpoint );
- $container.scrollLeft( $container.scrollLeft() + $activePage.position().left - midpoint );
- return this;
- };
-
- /** go to a certain page */
- Pagination.prototype.goToPage = function goToPage( page ){
- if( page <= 0 ){ page = 0; }
- if( this.numPages && page >= this.numPages ){ page = this.numPages - 1; }
- if( page === this.currPage ){ return this; }
-
- //console.debug( '\t going to page ' + page )
- this.currPage = page;
- this.$element.trigger( 'pagination.page-change', this.currPage );
- //console.info( 'pagination:page-change', this.currPage );
- this._render();
- return this;
- };
-
- /** go to the previous page */
- Pagination.prototype.prevPage = function prevPage(){
- return this.goToPage( this.currPage - 1 );
- };
-
- /** go to the next page */
- Pagination.prototype.nextPage = function nextPage(){
- return this.goToPage( this.currPage + 1 );
- };
-
- /** return the current page */
- Pagination.prototype.page = function page(){
- return this.currPage;
- };
-
- // alternate constructor invocation
- Pagination.create = function _create( $element, options ){
- return new Pagination( $element, options );
- };
-
- // as jq plugin
- jQuery.fn.extend({
- pagination : function $pagination( options ){
- var nonOptionsArgs = jQuery.makeArray( arguments ).slice( 1 );
-
- // if passed an object - use that as an options map to create pagination for each selected
- if( jQuery.type( options ) === 'object' ){
- return this.map( function(){
- Pagination.create( $( this ), options );
- return this;
- });
- }
-
- // (other invocations only work on the first element in selected)
- var $firstElement = $( this[0] ),
- previousControl = $firstElement.data( Pagination.prototype.DATA_KEY );
- // if a pagination control was found for this element, either...
- if( previousControl ){
- // invoke a function on the pagination object if passed a string (the function name)
- if( jQuery.type( options ) === 'string' ){
- var fn = previousControl[ options ];
- if( jQuery.type( fn ) === 'function' ){
- return fn.apply( previousControl, nonOptionsArgs );
- }
-
- // if passed nothing, return the previously set control
- } else {
- return previousControl;
- }
- }
- // if there is no control already set, return undefined
- return undefined;
- }
- });
-}());
diff -r 9b6a490c80c67174cfa2c0cf6a78c59f54a53088 -r c600b74bc7ce2dc669e3c34b9b8fde3ddb5ce717 static/scripts/mvc/ui.js
--- a/static/scripts/mvc/ui.js
+++ b/static/scripts/mvc/ui.js
@@ -1127,220 +1127,3 @@
}
});
}());
-
-
-//==============================================================================
-(function(){
- /** Builds (twitter bootstrap styled) pagination controls.
- * If the totalDataSize is not null, a horizontal list of page buttons is displayed.
- * If totalDataSize is null, two links ('Prev' and 'Next) are displayed.
- * When pages are changed, a 'pagination.page-change' event is fired
- * sending the event and the (0-based) page requested.
- */
- function Pagination( element, options ){
- /** the total number of pages */
- this.numPages = null;
- /** the current, active page */
- this.currPage = 0;
- return this.init( element, options );
- }
-
- /** data key under which this object will be stored in the element */
- Pagination.prototype.DATA_KEY = 'pagination';
- /** default options */
- Pagination.prototype.defaults = {
- /** which page to begin at */
- startingPage : 0,
- /** number of data per page */
- perPage : 20,
- /** the total number of data (null == unknown) */
- totalDataSize : null,
- /** size of current data on current page */
- currDataSize : null
- };
-
- /** init the control, calc numPages if possible, and render
- * @param {jQuery} the element that will contain the pagination control
- * @param {Object} options a map containing overrides to the pagination default options
- */
- Pagination.prototype.init = function _init( $element, options ){
- options = options || {};
- this.$element = $element;
- this.options = jQuery.extend( true, {}, this.defaults, options );
-
- this.currPage = this.options.startingPage;
- if( this.options.totalDataSize !== null ){
- this.numPages = Math.ceil( this.options.totalDataSize / this.options.perPage );
- // limit currPage by numPages
- if( this.currPage >= this.numPages ){
- this.currPage = this.numPages - 1;
- }
- }
- //console.debug( 'Pagination.prototype.init:', this.$element, this.currPage );
- //console.debug( JSON.stringify( this.options ) );
-
- // bind to data of element
- this.$element.data( Pagination.prototype.DATA_KEY, this );
-
- this._render();
- return this;
- };
-
- /** helper to create a simple li + a combo */
- function _make$Li( contents ){
- return $([
- '<li><a href="javascript:void(0);">', contents, '</a></li>'
- ].join( '' ));
- }
-
- /** render previous and next pagination buttons */
- Pagination.prototype._render = function __render(){
- // no data - no pagination
- if( this.options.totalDataSize === 0 ){ return this; }
- // only one page
- if( this.numPages === 1 ){ return this; }
-
- // when the number of pages are known, render each page as a link
- if( this.numPages > 0 ){
- this._renderPages();
- this._scrollToActivePage();
-
- // when the number of pages is not known, render previous or next
- } else {
- this._renderPrevNext();
- }
- return this;
- };
-
- /** render previous and next pagination buttons */
- Pagination.prototype._renderPrevNext = function __renderPrevNext(){
- var pagination = this,
- $prev = _make$Li( 'Prev' ),
- $next = _make$Li( 'Next' ),
- $paginationContainer = $( '<ul/>' ).addClass( 'pagination pagination-prev-next' );
-
- // disable if it either end
- if( this.currPage === 0 ){
- $prev.addClass( 'disabled' );
- } else {
- $prev.click( function(){ pagination.prevPage(); });
- }
- if( ( this.numPages && this.currPage === ( this.numPages - 1 ) )
- || ( this.options.currDataSize && this.options.currDataSize < this.options.perPage ) ){
- $next.addClass( 'disabled' );
- } else {
- $next.click( function(){ pagination.nextPage(); });
- }
-
- this.$element.html( $paginationContainer.append([ $prev, $next ]) );
- //console.debug( this.$element, this.$element.html() );
- return this.$element;
- };
-
- /** render page links for each possible page (if we can) */
- Pagination.prototype._renderPages = function __renderPages(){
- // it's better to scroll the control and let the user see all pages
- // than to force her/him to change pages in order to find the one they want (as traditional << >> does)
- var pagination = this,
- $scrollingContainer = $( '<div>' ).addClass( 'pagination-scroll-container' ),
- $paginationContainer = $( '<ul/>' ).addClass( 'pagination pagination-page-list' ),
- page$LiClick = function( ev ){
- pagination.goToPage( $( this ).data( 'page' ) );
- };
-
- for( var i=0; i<this.numPages; i+=1 ){
- // add html5 data tag 'page' for later click event handler use
- var $pageLi = _make$Li( i + 1 ).attr( 'data-page', i ).click( page$LiClick );
- // highlight the current page
- if( i === this.currPage ){
- $pageLi.addClass( 'active' );
- }
- //console.debug( '\t', $pageLi );
- $paginationContainer.append( $pageLi );
- }
- return this.$element.html( $scrollingContainer.html( $paginationContainer ) );
- };
-
- /** scroll scroll-container (if any) to show the active page */
- Pagination.prototype._scrollToActivePage = function __scrollToActivePage(){
- // scroll to show active page in center of scrollable area
- var $container = this.$element.find( '.pagination-scroll-container' );
- // no scroll container : don't scroll
- if( !$container.size() ){ return this; }
-
- var $activePage = this.$element.find( 'li.active' ),
- midpoint = $container.width() / 2;
- //console.debug( $container, $activePage, midpoint );
- $container.scrollLeft( $container.scrollLeft() + $activePage.position().left - midpoint );
- return this;
- };
-
- /** go to a certain page */
- Pagination.prototype.goToPage = function goToPage( page ){
- if( page <= 0 ){ page = 0; }
- if( this.numPages && page >= this.numPages ){ page = this.numPages - 1; }
- if( page === this.currPage ){ return this; }
-
- //console.debug( '\t going to page ' + page )
- this.currPage = page;
- this.$element.trigger( 'pagination.page-change', this.currPage );
- //console.info( 'pagination:page-change', this.currPage );
- this._render();
- return this;
- };
-
- /** go to the previous page */
- Pagination.prototype.prevPage = function prevPage(){
- return this.goToPage( this.currPage - 1 );
- };
-
- /** go to the next page */
- Pagination.prototype.nextPage = function nextPage(){
- return this.goToPage( this.currPage + 1 );
- };
-
- /** return the current page */
- Pagination.prototype.page = function page(){
- return this.currPage;
- };
-
- // alternate constructor invocation
- Pagination.create = function _create( $element, options ){
- return new Pagination( $element, options );
- };
-
- // as jq plugin
- jQuery.fn.extend({
- pagination : function $pagination( options ){
- var nonOptionsArgs = jQuery.makeArray( arguments ).slice( 1 );
-
- // if passed an object - use that as an options map to create pagination for each selected
- if( jQuery.type( options ) === 'object' ){
- return this.map( function(){
- Pagination.create( $( this ), options );
- return this;
- });
- }
-
- // (other invocations only work on the first element in selected)
- var $firstElement = $( this[0] ),
- previousControl = $firstElement.data( Pagination.prototype.DATA_KEY );
- // if a pagination control was found for this element, either...
- if( previousControl ){
- // invoke a function on the pagination object if passed a string (the function name)
- if( jQuery.type( options ) === 'string' ){
- var fn = previousControl[ options ];
- if( jQuery.type( fn ) === 'function' ){
- return fn.apply( previousControl, nonOptionsArgs );
- }
-
- // if passed nothing, return the previously set control
- } else {
- return previousControl;
- }
- }
- // if there is no control already set, return undefined
- return undefined;
- }
- });
-}());
diff -r 9b6a490c80c67174cfa2c0cf6a78c59f54a53088 -r c600b74bc7ce2dc669e3c34b9b8fde3ddb5ce717 static/scripts/packed/mvc/ui.js
--- a/static/scripts/packed/mvc/ui.js
+++ b/static/scripts/packed/mvc/ui.js
@@ -1,1 +1,1 @@
-var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:true,id:null,href:null,target:null,enabled:true,visible:true,tooltip_config:{}}});var IconButtonView=Backbone.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"};this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var a=this.template(this.model.toJSON());a.tooltip(this.model.get("tooltip_config"));this.$el.replaceWith(a);this.setElement(a);return this},events:{click:"click"},click:function(a){if(_.isFunction(this.model.get("on_click"))){this.model.get("on_click")(a);return false}return true},template:function(b){var a='title="'+b.title+'" class="icon-button';if(b.is_menu_button){a+=" menu-button"}a+=" "+b.icon_class;if(!b.enabled){a+="_disabled"}a+='"';if(b.id){a+=' id="'+b.id+'"'}a+=' href="'+b.href+'"';if(b.target){a+=' target="'+b.target+'"'}if(!b.visible){a+=' style="display: none;"'}if(b.enabled){a="<a "+a+"/>"}else{a="<span "+a+"/>"}return $(a)}});var IconButtonCollection=Backbone.Collection.extend({model:IconButton});var IconButtonMenuView=Backbone.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var a=this;this.collection.each(function(d){var b=$("<a/>").attr("href","javascript:void(0)").attr("title",d.attributes.title).addClass("icon-button menu-button").addClass(d.attributes.icon_class).appendTo(a.$el).click(d.attributes.on_click);if(d.attributes.tooltip_config){b.tooltip(d.attributes.tooltip_config)}var c=d.get("options");if(c){make_popupmenu(b,c)}});return this}});var create_icon_buttons_menu=function(b,a){if(!a){a={}}var c=new IconButtonCollection(_.map(b,function(d){return new IconButton(_.extend(d,a))}));return new IconButtonMenuView({collection:c})};var Grid=Backbone.Collection.extend({});var GridView=Backbone.View.extend({});var PopupMenu=Backbone.View.extend({initialize:function(b,a){this.$button=b;if(!this.$button.size()){this.$button=$("<div/>")}this.options=a||[];var c=this;this.$button.click(function(d){$(".popmenu-wrapper").remove();c._renderAndShow(d);return false})},_renderAndShow:function(a){this.render();this.$el.appendTo("body").css(this._getShownPosition(a)).show();this._setUpCloseBehavior()},render:function(){this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options));if(this.options.length){var a=this;this.$el.find("li").each(function(c,b){var d=a.options[c];if(d.func){$(this).children("a.popupmenu-option").click(function(e){d.func.call(a,e,d)})}})}return this},template:function(b,a){return['<ul id="',b,'-menu" class="dropdown-menu">',this._templateOptions(a),"</ul>"].join("")},_templateOptions:function(a){if(!a.length){return"<li>(no options)</li>"}return _.map(a,function(d){if(d.divider){return'<li class="divider"></li>'}else{if(d.header){return['<li class="head"><a href="javascript:void(0);">',d.html,"</a></li>"].join("")}}var c=d.href||"javascript:void(0);",e=(d.target)?(' target="'+d.target+'"'):(""),b=(d.checked)?('<span class="fa fa-check"></span>'):("");return['<li><a class="popupmenu-option" href="',c,'"',e,">",b,d.html,"</a></li>"].join("")}).join("")},_getShownPosition:function(b){var c=this.$el.width();var a=b.pageX-c/2;a=Math.min(a,$(document).scrollLeft()+$(window).width()-c-5);a=Math.max(a,$(document).scrollLeft()+5);return{top:b.pageY,left:a}},_setUpCloseBehavior:function(){var c=this;function a(e){$(document).off("click.close_popup");if(window.parent!==window){try{$(window.parent.document).off("click.close_popup")}catch(d){}}else{try{$("iframe#galaxy_main").contents().off("click.close_popup")}catch(d){}}c.remove()}$("html").one("click.close_popup",a);if(window.parent!==window){try{$(window.parent.document).find("html").one("click.close_popup",a)}catch(b){}}else{try{$("iframe#galaxy_main").contents().one("click.close_popup",a)}catch(b){}}},addItem:function(b,a){a=(a>=0)?a:this.options.length;this.options.splice(a,0,b);return this},removeItem:function(a){if(a>=0){this.options.splice(a,1)}return this},findIndexByHtml:function(b){for(var a=0;a<this.options.length;a++){if(_.has(this.options[a],"html")&&(this.options[a].html===b)){return a}}return null},findItemByHtml:function(a){return this.options[(this.findIndexByHtml(a))]},toString:function(){return"PopupMenu"}});PopupMenu.create=function _create(b,a){return new PopupMenu(b,a)};PopupMenu.make_popupmenu=function(b,c){var a=[];_.each(c,function(f,d){var e={html:d};if(f===null){e.header=true}else{if(jQuery.type(f)==="function"){e.func=f}}a.push(e)});return new PopupMenu($(b),a)};PopupMenu.convertLinksToOptions=function(c,a){c=$(c);a=a||"a";var b=[];c.find(a).each(function(g,e){var f={},d=$(g);f.html=d.text();if(d.attr("href")){var j=d.attr("href"),k=d.attr("target"),h=d.attr("confirm");f.func=function(){if((h)&&(!confirm(h))){return}switch(k){case"_parent":window.parent.location=j;break;case"_top":window.top.location=j;break;default:window.location=j}}}b.push(f)});return b};PopupMenu.fromExistingDom=function(d,c,a){d=$(d);c=$(c);var b=PopupMenu.convertLinksToOptions(c,a);c.remove();return new PopupMenu(d,b)};PopupMenu.make_popup_menus=function(c,b,d){c=c||document;b=b||"div[popupmenu]";d=d||function(e,f){return"#"+e.attr("popupmenu")};var a=[];$(c).find(b).each(function(){var e=$(this),f=$(c).find(d(e,c));a.push(PopupMenu.fromDom(f,e));f.addClass("popup")});return a};var faIconButton=function(a){a=a||{};a.tooltipConfig=a.tooltipConfig||{placement:"bottom"};a.classes=["icon-btn"].concat(a.classes||[]);if(a.disabled){a.classes.push("disabled")}var b=['<a class="',a.classes.join(" "),'"',((a.title)?(' title="'+a.title+'"'):("")),((!a.disabled&&a.target)?(' target="'+a.target+'"'):("")),' href="',((!a.disabled&&a.href)?(a.href):("javascript:void(0);")),'">','<span class="fa ',a.faIcon,'"></span>',"</a>"].join("");var c=$(b).tooltip(a.tooltipConfig);if(_.isFunction(a.onclick)){c.click(a.onclick)}return c};function LoadingIndicator(a,c){var b=this;c=jQuery.extend({cover:false},c||{});function d(){var e=['<div class="loading-indicator">','<div class="loading-indicator-text">','<span class="fa fa-spinner fa-spin fa-lg"></span>','<span class="loading-indicator-message">loading...</span>',"</div>","</div>"].join("\n");var g=$(e).hide().css(c.css||{position:"fixed"}),f=g.children(".loading-indicator-text");if(c.cover){g.css({"z-index":2,top:a.css("top"),bottom:a.css("bottom"),left:a.css("left"),right:a.css("right"),opacity:0.5,"background-color":"white","text-align":"center"});f=g.children(".loading-indicator-text").css({"margin-top":"20px"})}else{f=g.children(".loading-indicator-text").css({margin:"12px 0px 0px 10px",opacity:"0.85",color:"grey"});f.children(".loading-indicator-message").css({margin:"0px 8px 0px 0px","font-style":"italic"})}return g}b.show=function(f,e,g){f=f||"loading...";e=e||"fast";a.parent().find(".loading-indicator").remove();b.$indicator=d().insertBefore(a);b.message(f);b.$indicator.fadeIn(e,g);return b};b.message=function(e){b.$indicator.find("i").text(e)};b.hide=function(e,f){e=e||"fast";if(b.$indicator&&b.$indicator.size()){b.$indicator.fadeOut(e,function(){b.$indicator.remove();if(f){f()}})}else{if(f){f()}}return b};return b}(function(){var b=window._l||function(d){return d};function a(k,q){var e=27,n=13,d=$(k),f=true,h={initialVal:"",name:"search",placeholder:"search",classes:"",onclear:function(){},onfirstsearch:null,onsearch:function(r){},minSearchLen:0,escWillClear:true,oninit:function(){}};function j(r){var s=$(this).parent().children("input");s.focus().val("").trigger("clear:searchInput");q.onclear()}function p(s,r){$(this).trigger("search:searchInput",r);if(typeof q.onfirstsearch==="function"&&f){f=false;q.onfirstsearch(r)}else{q.onsearch(r)}}function g(){return['<input type="text" name="',q.name,'" placeholder="',q.placeholder,'" ','class="search-query ',q.classes,'" ',"/>"].join("")}function m(){return $(g()).focus(function(r){$(this).select()}).keyup(function(s){s.preventDefault();s.stopPropagation();if(!$(this).val()){$(this).blur()}if(s.which===e&&q.escWillClear){j.call(this,s)}else{var r=$(this).val();if((s.which===n)||(q.minSearchLen&&r.length>=q.minSearchLen)){p.call(this,s,r)}else{if(!r.length){j.call(this,s)}}}}).on("change",function(r){p.call(this,r,$(this).val())}).val(q.initialVal)}function l(){return $(['<span class="search-clear fa fa-times-circle" ','title="',b("clear search (esc)"),'"></span>'].join("")).tooltip({placement:"bottom"}).click(function(r){j.call(this,r)})}function o(){return $(['<span class="search-loading fa fa-spinner fa-spin" ','title="',b("loading..."),'"></span>'].join("")).hide().tooltip({placement:"bottom"})}function i(){d.find(".search-loading").toggle();d.find(".search-clear").toggle()}if(jQuery.type(q)==="string"){if(q==="toggle-loading"){i()}return d}if(jQuery.type(q)==="object"){q=jQuery.extend(true,{},h,q)}return d.addClass("search-input").prepend([m(),l(),o()])}jQuery.fn.extend({searchInput:function c(d){return this.each(function(){return a(this,d)})}})}());(function(){function c(o,n){this.currModeIndex=0;return this._init(o,n)}c.prototype.DATA_KEY="mode-button";c.prototype.defaults={switchModesOnClick:true};c.prototype._init=function j(o,n){n=n||{};this.$element=$(o);this.options=jQuery.extend(true,{},this.defaults,n);if(!n.modes){throw new Error('ModeButton requires a "modes" array')}var q=this;this.$element.click(function p(r){q.callModeFn();if(q.options.switchModesOnClick){q._incModeIndex()}$(this).html(q.options.modes[q.currModeIndex].html)});return this.reset()};c.prototype._incModeIndex=function l(){this.currModeIndex+=1;if(this.currModeIndex>=this.options.modes.length){this.currModeIndex=0}return this};c.prototype._getModeIndex=function f(n){for(var o=0;o<this.options.modes.length;o+=1){if(this.options.modes[o].mode===n){return o}}throw new Error("mode not found: "+n)};c.prototype._setModeByIndex=function m(n){var o=this.options.modes[n];if(!o){throw new Error("mode index not found: "+n)}this.currModeIndex=n;if(o.html){this.$element.html(o.html)}return this};c.prototype.currentMode=function d(){return this.options.modes[this.currModeIndex]};c.prototype.current=function i(){return this.currentMode().mode};c.prototype.getMode=function g(n){if(!n){return this.currentMode()}return this.options.modes[(this._getModeIndex(n))]};c.prototype.hasMode=function b(n){try{return !!this.getMode(n)}catch(o){}return false};c.prototype.setMode=function a(n){return this._setModeByIndex(this._getModeIndex(n))};c.prototype.reset=function h(){this.currModeIndex=0;if(this.options.initialMode){this.currModeIndex=this._getModeIndex(this.options.initialMode)}return this._setModeByIndex(this.currModeIndex)};c.prototype.callModeFn=function e(n){var o=this.getMode(n).onclick;if(o&&jQuery.type(o==="function")){return o.call(this.$element.get(0))}return undefined};jQuery.fn.extend({modeButton:function k(n){if(!this.size()){return this}if(jQuery.type(n)==="object"){return this.map(function(){var r=$(this);r.data("mode-button",new c(r,n));return this})}var p=$(this[0]),o=p.data("mode-button");if(!o){throw new Error("modeButton needs an options object or string name of a function")}if(o&&jQuery.type(n)==="string"){var q=n;if(o&&jQuery.type(o[q])==="function"){return o[q].apply(o,jQuery.makeArray(arguments).slice(1))}}return o}})}());function dropDownSelect(b,c){c=c||((!_.isEmpty(b))?(b[0]):(""));var a=$(['<div class="dropdown-select btn-group">','<button type="button" class="btn btn-default">','<span class="dropdown-select-selected">'+c+"</span>","</button>","</div>"].join("\n"));if(b&&b.length>1){a.find("button").addClass("dropdown-toggle").attr("data-toggle","dropdown").append(' <span class="caret"></span>');a.append(['<ul class="dropdown-menu" role="menu">',_.map(b,function(e){return['<li><a href="javascript:void(0)">',e,"</a></li>"].join("")}).join("\n"),"</ul>"].join("\n"))}function d(g){var h=$(this),f=h.parents(".dropdown-select"),e=h.text();f.find(".dropdown-select-selected").text(e);f.trigger("change.dropdown-select",e)}a.find("a").click(d);return a}(function(){function e(k,j){return this.init(k,j)}e.prototype.DATA_KEY="filter-control";e.prototype.init=function g(k,j){j=j||{filters:[]};this.$element=$(k).addClass("filter-control btn-group");this.options=jQuery.extend(true,{},this.defaults,j);this.currFilter=this.options.filters[0];return this.render()};e.prototype.render=function d(){this.$element.empty().append([this._renderKeySelect(),this._renderOpSelect(),this._renderValueInput()]);return this};e.prototype._renderKeySelect=function a(){var j=this;var k=this.options.filters.map(function(l){return l.key});this.$keySelect=dropDownSelect(k,this.currFilter.key).addClass("filter-control-key").on("change.dropdown-select",function(m,l){j.currFilter=_.findWhere(j.options.filters,{key:l});j.render()._triggerChange()});return this.$keySelect};e.prototype._renderOpSelect=function i(){var j=this,k=this.currFilter.ops;this.$opSelect=dropDownSelect(k,k[0]).addClass("filter-control-op").on("change.dropdown-select",function(m,l){j._triggerChange()});return this.$opSelect};e.prototype._renderValueInput=function c(){var j=this;if(this.currFilter.values){this.$valueSelect=dropDownSelect(this.currFilter.values,this.currFilter.values[0]).on("change.dropdown-select",function(l,k){j._triggerChange()})}else{this.$valueSelect=$("<input/>").addClass("form-control").on("change",function(k,l){j._triggerChange()})}this.$valueSelect.addClass("filter-control-value");return this.$valueSelect};e.prototype.val=function b(){var k=this.$element.find(".filter-control-key .dropdown-select-selected").text(),m=this.$element.find(".filter-control-op .dropdown-select-selected").text(),j=this.$element.find(".filter-control-value"),l=(j.hasClass("dropdown-select"))?(j.find(".dropdown-select-selected").text()):(j.val());return{key:k,op:m,value:l}};e.prototype._triggerChange=function h(){this.$element.trigger("change.filter-control",this.val())};jQuery.fn.extend({filterControl:function f(k){var j=jQuery.makeArray(arguments).slice(1);return this.map(function(){var n=$(this),m=n.data(e.prototype.DATA_KEY);if(jQuery.type(k)==="object"){m=new e(n,k);n.data(e.prototype.DATA_KEY,m)}if(m&&jQuery.type(k)==="string"){var l=m[k];if(jQuery.type(l)==="function"){return l.apply(m,j)}}return this})}})}());(function(){function i(o,n){this.numPages=null;this.currPage=0;return this.init(o,n)}i.prototype.DATA_KEY="pagination";i.prototype.defaults={startingPage:0,perPage:20,totalDataSize:null,currDataSize:null};i.prototype.init=function g(n,o){o=o||{};this.$element=n;this.options=jQuery.extend(true,{},this.defaults,o);this.currPage=this.options.startingPage;if(this.options.totalDataSize!==null){this.numPages=Math.ceil(this.options.totalDataSize/this.options.perPage);if(this.currPage>=this.numPages){this.currPage=this.numPages-1}}this.$element.data(i.prototype.DATA_KEY,this);this._render();return this};function m(n){return $(['<li><a href="javascript:void(0);">',n,"</a></li>"].join(""))}i.prototype._render=function e(){if(this.options.totalDataSize===0){return this}if(this.numPages===1){return this}if(this.numPages>0){this._renderPages();this._scrollToActivePage()}else{this._renderPrevNext()}return this};i.prototype._renderPrevNext=function b(){var o=this,p=m("Prev"),n=m("Next"),q=$("<ul/>").addClass("pagination pagination-prev-next");if(this.currPage===0){p.addClass("disabled")}else{p.click(function(){o.prevPage()})}if((this.numPages&&this.currPage===(this.numPages-1))||(this.options.currDataSize&&this.options.currDataSize<this.options.perPage)){n.addClass("disabled")}else{n.click(function(){o.nextPage()})}this.$element.html(q.append([p,n]));return this.$element};i.prototype._renderPages=function a(){var n=this,q=$("<div>").addClass("pagination-scroll-container"),s=$("<ul/>").addClass("pagination pagination-page-list"),r=function(t){n.goToPage($(this).data("page"))};for(var o=0;o<this.numPages;o+=1){var p=m(o+1).attr("data-page",o).click(r);if(o===this.currPage){p.addClass("active")}s.append(p)}return this.$element.html(q.html(s))};i.prototype._scrollToActivePage=function l(){var p=this.$element.find(".pagination-scroll-container");if(!p.size()){return this}var o=this.$element.find("li.active"),n=p.width()/2;p.scrollLeft(p.scrollLeft()+o.position().left-n);return this};i.prototype.goToPage=function j(n){if(n<=0){n=0}if(this.numPages&&n>=this.numPages){n=this.numPages-1}if(n===this.currPage){return this}this.currPage=n;this.$element.trigger("pagination.page-change",this.currPage);this._render();return this};i.prototype.prevPage=function c(){return this.goToPage(this.currPage-1)};i.prototype.nextPage=function h(){return this.goToPage(this.currPage+1)};i.prototype.page=function f(){return this.currPage};i.create=function k(n,o){return new i(n,o)};jQuery.fn.extend({pagination:function d(o){var n=jQuery.makeArray(arguments).slice(1);if(jQuery.type(o)==="object"){return this.map(function(){i.create($(this),o);return this})}var q=$(this[0]),r=q.data(i.prototype.DATA_KEY);if(r){if(jQuery.type(o)==="string"){var p=r[o];if(jQuery.type(p)==="function"){return p.apply(r,n)}}else{return r}}return undefined}})}());
\ No newline at end of file
+var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:true,id:null,href:null,target:null,enabled:true,visible:true,tooltip_config:{}}});var IconButtonView=Backbone.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"};this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var a=this.template(this.model.toJSON());a.tooltip(this.model.get("tooltip_config"));this.$el.replaceWith(a);this.setElement(a);return this},events:{click:"click"},click:function(a){if(_.isFunction(this.model.get("on_click"))){this.model.get("on_click")(a);return false}return true},template:function(b){var a='title="'+b.title+'" class="icon-button';if(b.is_menu_button){a+=" menu-button"}a+=" "+b.icon_class;if(!b.enabled){a+="_disabled"}a+='"';if(b.id){a+=' id="'+b.id+'"'}a+=' href="'+b.href+'"';if(b.target){a+=' target="'+b.target+'"'}if(!b.visible){a+=' style="display: none;"'}if(b.enabled){a="<a "+a+"/>"}else{a="<span "+a+"/>"}return $(a)}});var IconButtonCollection=Backbone.Collection.extend({model:IconButton});var IconButtonMenuView=Backbone.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var a=this;this.collection.each(function(d){var b=$("<a/>").attr("href","javascript:void(0)").attr("title",d.attributes.title).addClass("icon-button menu-button").addClass(d.attributes.icon_class).appendTo(a.$el).click(d.attributes.on_click);if(d.attributes.tooltip_config){b.tooltip(d.attributes.tooltip_config)}var c=d.get("options");if(c){make_popupmenu(b,c)}});return this}});var create_icon_buttons_menu=function(b,a){if(!a){a={}}var c=new IconButtonCollection(_.map(b,function(d){return new IconButton(_.extend(d,a))}));return new IconButtonMenuView({collection:c})};var Grid=Backbone.Collection.extend({});var GridView=Backbone.View.extend({});var PopupMenu=Backbone.View.extend({initialize:function(b,a){this.$button=b;if(!this.$button.size()){this.$button=$("<div/>")}this.options=a||[];var c=this;this.$button.click(function(d){$(".popmenu-wrapper").remove();c._renderAndShow(d);return false})},_renderAndShow:function(a){this.render();this.$el.appendTo("body").css(this._getShownPosition(a)).show();this._setUpCloseBehavior()},render:function(){this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options));if(this.options.length){var a=this;this.$el.find("li").each(function(c,b){var d=a.options[c];if(d.func){$(this).children("a.popupmenu-option").click(function(e){d.func.call(a,e,d)})}})}return this},template:function(b,a){return['<ul id="',b,'-menu" class="dropdown-menu">',this._templateOptions(a),"</ul>"].join("")},_templateOptions:function(a){if(!a.length){return"<li>(no options)</li>"}return _.map(a,function(d){if(d.divider){return'<li class="divider"></li>'}else{if(d.header){return['<li class="head"><a href="javascript:void(0);">',d.html,"</a></li>"].join("")}}var c=d.href||"javascript:void(0);",e=(d.target)?(' target="'+d.target+'"'):(""),b=(d.checked)?('<span class="fa fa-check"></span>'):("");return['<li><a class="popupmenu-option" href="',c,'"',e,">",b,d.html,"</a></li>"].join("")}).join("")},_getShownPosition:function(b){var c=this.$el.width();var a=b.pageX-c/2;a=Math.min(a,$(document).scrollLeft()+$(window).width()-c-5);a=Math.max(a,$(document).scrollLeft()+5);return{top:b.pageY,left:a}},_setUpCloseBehavior:function(){var c=this;function a(e){$(document).off("click.close_popup");if(window.parent!==window){try{$(window.parent.document).off("click.close_popup")}catch(d){}}else{try{$("iframe#galaxy_main").contents().off("click.close_popup")}catch(d){}}c.remove()}$("html").one("click.close_popup",a);if(window.parent!==window){try{$(window.parent.document).find("html").one("click.close_popup",a)}catch(b){}}else{try{$("iframe#galaxy_main").contents().one("click.close_popup",a)}catch(b){}}},addItem:function(b,a){a=(a>=0)?a:this.options.length;this.options.splice(a,0,b);return this},removeItem:function(a){if(a>=0){this.options.splice(a,1)}return this},findIndexByHtml:function(b){for(var a=0;a<this.options.length;a++){if(_.has(this.options[a],"html")&&(this.options[a].html===b)){return a}}return null},findItemByHtml:function(a){return this.options[(this.findIndexByHtml(a))]},toString:function(){return"PopupMenu"}});PopupMenu.create=function _create(b,a){return new PopupMenu(b,a)};PopupMenu.make_popupmenu=function(b,c){var a=[];_.each(c,function(f,d){var e={html:d};if(f===null){e.header=true}else{if(jQuery.type(f)==="function"){e.func=f}}a.push(e)});return new PopupMenu($(b),a)};PopupMenu.convertLinksToOptions=function(c,a){c=$(c);a=a||"a";var b=[];c.find(a).each(function(g,e){var f={},d=$(g);f.html=d.text();if(d.attr("href")){var j=d.attr("href"),k=d.attr("target"),h=d.attr("confirm");f.func=function(){if((h)&&(!confirm(h))){return}switch(k){case"_parent":window.parent.location=j;break;case"_top":window.top.location=j;break;default:window.location=j}}}b.push(f)});return b};PopupMenu.fromExistingDom=function(d,c,a){d=$(d);c=$(c);var b=PopupMenu.convertLinksToOptions(c,a);c.remove();return new PopupMenu(d,b)};PopupMenu.make_popup_menus=function(c,b,d){c=c||document;b=b||"div[popupmenu]";d=d||function(e,f){return"#"+e.attr("popupmenu")};var a=[];$(c).find(b).each(function(){var e=$(this),f=$(c).find(d(e,c));a.push(PopupMenu.fromDom(f,e));f.addClass("popup")});return a};var faIconButton=function(a){a=a||{};a.tooltipConfig=a.tooltipConfig||{placement:"bottom"};a.classes=["icon-btn"].concat(a.classes||[]);if(a.disabled){a.classes.push("disabled")}var b=['<a class="',a.classes.join(" "),'"',((a.title)?(' title="'+a.title+'"'):("")),((!a.disabled&&a.target)?(' target="'+a.target+'"'):("")),' href="',((!a.disabled&&a.href)?(a.href):("javascript:void(0);")),'">','<span class="fa ',a.faIcon,'"></span>',"</a>"].join("");var c=$(b).tooltip(a.tooltipConfig);if(_.isFunction(a.onclick)){c.click(a.onclick)}return c};function LoadingIndicator(a,c){var b=this;c=jQuery.extend({cover:false},c||{});function d(){var e=['<div class="loading-indicator">','<div class="loading-indicator-text">','<span class="fa fa-spinner fa-spin fa-lg"></span>','<span class="loading-indicator-message">loading...</span>',"</div>","</div>"].join("\n");var g=$(e).hide().css(c.css||{position:"fixed"}),f=g.children(".loading-indicator-text");if(c.cover){g.css({"z-index":2,top:a.css("top"),bottom:a.css("bottom"),left:a.css("left"),right:a.css("right"),opacity:0.5,"background-color":"white","text-align":"center"});f=g.children(".loading-indicator-text").css({"margin-top":"20px"})}else{f=g.children(".loading-indicator-text").css({margin:"12px 0px 0px 10px",opacity:"0.85",color:"grey"});f.children(".loading-indicator-message").css({margin:"0px 8px 0px 0px","font-style":"italic"})}return g}b.show=function(f,e,g){f=f||"loading...";e=e||"fast";a.parent().find(".loading-indicator").remove();b.$indicator=d().insertBefore(a);b.message(f);b.$indicator.fadeIn(e,g);return b};b.message=function(e){b.$indicator.find("i").text(e)};b.hide=function(e,f){e=e||"fast";if(b.$indicator&&b.$indicator.size()){b.$indicator.fadeOut(e,function(){b.$indicator.remove();if(f){f()}})}else{if(f){f()}}return b};return b}(function(){var b=window._l||function(d){return d};function a(k,q){var e=27,n=13,d=$(k),f=true,h={initialVal:"",name:"search",placeholder:"search",classes:"",onclear:function(){},onfirstsearch:null,onsearch:function(r){},minSearchLen:0,escWillClear:true,oninit:function(){}};function j(r){var s=$(this).parent().children("input");s.focus().val("").trigger("clear:searchInput");q.onclear()}function p(s,r){$(this).trigger("search:searchInput",r);if(typeof q.onfirstsearch==="function"&&f){f=false;q.onfirstsearch(r)}else{q.onsearch(r)}}function g(){return['<input type="text" name="',q.name,'" placeholder="',q.placeholder,'" ','class="search-query ',q.classes,'" ',"/>"].join("")}function m(){return $(g()).focus(function(r){$(this).select()}).keyup(function(s){s.preventDefault();s.stopPropagation();if(!$(this).val()){$(this).blur()}if(s.which===e&&q.escWillClear){j.call(this,s)}else{var r=$(this).val();if((s.which===n)||(q.minSearchLen&&r.length>=q.minSearchLen)){p.call(this,s,r)}else{if(!r.length){j.call(this,s)}}}}).on("change",function(r){p.call(this,r,$(this).val())}).val(q.initialVal)}function l(){return $(['<span class="search-clear fa fa-times-circle" ','title="',b("clear search (esc)"),'"></span>'].join("")).tooltip({placement:"bottom"}).click(function(r){j.call(this,r)})}function o(){return $(['<span class="search-loading fa fa-spinner fa-spin" ','title="',b("loading..."),'"></span>'].join("")).hide().tooltip({placement:"bottom"})}function i(){d.find(".search-loading").toggle();d.find(".search-clear").toggle()}if(jQuery.type(q)==="string"){if(q==="toggle-loading"){i()}return d}if(jQuery.type(q)==="object"){q=jQuery.extend(true,{},h,q)}return d.addClass("search-input").prepend([m(),l(),o()])}jQuery.fn.extend({searchInput:function c(d){return this.each(function(){return a(this,d)})}})}());(function(){function c(o,n){this.currModeIndex=0;return this._init(o,n)}c.prototype.DATA_KEY="mode-button";c.prototype.defaults={switchModesOnClick:true};c.prototype._init=function j(o,n){n=n||{};this.$element=$(o);this.options=jQuery.extend(true,{},this.defaults,n);if(!n.modes){throw new Error('ModeButton requires a "modes" array')}var q=this;this.$element.click(function p(r){q.callModeFn();if(q.options.switchModesOnClick){q._incModeIndex()}$(this).html(q.options.modes[q.currModeIndex].html)});return this.reset()};c.prototype._incModeIndex=function l(){this.currModeIndex+=1;if(this.currModeIndex>=this.options.modes.length){this.currModeIndex=0}return this};c.prototype._getModeIndex=function f(n){for(var o=0;o<this.options.modes.length;o+=1){if(this.options.modes[o].mode===n){return o}}throw new Error("mode not found: "+n)};c.prototype._setModeByIndex=function m(n){var o=this.options.modes[n];if(!o){throw new Error("mode index not found: "+n)}this.currModeIndex=n;if(o.html){this.$element.html(o.html)}return this};c.prototype.currentMode=function d(){return this.options.modes[this.currModeIndex]};c.prototype.current=function i(){return this.currentMode().mode};c.prototype.getMode=function g(n){if(!n){return this.currentMode()}return this.options.modes[(this._getModeIndex(n))]};c.prototype.hasMode=function b(n){try{return !!this.getMode(n)}catch(o){}return false};c.prototype.setMode=function a(n){return this._setModeByIndex(this._getModeIndex(n))};c.prototype.reset=function h(){this.currModeIndex=0;if(this.options.initialMode){this.currModeIndex=this._getModeIndex(this.options.initialMode)}return this._setModeByIndex(this.currModeIndex)};c.prototype.callModeFn=function e(n){var o=this.getMode(n).onclick;if(o&&jQuery.type(o==="function")){return o.call(this.$element.get(0))}return undefined};jQuery.fn.extend({modeButton:function k(n){if(!this.size()){return this}if(jQuery.type(n)==="object"){return this.map(function(){var r=$(this);r.data("mode-button",new c(r,n));return this})}var p=$(this[0]),o=p.data("mode-button");if(!o){throw new Error("modeButton needs an options object or string name of a function")}if(o&&jQuery.type(n)==="string"){var q=n;if(o&&jQuery.type(o[q])==="function"){return o[q].apply(o,jQuery.makeArray(arguments).slice(1))}}return o}})}());function dropDownSelect(b,c){c=c||((!_.isEmpty(b))?(b[0]):(""));var a=$(['<div class="dropdown-select btn-group">','<button type="button" class="btn btn-default">','<span class="dropdown-select-selected">'+c+"</span>","</button>","</div>"].join("\n"));if(b&&b.length>1){a.find("button").addClass("dropdown-toggle").attr("data-toggle","dropdown").append(' <span class="caret"></span>');a.append(['<ul class="dropdown-menu" role="menu">',_.map(b,function(e){return['<li><a href="javascript:void(0)">',e,"</a></li>"].join("")}).join("\n"),"</ul>"].join("\n"))}function d(g){var h=$(this),f=h.parents(".dropdown-select"),e=h.text();f.find(".dropdown-select-selected").text(e);f.trigger("change.dropdown-select",e)}a.find("a").click(d);return a}(function(){function e(k,j){return this.init(k,j)}e.prototype.DATA_KEY="filter-control";e.prototype.init=function g(k,j){j=j||{filters:[]};this.$element=$(k).addClass("filter-control btn-group");this.options=jQuery.extend(true,{},this.defaults,j);this.currFilter=this.options.filters[0];return this.render()};e.prototype.render=function d(){this.$element.empty().append([this._renderKeySelect(),this._renderOpSelect(),this._renderValueInput()]);return this};e.prototype._renderKeySelect=function a(){var j=this;var k=this.options.filters.map(function(l){return l.key});this.$keySelect=dropDownSelect(k,this.currFilter.key).addClass("filter-control-key").on("change.dropdown-select",function(m,l){j.currFilter=_.findWhere(j.options.filters,{key:l});j.render()._triggerChange()});return this.$keySelect};e.prototype._renderOpSelect=function i(){var j=this,k=this.currFilter.ops;this.$opSelect=dropDownSelect(k,k[0]).addClass("filter-control-op").on("change.dropdown-select",function(m,l){j._triggerChange()});return this.$opSelect};e.prototype._renderValueInput=function c(){var j=this;if(this.currFilter.values){this.$valueSelect=dropDownSelect(this.currFilter.values,this.currFilter.values[0]).on("change.dropdown-select",function(l,k){j._triggerChange()})}else{this.$valueSelect=$("<input/>").addClass("form-control").on("change",function(k,l){j._triggerChange()})}this.$valueSelect.addClass("filter-control-value");return this.$valueSelect};e.prototype.val=function b(){var k=this.$element.find(".filter-control-key .dropdown-select-selected").text(),m=this.$element.find(".filter-control-op .dropdown-select-selected").text(),j=this.$element.find(".filter-control-value"),l=(j.hasClass("dropdown-select"))?(j.find(".dropdown-select-selected").text()):(j.val());return{key:k,op:m,value:l}};e.prototype._triggerChange=function h(){this.$element.trigger("change.filter-control",this.val())};jQuery.fn.extend({filterControl:function f(k){var j=jQuery.makeArray(arguments).slice(1);return this.map(function(){var n=$(this),m=n.data(e.prototype.DATA_KEY);if(jQuery.type(k)==="object"){m=new e(n,k);n.data(e.prototype.DATA_KEY,m)}if(m&&jQuery.type(k)==="string"){var l=m[k];if(jQuery.type(l)==="function"){return l.apply(m,j)}}return this})}})}());
\ No newline at end of file
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: Client build: modularize pagination plugin from ui.js
by commits-noreply@bitbucket.org 08 Dec '14
by commits-noreply@bitbucket.org 08 Dec '14
08 Dec '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/9b6a490c80c6/
Changeset: 9b6a490c80c6
User: carlfeberhard
Date: 2014-12-08 20:04:47+00:00
Summary: Client build: modularize pagination plugin from ui.js
Affected #: 8 files
diff -r ee995df4a7e548befdfa09196b9665f05cfd55f6 -r 9b6a490c80c67174cfa2c0cf6a78c59f54a53088 client/galaxy/scripts/jq-plugins/ui/pagination.js
--- /dev/null
+++ b/client/galaxy/scripts/jq-plugins/ui/pagination.js
@@ -0,0 +1,226 @@
+// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js
+// Uses AMD or browser globals to create a jQuery plugin.
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['jquery'], factory);
+ } else {
+ // Browser globals
+ factory(jQuery);
+ }
+
+}(function ($) {
+
+ /** Builds (twitter bootstrap styled) pagination controls.
+ * If the totalDataSize is not null, a horizontal list of page buttons is displayed.
+ * If totalDataSize is null, two links ('Prev' and 'Next) are displayed.
+ * When pages are changed, a 'pagination.page-change' event is fired
+ * sending the event and the (0-based) page requested.
+ */
+ function Pagination( element, options ){
+ /** the total number of pages */
+ this.numPages = null;
+ /** the current, active page */
+ this.currPage = 0;
+ return this.init( element, options );
+ }
+
+ /** data key under which this object will be stored in the element */
+ Pagination.prototype.DATA_KEY = 'pagination';
+ /** default options */
+ Pagination.prototype.defaults = {
+ /** which page to begin at */
+ startingPage : 0,
+ /** number of data per page */
+ perPage : 20,
+ /** the total number of data (null == unknown) */
+ totalDataSize : null,
+ /** size of current data on current page */
+ currDataSize : null
+ };
+
+ /** init the control, calc numPages if possible, and render
+ * @param {jQuery} the element that will contain the pagination control
+ * @param {Object} options a map containing overrides to the pagination default options
+ */
+ Pagination.prototype.init = function _init( $element, options ){
+ options = options || {};
+ this.$element = $element;
+ this.options = jQuery.extend( true, {}, this.defaults, options );
+
+ this.currPage = this.options.startingPage;
+ if( this.options.totalDataSize !== null ){
+ this.numPages = Math.ceil( this.options.totalDataSize / this.options.perPage );
+ // limit currPage by numPages
+ if( this.currPage >= this.numPages ){
+ this.currPage = this.numPages - 1;
+ }
+ }
+ //console.debug( 'Pagination.prototype.init:', this.$element, this.currPage );
+ //console.debug( JSON.stringify( this.options ) );
+
+ // bind to data of element
+ this.$element.data( Pagination.prototype.DATA_KEY, this );
+
+ this._render();
+ return this;
+ };
+
+ /** helper to create a simple li + a combo */
+ function _make$Li( contents ){
+ return $([
+ '<li><a href="javascript:void(0);">', contents, '</a></li>'
+ ].join( '' ));
+ }
+
+ /** render previous and next pagination buttons */
+ Pagination.prototype._render = function __render(){
+ // no data - no pagination
+ if( this.options.totalDataSize === 0 ){ return this; }
+ // only one page
+ if( this.numPages === 1 ){ return this; }
+
+ // when the number of pages are known, render each page as a link
+ if( this.numPages > 0 ){
+ this._renderPages();
+ this._scrollToActivePage();
+
+ // when the number of pages is not known, render previous or next
+ } else {
+ this._renderPrevNext();
+ }
+ return this;
+ };
+
+ /** render previous and next pagination buttons */
+ Pagination.prototype._renderPrevNext = function __renderPrevNext(){
+ var pagination = this,
+ $prev = _make$Li( 'Prev' ),
+ $next = _make$Li( 'Next' ),
+ $paginationContainer = $( '<ul/>' ).addClass( 'pagination pagination-prev-next' );
+
+ // disable if it either end
+ if( this.currPage === 0 ){
+ $prev.addClass( 'disabled' );
+ } else {
+ $prev.click( function(){ pagination.prevPage(); });
+ }
+ if( ( this.numPages && this.currPage === ( this.numPages - 1 ) )
+ || ( this.options.currDataSize && this.options.currDataSize < this.options.perPage ) ){
+ $next.addClass( 'disabled' );
+ } else {
+ $next.click( function(){ pagination.nextPage(); });
+ }
+
+ this.$element.html( $paginationContainer.append([ $prev, $next ]) );
+ //console.debug( this.$element, this.$element.html() );
+ return this.$element;
+ };
+
+ /** render page links for each possible page (if we can) */
+ Pagination.prototype._renderPages = function __renderPages(){
+ // it's better to scroll the control and let the user see all pages
+ // than to force her/him to change pages in order to find the one they want (as traditional << >> does)
+ var pagination = this,
+ $scrollingContainer = $( '<div>' ).addClass( 'pagination-scroll-container' ),
+ $paginationContainer = $( '<ul/>' ).addClass( 'pagination pagination-page-list' ),
+ page$LiClick = function( ev ){
+ pagination.goToPage( $( this ).data( 'page' ) );
+ };
+
+ for( var i=0; i<this.numPages; i+=1 ){
+ // add html5 data tag 'page' for later click event handler use
+ var $pageLi = _make$Li( i + 1 ).attr( 'data-page', i ).click( page$LiClick );
+ // highlight the current page
+ if( i === this.currPage ){
+ $pageLi.addClass( 'active' );
+ }
+ //console.debug( '\t', $pageLi );
+ $paginationContainer.append( $pageLi );
+ }
+ return this.$element.html( $scrollingContainer.html( $paginationContainer ) );
+ };
+
+ /** scroll scroll-container (if any) to show the active page */
+ Pagination.prototype._scrollToActivePage = function __scrollToActivePage(){
+ // scroll to show active page in center of scrollable area
+ var $container = this.$element.find( '.pagination-scroll-container' );
+ // no scroll container : don't scroll
+ if( !$container.size() ){ return this; }
+
+ var $activePage = this.$element.find( 'li.active' ),
+ midpoint = $container.width() / 2;
+ //console.debug( $container, $activePage, midpoint );
+ $container.scrollLeft( $container.scrollLeft() + $activePage.position().left - midpoint );
+ return this;
+ };
+
+ /** go to a certain page */
+ Pagination.prototype.goToPage = function goToPage( page ){
+ if( page <= 0 ){ page = 0; }
+ if( this.numPages && page >= this.numPages ){ page = this.numPages - 1; }
+ if( page === this.currPage ){ return this; }
+
+ //console.debug( '\t going to page ' + page )
+ this.currPage = page;
+ this.$element.trigger( 'pagination.page-change', this.currPage );
+ //console.info( 'pagination:page-change', this.currPage );
+ this._render();
+ return this;
+ };
+
+ /** go to the previous page */
+ Pagination.prototype.prevPage = function prevPage(){
+ return this.goToPage( this.currPage - 1 );
+ };
+
+ /** go to the next page */
+ Pagination.prototype.nextPage = function nextPage(){
+ return this.goToPage( this.currPage + 1 );
+ };
+
+ /** return the current page */
+ Pagination.prototype.page = function page(){
+ return this.currPage;
+ };
+
+ // alternate constructor invocation
+ Pagination.create = function _create( $element, options ){
+ return new Pagination( $element, options );
+ };
+
+ // as jq plugin
+ jQuery.fn.extend({
+ pagination : function $pagination( options ){
+ var nonOptionsArgs = jQuery.makeArray( arguments ).slice( 1 );
+
+ // if passed an object - use that as an options map to create pagination for each selected
+ if( jQuery.type( options ) === 'object' ){
+ return this.map( function(){
+ Pagination.create( $( this ), options );
+ return this;
+ });
+ }
+
+ // (other invocations only work on the first element in selected)
+ var $firstElement = $( this[0] ),
+ previousControl = $firstElement.data( Pagination.prototype.DATA_KEY );
+ // if a pagination control was found for this element, either...
+ if( previousControl ){
+ // invoke a function on the pagination object if passed a string (the function name)
+ if( jQuery.type( options ) === 'string' ){
+ var fn = previousControl[ options ];
+ if( jQuery.type( fn ) === 'function' ){
+ return fn.apply( previousControl, nonOptionsArgs );
+ }
+
+ // if passed nothing, return the previously set control
+ } else {
+ return previousControl;
+ }
+ }
+ // if there is no control already set, return undefined
+ return undefined;
+ }
+ });
+}));
diff -r ee995df4a7e548befdfa09196b9665f05cfd55f6 -r 9b6a490c80c67174cfa2c0cf6a78c59f54a53088 config/plugins/visualizations/scatterplot/templates/scatterplot.mako
--- a/config/plugins/visualizations/scatterplot/templates/scatterplot.mako
+++ b/config/plugins/visualizations/scatterplot/templates/scatterplot.mako
@@ -33,6 +33,7 @@
'libs/handlebars.runtime',
'mvc/ui',
'jq-plugins/ui/peek-column-selector',
+ 'jq-plugins/ui/pagination',
'mvc/visualization/visualization-model' )}
${h.javascript_link( root + 'plugins/visualizations/scatterplot/static/scatterplot-edit.js' )}
diff -r ee995df4a7e548befdfa09196b9665f05cfd55f6 -r 9b6a490c80c67174cfa2c0cf6a78c59f54a53088 static/scripts/jq-plugins/ui/pagination.js
--- /dev/null
+++ b/static/scripts/jq-plugins/ui/pagination.js
@@ -0,0 +1,226 @@
+// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js
+// Uses AMD or browser globals to create a jQuery plugin.
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['jquery'], factory);
+ } else {
+ // Browser globals
+ factory(jQuery);
+ }
+
+}(function ($) {
+
+ /** Builds (twitter bootstrap styled) pagination controls.
+ * If the totalDataSize is not null, a horizontal list of page buttons is displayed.
+ * If totalDataSize is null, two links ('Prev' and 'Next) are displayed.
+ * When pages are changed, a 'pagination.page-change' event is fired
+ * sending the event and the (0-based) page requested.
+ */
+ function Pagination( element, options ){
+ /** the total number of pages */
+ this.numPages = null;
+ /** the current, active page */
+ this.currPage = 0;
+ return this.init( element, options );
+ }
+
+ /** data key under which this object will be stored in the element */
+ Pagination.prototype.DATA_KEY = 'pagination';
+ /** default options */
+ Pagination.prototype.defaults = {
+ /** which page to begin at */
+ startingPage : 0,
+ /** number of data per page */
+ perPage : 20,
+ /** the total number of data (null == unknown) */
+ totalDataSize : null,
+ /** size of current data on current page */
+ currDataSize : null
+ };
+
+ /** init the control, calc numPages if possible, and render
+ * @param {jQuery} the element that will contain the pagination control
+ * @param {Object} options a map containing overrides to the pagination default options
+ */
+ Pagination.prototype.init = function _init( $element, options ){
+ options = options || {};
+ this.$element = $element;
+ this.options = jQuery.extend( true, {}, this.defaults, options );
+
+ this.currPage = this.options.startingPage;
+ if( this.options.totalDataSize !== null ){
+ this.numPages = Math.ceil( this.options.totalDataSize / this.options.perPage );
+ // limit currPage by numPages
+ if( this.currPage >= this.numPages ){
+ this.currPage = this.numPages - 1;
+ }
+ }
+ //console.debug( 'Pagination.prototype.init:', this.$element, this.currPage );
+ //console.debug( JSON.stringify( this.options ) );
+
+ // bind to data of element
+ this.$element.data( Pagination.prototype.DATA_KEY, this );
+
+ this._render();
+ return this;
+ };
+
+ /** helper to create a simple li + a combo */
+ function _make$Li( contents ){
+ return $([
+ '<li><a href="javascript:void(0);">', contents, '</a></li>'
+ ].join( '' ));
+ }
+
+ /** render previous and next pagination buttons */
+ Pagination.prototype._render = function __render(){
+ // no data - no pagination
+ if( this.options.totalDataSize === 0 ){ return this; }
+ // only one page
+ if( this.numPages === 1 ){ return this; }
+
+ // when the number of pages are known, render each page as a link
+ if( this.numPages > 0 ){
+ this._renderPages();
+ this._scrollToActivePage();
+
+ // when the number of pages is not known, render previous or next
+ } else {
+ this._renderPrevNext();
+ }
+ return this;
+ };
+
+ /** render previous and next pagination buttons */
+ Pagination.prototype._renderPrevNext = function __renderPrevNext(){
+ var pagination = this,
+ $prev = _make$Li( 'Prev' ),
+ $next = _make$Li( 'Next' ),
+ $paginationContainer = $( '<ul/>' ).addClass( 'pagination pagination-prev-next' );
+
+ // disable if it either end
+ if( this.currPage === 0 ){
+ $prev.addClass( 'disabled' );
+ } else {
+ $prev.click( function(){ pagination.prevPage(); });
+ }
+ if( ( this.numPages && this.currPage === ( this.numPages - 1 ) )
+ || ( this.options.currDataSize && this.options.currDataSize < this.options.perPage ) ){
+ $next.addClass( 'disabled' );
+ } else {
+ $next.click( function(){ pagination.nextPage(); });
+ }
+
+ this.$element.html( $paginationContainer.append([ $prev, $next ]) );
+ //console.debug( this.$element, this.$element.html() );
+ return this.$element;
+ };
+
+ /** render page links for each possible page (if we can) */
+ Pagination.prototype._renderPages = function __renderPages(){
+ // it's better to scroll the control and let the user see all pages
+ // than to force her/him to change pages in order to find the one they want (as traditional << >> does)
+ var pagination = this,
+ $scrollingContainer = $( '<div>' ).addClass( 'pagination-scroll-container' ),
+ $paginationContainer = $( '<ul/>' ).addClass( 'pagination pagination-page-list' ),
+ page$LiClick = function( ev ){
+ pagination.goToPage( $( this ).data( 'page' ) );
+ };
+
+ for( var i=0; i<this.numPages; i+=1 ){
+ // add html5 data tag 'page' for later click event handler use
+ var $pageLi = _make$Li( i + 1 ).attr( 'data-page', i ).click( page$LiClick );
+ // highlight the current page
+ if( i === this.currPage ){
+ $pageLi.addClass( 'active' );
+ }
+ //console.debug( '\t', $pageLi );
+ $paginationContainer.append( $pageLi );
+ }
+ return this.$element.html( $scrollingContainer.html( $paginationContainer ) );
+ };
+
+ /** scroll scroll-container (if any) to show the active page */
+ Pagination.prototype._scrollToActivePage = function __scrollToActivePage(){
+ // scroll to show active page in center of scrollable area
+ var $container = this.$element.find( '.pagination-scroll-container' );
+ // no scroll container : don't scroll
+ if( !$container.size() ){ return this; }
+
+ var $activePage = this.$element.find( 'li.active' ),
+ midpoint = $container.width() / 2;
+ //console.debug( $container, $activePage, midpoint );
+ $container.scrollLeft( $container.scrollLeft() + $activePage.position().left - midpoint );
+ return this;
+ };
+
+ /** go to a certain page */
+ Pagination.prototype.goToPage = function goToPage( page ){
+ if( page <= 0 ){ page = 0; }
+ if( this.numPages && page >= this.numPages ){ page = this.numPages - 1; }
+ if( page === this.currPage ){ return this; }
+
+ //console.debug( '\t going to page ' + page )
+ this.currPage = page;
+ this.$element.trigger( 'pagination.page-change', this.currPage );
+ //console.info( 'pagination:page-change', this.currPage );
+ this._render();
+ return this;
+ };
+
+ /** go to the previous page */
+ Pagination.prototype.prevPage = function prevPage(){
+ return this.goToPage( this.currPage - 1 );
+ };
+
+ /** go to the next page */
+ Pagination.prototype.nextPage = function nextPage(){
+ return this.goToPage( this.currPage + 1 );
+ };
+
+ /** return the current page */
+ Pagination.prototype.page = function page(){
+ return this.currPage;
+ };
+
+ // alternate constructor invocation
+ Pagination.create = function _create( $element, options ){
+ return new Pagination( $element, options );
+ };
+
+ // as jq plugin
+ jQuery.fn.extend({
+ pagination : function $pagination( options ){
+ var nonOptionsArgs = jQuery.makeArray( arguments ).slice( 1 );
+
+ // if passed an object - use that as an options map to create pagination for each selected
+ if( jQuery.type( options ) === 'object' ){
+ return this.map( function(){
+ Pagination.create( $( this ), options );
+ return this;
+ });
+ }
+
+ // (other invocations only work on the first element in selected)
+ var $firstElement = $( this[0] ),
+ previousControl = $firstElement.data( Pagination.prototype.DATA_KEY );
+ // if a pagination control was found for this element, either...
+ if( previousControl ){
+ // invoke a function on the pagination object if passed a string (the function name)
+ if( jQuery.type( options ) === 'string' ){
+ var fn = previousControl[ options ];
+ if( jQuery.type( fn ) === 'function' ){
+ return fn.apply( previousControl, nonOptionsArgs );
+ }
+
+ // if passed nothing, return the previously set control
+ } else {
+ return previousControl;
+ }
+ }
+ // if there is no control already set, return undefined
+ return undefined;
+ }
+ });
+}));
diff -r ee995df4a7e548befdfa09196b9665f05cfd55f6 -r 9b6a490c80c67174cfa2c0cf6a78c59f54a53088 static/scripts/packed/jq-plugins/ui/pagination.js
--- /dev/null
+++ b/static/scripts/packed/jq-plugins/ui/pagination.js
@@ -0,0 +1,1 @@
+(function(a){if(typeof define==="function"&&define.amd){define(["jquery"],a)}else{a(jQuery)}}(function(c){function k(p,o){this.numPages=null;this.currPage=0;return this.init(p,o)}k.prototype.DATA_KEY="pagination";k.prototype.defaults={startingPage:0,perPage:20,totalDataSize:null,currDataSize:null};k.prototype.init=function h(o,p){p=p||{};this.$element=o;this.options=jQuery.extend(true,{},this.defaults,p);this.currPage=this.options.startingPage;if(this.options.totalDataSize!==null){this.numPages=Math.ceil(this.options.totalDataSize/this.options.perPage);if(this.currPage>=this.numPages){this.currPage=this.numPages-1}}this.$element.data(k.prototype.DATA_KEY,this);this._render();return this};function n(o){return c(['<li><a href="javascript:void(0);">',o,"</a></li>"].join(""))}k.prototype._render=function f(){if(this.options.totalDataSize===0){return this}if(this.numPages===1){return this}if(this.numPages>0){this._renderPages();this._scrollToActivePage()}else{this._renderPrevNext()}return this};k.prototype._renderPrevNext=function b(){var p=this,q=n("Prev"),o=n("Next"),r=c("<ul/>").addClass("pagination pagination-prev-next");if(this.currPage===0){q.addClass("disabled")}else{q.click(function(){p.prevPage()})}if((this.numPages&&this.currPage===(this.numPages-1))||(this.options.currDataSize&&this.options.currDataSize<this.options.perPage)){o.addClass("disabled")}else{o.click(function(){p.nextPage()})}this.$element.html(r.append([q,o]));return this.$element};k.prototype._renderPages=function a(){var o=this,r=c("<div>").addClass("pagination-scroll-container"),t=c("<ul/>").addClass("pagination pagination-page-list"),s=function(u){o.goToPage(c(this).data("page"))};for(var p=0;p<this.numPages;p+=1){var q=n(p+1).attr("data-page",p).click(s);if(p===this.currPage){q.addClass("active")}t.append(q)}return this.$element.html(r.html(t))};k.prototype._scrollToActivePage=function m(){var q=this.$element.find(".pagination-scroll-container");if(!q.size()){return this}var p=this.$element.find("li.active"),o=q.width()/2;q.scrollLeft(q.scrollLeft()+p.position().left-o);return this};k.prototype.goToPage=function j(o){if(o<=0){o=0}if(this.numPages&&o>=this.numPages){o=this.numPages-1}if(o===this.currPage){return this}this.currPage=o;this.$element.trigger("pagination.page-change",this.currPage);this._render();return this};k.prototype.prevPage=function d(){return this.goToPage(this.currPage-1)};k.prototype.nextPage=function i(){return this.goToPage(this.currPage+1)};k.prototype.page=function g(){return this.currPage};k.create=function l(o,p){return new k(o,p)};jQuery.fn.extend({pagination:function e(p){var o=jQuery.makeArray(arguments).slice(1);if(jQuery.type(p)==="object"){return this.map(function(){k.create(c(this),p);return this})}var r=c(this[0]),s=r.data(k.prototype.DATA_KEY);if(s){if(jQuery.type(p)==="string"){var q=s[p];if(jQuery.type(q)==="function"){return q.apply(s,o)}}else{return s}}return undefined}})}));
\ No newline at end of file
diff -r ee995df4a7e548befdfa09196b9665f05cfd55f6 -r 9b6a490c80c67174cfa2c0cf6a78c59f54a53088 static/style/blue/base.css
--- a/static/style/blue/base.css
+++ b/static/style/blue/base.css
@@ -1642,12 +1642,6 @@
.quota-meter-bar-warn{background-color:#bf822c}
.quota-meter-bar-error{background-color:#b93e3a}
.quota-meter-text{position:absolute;top:50%;left:0;width:100px;height:16px;margin-top:-6px;text-align:center;z-index:9001;color:#000;white-space:nowrap}
-.pagination{margin:0px}
-.pagination-scroll-container{display:inline-block;background-color:#F8F8F8;border-radius:3px;border:1px solid #BFBFBF;overflow:auto}
-.pagination-scroll-container .pagination-page-list{margin:3px 0px 3px 0px}
-.pagination-scroll-container .pagination-page-list>li:first-child>a,.pagination-scroll-container .pagination-page-list>li:first-child>span{border-radius:0px;border-left:0px}
-.pagination-scroll-container .pagination-page-list>li:last-child>a,.pagination-scroll-container .pagination-page-list>li:last-child>span{border-radius:0px}
-.pagination-scroll-container .pagination-page-list>li>a{float:none;position:static;border:1px solid #BFBFBF;border-width:0px 0px 0px 1px}
div.metadataForm{border:solid #aaaaaa 1px}
div.metadataFormTitle{font-weight:bold;padding:5px;padding-left:10px;padding-right:10px;background:#cccccc;background-repeat:repeat-x;background-position:top;border-bottom:solid #aaaaaa 1px}
div.metadataFormBody{background:#FFFFFF;padding:5px 0}
@@ -2112,6 +2106,12 @@
.peek-column-selector .control td:hover .button{background-color:#EEE;border:1px solid black;cursor:pointer;color:black}
.peek-column-selector .control td.disabled .button,.peek-column-selector .control td.disabled:hover .button{background-color:transparent;border:1px solid #CCC;cursor:not-allowed;color:#CCC}
.peek-column-selector .control td.selected .button{background-color:black;border:1px solid black;color:white}
+.pagination{margin:0px}
+.pagination-scroll-container{display:inline-block;background-color:#F8F8F8;border-radius:3px;border:1px solid #BFBFBF;overflow:auto}
+.pagination-scroll-container .pagination-page-list{margin:3px 0px 3px 0px}
+.pagination-scroll-container .pagination-page-list>li:first-child>a,.pagination-scroll-container .pagination-page-list>li:first-child>span{border-radius:0px;border-left:0px}
+.pagination-scroll-container .pagination-page-list>li:last-child>a,.pagination-scroll-container .pagination-page-list>li:last-child>span{border-radius:0px}
+.pagination-scroll-container .pagination-page-list>li>a{float:none;position:static;border:1px solid #BFBFBF;border-width:0px 0px 0px 1px}
.toolMenuContainer{color:#000;background:#dfe5f9;min-height:100%;padding:5px 10px}
div.toolSectionPad{margin:0;padding:0;height:5px;font-size:0px}
div.toolSectionWrapper{margin-bottom:5px}
diff -r ee995df4a7e548befdfa09196b9665f05cfd55f6 -r 9b6a490c80c67174cfa2c0cf6a78c59f54a53088 static/style/src/less/base.less
--- a/static/style/src/less/base.less
+++ b/static/style/src/less/base.less
@@ -509,42 +509,6 @@
white-space: nowrap;
}
-// ---------------------------------------------------------------------------- pagination & scrolling pagination
-.pagination {
- margin: 0px;
-}
-
-.pagination-scroll-container {
- display : inline-block;
- background-color: #F8F8F8;
- border-radius : 3px;
- border : 1px solid #BFBFBF;
- overflow : auto;
-}
-
-.pagination-scroll-container .pagination-page-list {
- margin : 3px 0px 3px 0px;
-}
-
-.pagination-scroll-container .pagination-page-list > li:first-child > a,
-.pagination-scroll-container .pagination-page-list > li:first-child > span {
- border-radius : 0px;
- border-left : 0px;
-}
-
-.pagination-scroll-container .pagination-page-list > li:last-child > a,
-.pagination-scroll-container .pagination-page-list > li:last-child > span {
- border-radius : 0px;
-}
-
-.pagination-scroll-container .pagination-page-list > li > a {
- float : none;
- position : static;
- border : 1px solid #BFBFBF;
- border-width : 0px 0px 0px 1px;
-}
-
-
// ==== Tool form styles ====
div.metadataForm {
@@ -1495,6 +1459,7 @@
@import "ui/dataset-choice.less";
@import "ui/peek-column-selector.less";
+@import "ui/pagination.less";
// ==== Tool menu styles
diff -r ee995df4a7e548befdfa09196b9665f05cfd55f6 -r 9b6a490c80c67174cfa2c0cf6a78c59f54a53088 static/style/src/less/ui/pagination.less
--- /dev/null
+++ b/static/style/src/less/ui/pagination.less
@@ -0,0 +1,34 @@
+// pagination & scrolling pagination: see scripts/jq-plugins/ui/pagination.js
+.pagination {
+ margin: 0px;
+}
+
+.pagination-scroll-container {
+ display : inline-block;
+ background-color: #F8F8F8;
+ border-radius : 3px;
+ border : 1px solid #BFBFBF;
+ overflow : auto;
+}
+
+.pagination-scroll-container .pagination-page-list {
+ margin : 3px 0px 3px 0px;
+}
+
+.pagination-scroll-container .pagination-page-list > li:first-child > a,
+.pagination-scroll-container .pagination-page-list > li:first-child > span {
+ border-radius : 0px;
+ border-left : 0px;
+}
+
+.pagination-scroll-container .pagination-page-list > li:last-child > a,
+.pagination-scroll-container .pagination-page-list > li:last-child > span {
+ border-radius : 0px;
+}
+
+.pagination-scroll-container .pagination-page-list > li > a {
+ float : none;
+ position : static;
+ border : 1px solid #BFBFBF;
+ border-width : 0px 0px 0px 1px;
+}
diff -r ee995df4a7e548befdfa09196b9665f05cfd55f6 -r 9b6a490c80c67174cfa2c0cf6a78c59f54a53088 static/style/src/less/ui/peek-column-selector.less
--- a/static/style/src/less/ui/peek-column-selector.less
+++ b/static/style/src/less/ui/peek-column-selector.less
@@ -1,4 +1,4 @@
-// peek-based column chooser, see: scripts/ui/peek-column-selector.js
+// peek-based column chooser, see: scripts/jq-plugins/ui/peek-column-selector.js
.peek-column-selector {
border-radius: 3px;
border: 1px solid rgb(95, 105, 144);
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: carlfeberhard: Client build: begin modularizing ui.js by extracting peekControl/peek-column-selector plugin, use UMD pattern, rename to peek-column-selector, extract less rules to their own file; Plugins, scatterplot: remove require and rework to use the new module
by commits-noreply@bitbucket.org 08 Dec '14
by commits-noreply@bitbucket.org 08 Dec '14
08 Dec '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/ee995df4a7e5/
Changeset: ee995df4a7e5
User: carlfeberhard
Date: 2014-12-08 19:47:48+00:00
Summary: Client build: begin modularizing ui.js by extracting peekControl/peek-column-selector plugin, use UMD pattern, rename to peek-column-selector, extract less rules to their own file; Plugins, scatterplot: remove require and rework to use the new module
Affected #: 12 files
diff -r bfc3b5e52781683efc8024f18c4aa57c65af1e15 -r ee995df4a7e548befdfa09196b9665f05cfd55f6 client/galaxy/scripts/jq-plugins/ui/peek-column-selector.js
--- /dev/null
+++ b/client/galaxy/scripts/jq-plugins/ui/peek-column-selector.js
@@ -0,0 +1,317 @@
+// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js
+// Uses AMD or browser globals to create a jQuery plugin.
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['jquery'], factory);
+ } else {
+ // Browser globals
+ factory(jQuery);
+ }
+
+}(function ($) {
+ //==============================================================================
+ /** Column selection using the peek display as the control.
+ * Adds rows to the bottom of the peek with clickable areas in each cell
+ * to allow the user to select columns.
+ * Column selection can be limited to a single column or multiple.
+ * (Optionally) adds a left hand column of column selection prompts.
+ * (Optionally) allows the column headers to be clicked/renamed
+ * and set to some initial value.
+ * (Optionally) hides comment rows.
+ * (Optionally) allows pre-selecting and disabling certain columns for
+ * each row control.
+ *
+ * Construct by selecting a peek table to be used with jQuery and
+ * calling 'peekColumnSelector' with options.
+ * Options must include a 'controls' array and can include other options
+ * listed below.
+ * @example:
+ * $( 'pre.peek' ).peekColumnSelector({
+ * columnNames : ["Chromosome", "Start", "Base", "", "", "Qual" ],
+ * controls : [
+ * { label: 'X Column', id: 'xColumn' },
+ * { label: 'Y Column', id: 'yColumn', selected: 2 },
+ * { label: 'ID Column', id: 'idColumn', selected: 4, disabled: [ 1, 5 ] },
+ * { label: 'Heatmap', id: 'heatmap', selected: [ 2, 4 ], disabled: [ 0, 1 ], multiselect: true,
+ * selectedText: 'Included', unselectedText: 'Excluded' }
+ * ],
+ * renameColumns : true,
+ * hideCommentRows : true,
+ * includePrompts : true,
+ * topLeftContent : 'Data sample:'
+ * }).on( 'peek-column-selector.change', function( ev, selection ){
+ * console.info( 'new selection:', selection );
+ * //{ yColumn: 2 }
+ * }).on( 'peek-column-selector.rename', function( ev, names ){
+ * console.info( 'column names', names );
+ * //[ 'Bler', 'Start', 'Base', '', '', 'Qual' ]
+ * });
+ *
+ * An event is fired when column selection is changed and the event
+ * is passed an object in the form: { the row id : the new selection value }.
+ * An event is also fired when the table headers are re-named and
+ * is passed the new array of column names.
+ */
+
+ /** option defaults */
+ var defaults = {
+ /** does this control allow renaming headers? */
+ renameColumns : false,
+ /** does this control allow renaming headers? */
+ columnNames : [],
+ /** the comment character used by the peek's datatype */
+ commentChar : '#',
+ /** should comment rows be shown or hidden in the peek */
+ hideCommentRows : false,
+ /** should a column of row control prompts be used */
+ includePrompts : true,
+ /** what is the content of the top left cell (often a title) */
+ topLeftContent : 'Columns:'
+ },
+ /** class added to the pre.peek element (to allow css on just the control) */
+ PEEKCONTROL_CLASS = 'peek-column-selector',
+ /** the string of the event fired when a control row changes */
+ CHANGE_EVENT = 'peek-column-selector.change',
+ /** the string of the event fired when a column is renamed */
+ RENAME_EVENT = 'peek-column-selector.rename',
+ /** class added to the control rows */
+ ROW_CLASS = 'control',
+ /** class added to the left-hand cells that serve as row prompts */
+ PROMPT_CLASS = 'control-prompt',
+ /** class added to selected _cells_/tds */
+ SELECTED_CLASS = 'selected',
+ /** class added to disabled/un-clickable cells/tds */
+ DISABLED_CLASS = 'disabled',
+ /** class added to the clickable surface within a cell to select it */
+ BUTTON_CLASS = 'button',
+ /** class added to peek table header (th) cells to indicate they can be clicked and are renamable */
+ RENAMABLE_HEADER_CLASS = 'renamable-header',
+ /** the data key used for each cell to store the column index ('data-...') */
+ COLUMN_INDEX_DATA_KEY = 'column-index',
+ /** renamable header data key used to store the column name (w/o the number and dot: '1.Bler') */
+ COLUMN_NAME_DATA_KEY = 'column-name';
+
+ //TODO: not happy with pure functional here - rows should polymorph (multi, single, etc.)
+ //TODO: needs clean up, move handlers to outer scope
+
+ // ........................................................................
+ /** validate the control data sent in for each row */
+ function validateControl( control ){
+ if( control.disabled && jQuery.type( control.disabled ) !== 'array' ){
+ throw new Error( '"disabled" must be defined as an array of indeces: ' + JSON.stringify( control ) );
+ }
+ if( control.multiselect && control.selected && jQuery.type( control.selected ) !== 'array' ){
+ throw new Error( 'Mulitselect rows need an array for "selected": ' + JSON.stringify( control ) );
+ }
+ if( !control.label || !control.id ){
+ throw new Error( 'Peek controls need a label and id for each control row: ' + JSON.stringify( control ) );
+ }
+ if( control.disabled && control.disabled.indexOf( control.selected ) !== -1 ){
+ throw new Error( 'Selected column is in the list of disabled columns: ' + JSON.stringify( control ) );
+ }
+ return control;
+ }
+
+ /** build the inner control surface (i.e. button-like) */
+ function buildButton( control, columnIndex ){
+ return $( '<div/>' ).addClass( BUTTON_CLASS ).text( control.label );
+ }
+
+ /** build the basic (shared) cell structure */
+ function buildControlCell( control, columnIndex ){
+ var $td = $( '<td/>' )
+ .html( buildButton( control, columnIndex ) )
+ .attr( 'data-' + COLUMN_INDEX_DATA_KEY, columnIndex );
+
+ // disable if index in disabled array
+ if( control.disabled && control.disabled.indexOf( columnIndex ) !== -1 ){
+ $td.addClass( DISABLED_CLASS );
+ }
+ return $td;
+ }
+
+ /** set the text of the control based on selected/un */
+ function setSelectedText( $cell, control, columnIndex ){
+ var $button = $cell.children( '.' + BUTTON_CLASS );
+ if( $cell.hasClass( SELECTED_CLASS ) ){
+ $button.html( ( control.selectedText !== undefined )?( control.selectedText ):( control.label ) );
+ } else {
+ $button.html( ( control.unselectedText !== undefined )?( control.unselectedText ):( control.label ) );
+ }
+ }
+
+ /** build a cell for a row that only allows one selection */
+ function buildSingleSelectCell( control, columnIndex ){
+ // only one selection - selected is single index
+ var $cell = buildControlCell( control, columnIndex );
+ if( control.selected === columnIndex ){
+ $cell.addClass( SELECTED_CLASS );
+ }
+ setSelectedText( $cell, control, columnIndex );
+
+ // only add the handler to non-disabled controls
+ if( !$cell.hasClass( DISABLED_CLASS ) ){
+ $cell.click( function selectClick( ev ){
+ var $cell = $( this );
+ // don't re-select or fire event if already selected
+ if( !$cell.hasClass( SELECTED_CLASS ) ){
+ // only one can be selected - remove selected on all others, add it here
+ var $otherSelected = $cell.parent().children( '.' + SELECTED_CLASS ).removeClass( SELECTED_CLASS );
+ $otherSelected.each( function(){
+ setSelectedText( $( this ), control, columnIndex );
+ });
+
+ $cell.addClass( SELECTED_CLASS );
+ setSelectedText( $cell, control, columnIndex );
+
+ // fire the event from the table itself, passing the id and index of selected
+ var eventData = {},
+ key = $cell.parent().attr( 'id' ),
+ val = $cell.data( COLUMN_INDEX_DATA_KEY );
+ eventData[ key ] = val;
+ $cell.parents( '.peek' ).trigger( CHANGE_EVENT, eventData );
+ }
+ });
+ }
+ return $cell;
+ }
+
+ /** build a cell for a row that allows multiple selections */
+ function buildMultiSelectCell( control, columnIndex ){
+ var $cell = buildControlCell( control, columnIndex );
+ // multiple selection - selected is an array
+ if( control.selected && control.selected.indexOf( columnIndex ) !== -1 ){
+ $cell.addClass( SELECTED_CLASS );
+ }
+ setSelectedText( $cell, control, columnIndex );
+
+ // only add the handler to non-disabled controls
+ if( !$cell.hasClass( DISABLED_CLASS ) ){
+ $cell.click( function multiselectClick( ev ){
+ var $cell = $( this );
+ // can be more than one selected - toggle selected on this cell
+ $cell.toggleClass( SELECTED_CLASS );
+ setSelectedText( $cell, control, columnIndex );
+ var selectedColumnIndeces = $cell.parent().find( '.' + SELECTED_CLASS ).map( function( i, e ){
+ return $( e ).data( COLUMN_INDEX_DATA_KEY );
+ });
+ // fire the event from the table itself, passing the id and index of selected
+ var eventData = {},
+ key = $cell.parent().attr( 'id' ),
+ val = jQuery.makeArray( selectedColumnIndeces );
+ eventData[ key ] = val;
+ $cell.parents( '.peek' ).trigger( CHANGE_EVENT, eventData );
+ });
+ }
+ return $cell;
+ }
+
+ /** iterate over columns in peek and create a control for each */
+ function buildControlCells( count, control ){
+ var $cells = [];
+ // build a control for each column - using a build fn based on control
+ for( var columnIndex=0; columnIndex<count; columnIndex+=1 ){
+ $cells.push( control.multiselect? buildMultiSelectCell( control, columnIndex )
+ : buildSingleSelectCell( control, columnIndex ) );
+ }
+ return $cells;
+ }
+
+ /** build a row of controls for the peek */
+ function buildControlRow( cellCount, control, includePrompts ){
+ var $controlRow = $( '<tr/>' ).attr( 'id', control.id ).addClass( ROW_CLASS );
+ if( includePrompts ){
+ var $promptCell = $( '<td/>' ).addClass( PROMPT_CLASS ).text( control.label + ':' );
+ $controlRow.append( $promptCell );
+ }
+ $controlRow.append( buildControlCells( cellCount, control ) );
+ return $controlRow;
+ }
+
+ // ........................................................................
+ /** add to the peek, using options for configuration, return the peek */
+ function peekColumnSelector( options ){
+ options = jQuery.extend( true, {}, defaults, options );
+
+ var $peek = $( this ).addClass( PEEKCONTROL_CLASS ),
+ $peektable = $peek.find( 'table' ),
+ // get the size of the tables - width and height, number of comment rows
+ columnCount = $peektable.find( 'th' ).size(),
+ rowCount = $peektable.find( 'tr' ).size(),
+ // get the rows containing text starting with the comment char (also make them grey)
+ $commentRows = $peektable.find( 'td[colspan]' ).map( function( e, i ){
+ var $this = $( this );
+ if( $this.text() && $this.text().match( new RegExp( '^' + options.commentChar ) ) ){
+ return $( this ).css( 'color', 'grey' ).parent().get(0);
+ }
+ return null;
+ });
+
+ // should comment rows in the peek be hidden?
+ if( options.hideCommentRows ){
+ $commentRows.hide();
+ rowCount -= $commentRows.size();
+ }
+ //console.debug( 'rowCount:', rowCount, 'columnCount:', columnCount, '$commentRows:', $commentRows );
+
+ // should a first column of control prompts be added?
+ if( options.includePrompts ){
+ var $topLeft = $( '<th/>' ).addClass( 'top-left' ).text( options.topLeftContent )
+ .attr( 'rowspan', rowCount );
+ $peektable.find( 'tr' ).first().prepend( $topLeft );
+ }
+
+ // save either the options column name or the parsed text of each column header in html5 data attr and text
+ var $headers = $peektable.find( 'th:not(.top-left)' ).each( function( i, e ){
+ var $this = $( this ),
+ // can be '1.name' or '1'
+ text = $this.text().replace( /^\d+\.*/, '' ),
+ name = options.columnNames[ i ] || text;
+ $this.attr( 'data-' + COLUMN_NAME_DATA_KEY, name )
+ .text( ( i + 1 ) + (( name )?( '.' + name ):( '' )) );
+ });
+
+ // allow renaming of columns when the header is clicked
+ if( options.renameColumns ){
+ $headers.addClass( RENAMABLE_HEADER_CLASS )
+ .click( function renameColumn(){
+ // prompt for new name
+ var $this = $( this ),
+ index = $this.index() + ( options.includePrompts? 0: 1 ),
+ prevName = $this.data( COLUMN_NAME_DATA_KEY ),
+ newColumnName = prompt( 'New column name:', prevName );
+ if( newColumnName !== null && newColumnName !== prevName ){
+ // set the new text and data
+ $this.text( index + ( newColumnName?( '.' + newColumnName ):'' ) )
+ .data( COLUMN_NAME_DATA_KEY, newColumnName )
+ .attr( 'data-', COLUMN_NAME_DATA_KEY, newColumnName );
+ // fire event for new column names
+ var columnNames = jQuery.makeArray(
+ $this.parent().children( 'th:not(.top-left)' ).map( function(){
+ return $( this ).data( COLUMN_NAME_DATA_KEY );
+ }));
+ $this.parents( '.peek' ).trigger( RENAME_EVENT, columnNames );
+ }
+ });
+ }
+
+ // build a row for each control
+ options.controls.forEach( function( control, i ){
+ validateControl( control );
+ var $controlRow = buildControlRow( columnCount, control, options.includePrompts );
+ $peektable.find( 'tbody' ).append( $controlRow );
+ });
+ return this;
+ }
+
+ // ........................................................................
+ // as jq plugin
+ jQuery.fn.extend({
+ peekColumnSelector : function $peekColumnSelector( options ){
+ return this.map( function(){
+ return peekColumnSelector.call( this, options );
+ });
+ }
+ });
+}));
diff -r bfc3b5e52781683efc8024f18c4aa57c65af1e15 -r ee995df4a7e548befdfa09196b9665f05cfd55f6 client/galaxy/scripts/mvc/ui.js
--- a/client/galaxy/scripts/mvc/ui.js
+++ b/client/galaxy/scripts/mvc/ui.js
@@ -1344,311 +1344,3 @@
}
});
}());
-
-
-//==============================================================================
-/** Column selection using the peek display as the control.
- * Adds rows to the bottom of the peek with clickable areas in each cell
- * to allow the user to select columns.
- * Column selection can be limited to a single column or multiple.
- * (Optionally) adds a left hand column of column selection prompts.
- * (Optionally) allows the column headers to be clicked/renamed
- * and set to some initial value.
- * (Optionally) hides comment rows.
- * (Optionally) allows pre-selecting and disabling certain columns for
- * each row control.
- *
- * Construct by selecting a peek table to be used with jQuery and
- * calling 'peekControl' with options.
- * Options must include a 'controls' array and can include other options
- * listed below.
- * @example:
- * $( 'pre.peek' ).peekControl({
- * columnNames : ["Chromosome", "Start", "Base", "", "", "Qual" ],
- * controls : [
- * { label: 'X Column', id: 'xColumn' },
- * { label: 'Y Column', id: 'yColumn', selected: 2 },
- * { label: 'ID Column', id: 'idColumn', selected: 4, disabled: [ 1, 5 ] },
- * { label: 'Heatmap', id: 'heatmap', selected: [ 2, 4 ], disabled: [ 0, 1 ], multiselect: true,
- * selectedText: 'Included', unselectedText: 'Excluded' }
- * ],
- * renameColumns : true,
- * hideCommentRows : true,
- * includePrompts : true,
- * topLeftContent : 'Data sample:'
- * }).on( 'peek-control.change', function( ev, selection ){
- * console.info( 'new selection:', selection );
- * //{ yColumn: 2 }
- * }).on( 'peek-control.rename', function( ev, names ){
- * console.info( 'column names', names );
- * //[ 'Bler', 'Start', 'Base', '', '', 'Qual' ]
- * });
- *
- * An event is fired when column selection is changed and the event
- * is passed an object in the form: { the row id : the new selection value }.
- * An event is also fired when the table headers are re-named and
- * is passed the new array of column names.
- */
-(function(){
-
- /** option defaults */
- var defaults = {
- /** does this control allow renaming headers? */
- renameColumns : false,
- /** does this control allow renaming headers? */
- columnNames : [],
- /** the comment character used by the peek's datatype */
- commentChar : '#',
- /** should comment rows be shown or hidden in the peek */
- hideCommentRows : false,
- /** should a column of row control prompts be used */
- includePrompts : true,
- /** what is the content of the top left cell (often a title) */
- topLeftContent : 'Columns:'
- },
- /** the string of the event fired when a control row changes */
- CHANGE_EVENT = 'peek-control.change',
- /** the string of the event fired when a column is renamed */
- RENAME_EVENT = 'peek-control.rename',
- /** class added to the pre.peek element (to allow css on just the control) */
- PEEKCONTROL_CLASS = 'peek-control',
- /** class added to the control rows */
- ROW_CLASS = 'control',
- /** class added to the left-hand cells that serve as row prompts */
- PROMPT_CLASS = 'control-prompt',
- /** class added to selected _cells_/tds */
- SELECTED_CLASS = 'selected',
- /** class added to disabled/un-clickable cells/tds */
- DISABLED_CLASS = 'disabled',
- /** class added to the clickable surface within a cell to select it */
- BUTTON_CLASS = 'button',
- /** class added to peek table header (th) cells to indicate they can be clicked and are renamable */
- RENAMABLE_HEADER_CLASS = 'renamable-header',
- /** the data key used for each cell to store the column index ('data-...') */
- COLUMN_INDEX_DATA_KEY = 'column-index',
- /** renamable header data key used to store the column name (w/o the number and dot: '1.Bler') */
- COLUMN_NAME_DATA_KEY = 'column-name';
-
- //TODO: not happy with pure functional here - rows should polymorph (multi, single, etc.)
- //TODO: needs clean up, move handlers to outer scope
-
- // ........................................................................
- /** validate the control data sent in for each row */
- function validateControl( control ){
- if( control.disabled && jQuery.type( control.disabled ) !== 'array' ){
- throw new Error( '"disabled" must be defined as an array of indeces: ' + JSON.stringify( control ) );
- }
- if( control.multiselect && control.selected && jQuery.type( control.selected ) !== 'array' ){
- throw new Error( 'Mulitselect rows need an array for "selected": ' + JSON.stringify( control ) );
- }
- if( !control.label || !control.id ){
- throw new Error( 'Peek controls need a label and id for each control row: ' + JSON.stringify( control ) );
- }
- if( control.disabled && control.disabled.indexOf( control.selected ) !== -1 ){
- throw new Error( 'Selected column is in the list of disabled columns: ' + JSON.stringify( control ) );
- }
- return control;
- }
-
- /** build the inner control surface (i.e. button-like) */
- function buildButton( control, columnIndex ){
- return $( '<div/>' ).addClass( BUTTON_CLASS ).text( control.label );
- }
-
- /** build the basic (shared) cell structure */
- function buildControlCell( control, columnIndex ){
- var $td = $( '<td/>' )
- .html( buildButton( control, columnIndex ) )
- .attr( 'data-' + COLUMN_INDEX_DATA_KEY, columnIndex );
-
- // disable if index in disabled array
- if( control.disabled && control.disabled.indexOf( columnIndex ) !== -1 ){
- $td.addClass( DISABLED_CLASS );
- }
- return $td;
- }
-
- /** set the text of the control based on selected/un */
- function setSelectedText( $cell, control, columnIndex ){
- var $button = $cell.children( '.' + BUTTON_CLASS );
- if( $cell.hasClass( SELECTED_CLASS ) ){
- $button.html( ( control.selectedText !== undefined )?( control.selectedText ):( control.label ) );
- } else {
- $button.html( ( control.unselectedText !== undefined )?( control.unselectedText ):( control.label ) );
- }
- }
-
- /** build a cell for a row that only allows one selection */
- function buildSingleSelectCell( control, columnIndex ){
- // only one selection - selected is single index
- var $cell = buildControlCell( control, columnIndex );
- if( control.selected === columnIndex ){
- $cell.addClass( SELECTED_CLASS );
- }
- setSelectedText( $cell, control, columnIndex );
-
- // only add the handler to non-disabled controls
- if( !$cell.hasClass( DISABLED_CLASS ) ){
- $cell.click( function selectClick( ev ){
- var $cell = $( this );
- // don't re-select or fire event if already selected
- if( !$cell.hasClass( SELECTED_CLASS ) ){
- // only one can be selected - remove selected on all others, add it here
- var $otherSelected = $cell.parent().children( '.' + SELECTED_CLASS ).removeClass( SELECTED_CLASS );
- $otherSelected.each( function(){
- setSelectedText( $( this ), control, columnIndex );
- });
-
- $cell.addClass( SELECTED_CLASS );
- setSelectedText( $cell, control, columnIndex );
-
- // fire the event from the table itself, passing the id and index of selected
- var eventData = {},
- key = $cell.parent().attr( 'id' ),
- val = $cell.data( COLUMN_INDEX_DATA_KEY );
- eventData[ key ] = val;
- $cell.parents( '.peek' ).trigger( CHANGE_EVENT, eventData );
- }
- });
- }
- return $cell;
- }
-
- /** build a cell for a row that allows multiple selections */
- function buildMultiSelectCell( control, columnIndex ){
- var $cell = buildControlCell( control, columnIndex );
- // multiple selection - selected is an array
- if( control.selected && control.selected.indexOf( columnIndex ) !== -1 ){
- $cell.addClass( SELECTED_CLASS );
- }
- setSelectedText( $cell, control, columnIndex );
-
- // only add the handler to non-disabled controls
- if( !$cell.hasClass( DISABLED_CLASS ) ){
- $cell.click( function multiselectClick( ev ){
- var $cell = $( this );
- // can be more than one selected - toggle selected on this cell
- $cell.toggleClass( SELECTED_CLASS );
- setSelectedText( $cell, control, columnIndex );
- var selectedColumnIndeces = $cell.parent().find( '.' + SELECTED_CLASS ).map( function( i, e ){
- return $( e ).data( COLUMN_INDEX_DATA_KEY );
- });
- // fire the event from the table itself, passing the id and index of selected
- var eventData = {},
- key = $cell.parent().attr( 'id' ),
- val = jQuery.makeArray( selectedColumnIndeces );
- eventData[ key ] = val;
- $cell.parents( '.peek' ).trigger( CHANGE_EVENT, eventData );
- });
- }
- return $cell;
- }
-
- /** iterate over columns in peek and create a control for each */
- function buildControlCells( count, control ){
- var $cells = [];
- // build a control for each column - using a build fn based on control
- for( var columnIndex=0; columnIndex<count; columnIndex+=1 ){
- $cells.push( control.multiselect? buildMultiSelectCell( control, columnIndex )
- : buildSingleSelectCell( control, columnIndex ) );
- }
- return $cells;
- }
-
- /** build a row of controls for the peek */
- function buildControlRow( cellCount, control, includePrompts ){
- var $controlRow = $( '<tr/>' ).attr( 'id', control.id ).addClass( ROW_CLASS );
- if( includePrompts ){
- var $promptCell = $( '<td/>' ).addClass( PROMPT_CLASS ).text( control.label + ':' );
- $controlRow.append( $promptCell );
- }
- $controlRow.append( buildControlCells( cellCount, control ) );
- return $controlRow;
- }
-
- // ........................................................................
- /** add to the peek, using options for configuration, return the peek */
- function peekControl( options ){
- options = jQuery.extend( true, {}, defaults, options );
-
- var $peek = $( this ).addClass( PEEKCONTROL_CLASS ),
- $peektable = $peek.find( 'table' ),
- // get the size of the tables - width and height, number of comment rows
- columnCount = $peektable.find( 'th' ).size(),
- rowCount = $peektable.find( 'tr' ).size(),
- // get the rows containing text starting with the comment char (also make them grey)
- $commentRows = $peektable.find( 'td[colspan]' ).map( function( e, i ){
- var $this = $( this );
- if( $this.text() && $this.text().match( new RegExp( '^' + options.commentChar ) ) ){
- return $( this ).css( 'color', 'grey' ).parent().get(0);
- }
- return null;
- });
-
- // should comment rows in the peek be hidden?
- if( options.hideCommentRows ){
- $commentRows.hide();
- rowCount -= $commentRows.size();
- }
- //console.debug( 'rowCount:', rowCount, 'columnCount:', columnCount, '$commentRows:', $commentRows );
-
- // should a first column of control prompts be added?
- if( options.includePrompts ){
- var $topLeft = $( '<th/>' ).addClass( 'top-left' ).text( options.topLeftContent )
- .attr( 'rowspan', rowCount );
- $peektable.find( 'tr' ).first().prepend( $topLeft );
- }
-
- // save either the options column name or the parsed text of each column header in html5 data attr and text
- var $headers = $peektable.find( 'th:not(.top-left)' ).each( function( i, e ){
- var $this = $( this ),
- // can be '1.name' or '1'
- text = $this.text().replace( /^\d+\.*/, '' ),
- name = options.columnNames[ i ] || text;
- $this.attr( 'data-' + COLUMN_NAME_DATA_KEY, name )
- .text( ( i + 1 ) + (( name )?( '.' + name ):( '' )) );
- });
-
- // allow renaming of columns when the header is clicked
- if( options.renameColumns ){
- $headers.addClass( RENAMABLE_HEADER_CLASS )
- .click( function renameColumn(){
- // prompt for new name
- var $this = $( this ),
- index = $this.index() + ( options.includePrompts? 0: 1 ),
- prevName = $this.data( COLUMN_NAME_DATA_KEY ),
- newColumnName = prompt( 'New column name:', prevName );
- if( newColumnName !== null && newColumnName !== prevName ){
- // set the new text and data
- $this.text( index + ( newColumnName?( '.' + newColumnName ):'' ) )
- .data( COLUMN_NAME_DATA_KEY, newColumnName )
- .attr( 'data-', COLUMN_NAME_DATA_KEY, newColumnName );
- // fire event for new column names
- var columnNames = jQuery.makeArray(
- $this.parent().children( 'th:not(.top-left)' ).map( function(){
- return $( this ).data( COLUMN_NAME_DATA_KEY );
- }));
- $this.parents( '.peek' ).trigger( RENAME_EVENT, columnNames );
- }
- });
- }
-
- // build a row for each control
- options.controls.forEach( function( control, i ){
- validateControl( control );
- var $controlRow = buildControlRow( columnCount, control, options.includePrompts );
- $peektable.find( 'tbody' ).append( $controlRow );
- });
- return this;
- }
-
- // ........................................................................
- // as jq plugin
- jQuery.fn.extend({
- peekControl : function $peekControl( options ){
- return this.map( function(){
- return peekControl.call( this, options );
- });
- }
- });
-}());
diff -r bfc3b5e52781683efc8024f18c4aa57c65af1e15 -r ee995df4a7e548befdfa09196b9665f05cfd55f6 config/plugins/visualizations/scatterplot/src/scatterplot-config-editor.js
--- a/config/plugins/visualizations/scatterplot/src/scatterplot-config-editor.js
+++ b/config/plugins/visualizations/scatterplot/src/scatterplot-config-editor.js
@@ -120,7 +120,7 @@
peek : this.dataset.peek
}));
- $dataControl.find( '.peek' ).peekControl({
+ $dataControl.find( '.peek' ).peekColumnSelector({
controls : [
{ label: 'X Column', id: 'xColumn', selected: config.xColumn, disabled: columnTypes.text },
{ label: 'Y Column', id: 'yColumn', selected: config.yColumn, disabled: columnTypes.text },
@@ -128,11 +128,11 @@
]
//renameColumns : true
- }).on( 'peek-control.change', function( ev, data ){
+ }).on( 'peek-column-selector.change', function( ev, data ){
//console.info( 'new selection:', data );
editor.model.set( 'config', data );
- }).on( 'peek-control.rename', function( ev, data ){
+ }).on( 'peek-column-selector.rename', function( ev, data ){
//console.info( 'new column names', data );
});
diff -r bfc3b5e52781683efc8024f18c4aa57c65af1e15 -r ee995df4a7e548befdfa09196b9665f05cfd55f6 config/plugins/visualizations/scatterplot/static/scatterplot-edit.js
--- a/config/plugins/visualizations/scatterplot/static/scatterplot-edit.js
+++ b/config/plugins/visualizations/scatterplot/static/scatterplot-edit.js
@@ -1,1 +1,1 @@
-function scatterplot(a,b,c){function d(){var a={v:{},h:{}};return a.v.lines=p.selectAll("line.v-grid-line").data(m.x.ticks(q.x.fn.ticks()[0])),a.v.lines.enter().append("svg:line").classed("grid-line v-grid-line",!0),a.v.lines.attr("x1",m.x).attr("x2",m.x).attr("y1",0).attr("y2",b.height),a.v.lines.exit().remove(),a.h.lines=p.selectAll("line.h-grid-line").data(m.y.ticks(q.y.fn.ticks()[0])),a.h.lines.enter().append("svg:line").classed("grid-line h-grid-line",!0),a.h.lines.attr("x1",0).attr("x2",b.width).attr("y1",m.y).attr("y2",m.y),a.h.lines.exit().remove(),a}function e(){return t.attr("cx",function(a,b){return m.x(j(a,b))}).attr("cy",function(a,b){return m.y(k(a,b))}).style("display","block").filter(function(){var a=d3.select(this).attr("cx"),c=d3.select(this).attr("cy");return 0>a||a>b.width?!0:0>c||c>b.height?!0:!1}).style("display","none")}function f(){$(".chart-info-box").remove(),q.redraw(),e(),s=d(),$(o.node()).trigger("zoom.scatterplot",{scale:n.scale(),translate:n.translate()})}function g(a,c,d){return c+=8,$(['<div class="chart-info-box" style="position: absolute">',void 0!==b.idColumn?"<div>"+d[b.idColumn]+"</div>":"","<div>",j(d),"</div>","<div>",k(d),"</div>","</div>"].join("")).css({top:a,left:c,"z-index":2})}var h=function(a,b){return"translate("+a+","+b+")"},i=function(a,b,c){return"rotate("+a+","+b+","+c+")"},j=function(a){return a[b.xColumn]},k=function(a){return a[b.yColumn]},l={x:{extent:d3.extent(c,j)},y:{extent:d3.extent(c,k)}},m={x:d3.scale.linear().domain(l.x.extent).range([0,b.width]),y:d3.scale.linear().domain(l.y.extent).range([b.height,0])},n=d3.behavior.zoom().x(m.x).y(m.y).scaleExtent([1,30]).scale(b.scale||1).translate(b.translate||[0,0]),o=d3.select(a).attr("class","scatterplot").attr("width","100%").attr("height",b.height+(b.margin.top+b.margin.bottom)),p=o.append("g").attr("class","content").attr("transform",h(b.margin.left,b.margin.top)).call(n);p.append("rect").attr("class","zoom-rect").attr("width",b.width).attr("height",b.height).style("fill","transparent");var q={x:{},y:{}};q.x.fn=d3.svg.axis().orient("bottom").scale(m.x).ticks(b.xTicks).tickFormat(d3.format("s")),q.y.fn=d3.svg.axis().orient("left").scale(m.y).ticks(b.yTicks).tickFormat(d3.format("s")),q.x.g=p.append("g").attr("class","x axis").attr("transform",h(0,b.height)).call(q.x.fn),q.y.g=p.append("g").attr("class","y axis").call(q.y.fn);var r=6;q.x.label=o.append("text").attr("id","x-axis-label").attr("class","axis-label").text(b.xLabel).attr("text-anchor","middle").attr("dominant-baseline","text-after-edge").attr("x",b.width/2+b.margin.left).attr("y",b.height+b.margin.bottom+b.margin.top-r),q.y.label=o.append("text").attr("id","y-axis-label").attr("class","axis-label").text(b.yLabel).attr("text-anchor","middle").attr("dominant-baseline","text-before-edge").attr("x",r).attr("y",b.height/2).attr("transform",i(-90,r,b.height/2)),q.redraw=function(){o.select(".x.axis").call(q.x.fn),o.select(".y.axis").call(q.y.fn)};var s=d(),t=p.selectAll(".glyph").data(c).enter().append("svg:circle").classed("glyph",!0).attr("cx",function(a,b){return m.x(j(a,b))}).attr("cy",function(a,b){return m.y(k(a,b))}).attr("r",0);t.transition().duration(b.animDuration).attr("r",b.datapointSize),e(),n.on("zoom",f),t.on("mouseover",function(a,c){var d=d3.select(this);d.classed("highlight",!0).style("fill","red").style("fill-opacity",1),p.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",d.attr("cx")-b.datapointSize).attr("y1",d.attr("cy")).attr("x2",0).attr("y2",d.attr("cy")).classed("hoverline",!0),d.attr("cy")<b.height&&p.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",d.attr("cx")).attr("y1",+d.attr("cy")+b.datapointSize).attr("x2",d.attr("cx")).attr("y2",b.height).classed("hoverline",!0);var e=this.getBoundingClientRect();$("body").append(g(e.top,e.right,a)),$(o.node()).trigger("mouseover-datapoint.scatterplot",[this,a,c])}),t.on("mouseout",function(){d3.select(this).classed("highlight",!1).style("fill","black").style("fill-opacity",.2),p.selectAll(".hoverline").remove(),$(".chart-info-box").remove()})}this.scatterplot=this.scatterplot||{},this.scatterplot.chartcontrol=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f,g="",h="function",i=this.escapeExpression;return g+='<p class="help-text">\n Use the following controls to how the chart is displayed.\n The slide controls can be moved by the mouse or, if the \'handle\' is in focus, your keyboard\'s arrow keys.\n Move the focus between controls by using the tab or shift+tab keys on your keyboard.\n Use the \'Draw\' button to render (or re-render) the chart with the current settings.\n</p>\n\n<div data-config-key="datapointSize" class="form-input numeric-slider-input">\n <label for="datapointSize">Size of data point: </label>\n <div class="slider-output">',(f=c.datapointSize)?f=f.call(b,{hash:{},data:e}):(f=b.datapointSize,f=typeof f===h?f.apply(b):f),g+=i(f)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n Size of the graphic representation of each data point\n </p>\n</div>\n\n<div data-config-key="width" class="form-input numeric-slider-input">\n <label for="width">Chart width: </label>\n <div class="slider-output">',(f=c.width)?f=f.call(b,{hash:{},data:e}):(f=b.width,f=typeof f===h?f.apply(b):f),g+=i(f)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including chart margins and axes)\n </p>\n</div>\n\n<div data-config-key="height" class="form-input numeric-slider-input">\n <label for="height">Chart height: </label>\n <div class="slider-output">',(f=c.height)?f=f.call(b,{hash:{},data:e}):(f=b.height,f=typeof f===h?f.apply(b):f),g+=i(f)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including chart margins and axes)\n </p>\n</div>\n\n<div data-config-key="X-axis-label"class="text-input form-input">\n <label for="X-axis-label">Re-label the X axis: </label>\n <input type="text" name="X-axis-label" id="X-axis-label" value="',(f=c.xLabel)?f=f.call(b,{hash:{},data:e}):(f=b.xLabel,f=typeof f===h?f.apply(b):f),g+=i(f)+'" />\n <p class="form-help help-text-small"></p>\n</div>\n\n<div data-config-key="Y-axis-label" class="text-input form-input">\n <label for="Y-axis-label">Re-label the Y axis: </label>\n <input type="text" name="Y-axis-label" id="Y-axis-label" value="',(f=c.yLabel)?f=f.call(b,{hash:{},data:e}):(f=b.yLabel,f=typeof f===h?f.apply(b):f),g+=i(f)+'" />\n <p class="form-help help-text-small"></p>\n</div>\n\n<button class="render-button btn btn-primary active">Draw</button>\n'}),this.scatterplot.datacontrol=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f,g="",h="function";return g+='<p class="help-text">\n Use the following control to change which columns are used by the chart. Click any cell\n from the last three rows of the table to select the column for the appropriate data.\n Use the \'Draw\' button to render (or re-render) the chart with the current settings.\n</p>\n\n<ul class="help-text" style="margin-left: 8px">\n <li><b>X Column</b>: which column values will be used for the x axis of the chart.</li>\n <li><b>Y Column</b>: which column values will be used for the y axis of the chart.</li>\n <li><b>ID Column</b>: an additional column value displayed when the user hovers over a data point.\n It may be useful to select unique or categorical identifiers here (such as gene ids).\n </li>\n</ul>\n\n<div class="column-selection">\n <pre class="peek">',(f=c.peek)?f=f.call(b,{hash:{},data:e}):(f=b.peek,f=typeof f===h?f.apply(b):f),(f||0===f)&&(g+=f),g+='</pre>\n</div>\n\n<p class="help-text help-text-small">\n <b>Note</b>: If it can be determined from the dataset\'s filetype that a column is not numeric,\n that column choice may be disabled for either the x or y axis.\n</p>\n\n<button class="render-button btn btn-primary active">Draw</button>\n'}),this.scatterplot.editor=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f="";return f+='<div class="scatterplot-editor tabbable tabs-left">\n \n <ul class="nav nav-tabs">\n \n <li class="active">\n <a title="Use this tab to change which data are used"\n href="#data-control" data-toggle="tab">Data Controls</a>\n </li>\n <li>\n <a title="Use this tab to change how the chart is drawn"\n href="#chart-control" data-toggle="tab" >Chart Controls</a>\n </li>\n \n <li class="disabled">\n <a title="This tab will display the chart"\n href="#chart-display" data-toggle="tab">Chart</a>\n </li>\n \n <li class="file-controls">\n<!-- <button class="copy-btn btn btn-default"\n title="Save this as a new visualization">Save to new</button>-->\n <button class="save-btn btn btn-default">Save</button>\n </li>\n </ul>\n\n \n <div class="tab-content">\n \n <div id="data-control" class="scatterplot-config-control tab-pane active">\n \n </div>\n \n \n <div id="chart-control" class="scatterplot-config-control tab-pane">\n \n </div>\n\n \n <div id="chart-display" class="scatterplot-display tab-pane"></div>\n\n </div>\n</div>\n'});var ScatterplotConfigEditor=Backbone.View.extend({className:"scatterplot-control-form",initialize:function(a){if(this.model||(this.model=new Visualization({type:"scatterplot"})),!a||!a.dataset)throw new Error("ScatterplotConfigEditor requires a dataset");this.dataset=a.dataset,this.display=new ScatterplotDisplay({dataset:a.dataset,model:this.model})},render:function(){this.$el.empty().append(ScatterplotConfigEditor.templates.mainLayout({})),this.model.id&&(this.$el.find(".copy-btn").show(),this.$el.find(".save-btn").text("Update saved")),this.$el.find("[title]").tooltip(),this._render_dataControl(),this._render_chartControls(),this._render_chartDisplay();var a=this.model.get("config");return this.model.id&&_.isFinite(a.xColumn)&&_.isFinite(a.yColumn)&&this.renderChart(),this},_getColumnIndecesByType:function(){var a={numeric:[],text:[],all:[]};return _.each(this.dataset.metadata_column_types||[],function(b,c){"int"===b||"float"===b?a.numeric.push(c):("str"===b||"list"===b)&&a.text.push(c),a.all.push(c)}),a.numeric.length<2&&(a.numeric=[]),a},_render_dataControl:function(a){a=a||this.$el;var b=this,c=this.model.get("config"),d=this._getColumnIndecesByType(),e=a.find(".tab-pane#data-control");return e.html(ScatterplotConfigEditor.templates.dataControl({peek:this.dataset.peek})),e.find(".peek").peekControl({controls:[{label:"X Column",id:"xColumn",selected:c.xColumn,disabled:d.text},{label:"Y Column",id:"yColumn",selected:c.yColumn,disabled:d.text},{label:"ID Column",id:"idColumn",selected:c.idColumn}]}).on("peek-control.change",function(a,c){b.model.set("config",c)}).on("peek-control.rename",function(){}),e.find("[title]").tooltip(),e},_render_chartControls:function(a){function b(){var a=$(this),b=a.slider("value");c.model.set("config",_.object([[a.parent().data("config-key"),b]])),a.siblings(".slider-output").text(b)}a=a||this.$el;var c=this,d=this.model.get("config"),e=a.find("#chart-control");e.html(ScatterplotConfigEditor.templates.chartControl(d));var f={datapointSize:{min:2,max:10,step:1},width:{min:200,max:800,step:20},height:{min:200,max:800,step:20}};e.find(".numeric-slider-input").each(function(){var a=$(this),c=a.attr("data-config-key"),e=_.extend(f[c],{value:d[c],change:b,slide:b});a.find(".slider").slider(e),a.children(".slider-output").text(d[c])});var g=this.dataset.metadata_column_names||[],h=d.xLabel||g[d.xColumn]||"X",i=d.yLabel||g[d.yColumn]||"Y";return e.find('input[name="X-axis-label"]').val(h).on("change",function(){c.model.set("config",{xLabel:$(this).val()})}),e.find('input[name="Y-axis-label"]').val(i).on("change",function(){c.model.set("config",{yLabel:$(this).val()})}),e.find("[title]").tooltip(),e},_render_chartDisplay:function(a){a=a||this.$el;var b=a.find(".tab-pane#chart-display");return this.display.setElement(b),this.display.render(),b.find("[title]").tooltip(),b},events:{"change #include-id-checkbox":"toggleThirdColumnSelector","click #data-control .render-button":"renderChart","click #chart-control .render-button":"renderChart","click .save-btn":"saveVisualization"},saveVisualization:function(){var a=this;this.model.save().fail(function(b,c,d){console.error(b,c,d),a.trigger("save:error",view),alert("Error loading data:\n"+b.responseText)}).then(function(){a.display.render()})},toggleThirdColumnSelector:function(){this.$el.find('select[name="idColumn"]').parent().toggle()},renderChart:function(){this.$el.find(".nav li.disabled").removeClass("disabled"),this.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show"),this.display.fetchData()},toString:function(){return"ScatterplotConfigEditor("+(this.dataset?this.dataset.id:"")+")"}});ScatterplotConfigEditor.templates={mainLayout:scatterplot.editor,dataControl:scatterplot.datacontrol,chartControl:scatterplot.chartcontrol};var ScatterplotDisplay=Backbone.View.extend({initialize:function(a){this.data=null,this.dataset=a.dataset,this.lineCount=this.dataset.metadata_data_lines||null},fetchData:function(){this.showLoadingIndicator();var a=this,b=this.model.get("config"),c=jQuery.getJSON("/api/datasets/"+this.dataset.id,{data_type:"raw_data",provider:"dataset-column",limit:b.pagination.perPage,offset:b.pagination.currPage*b.pagination.perPage});return c.done(function(b){a.data=b.data,a.trigger("data:fetched",a),a.renderData()}),c.fail(function(b,c,d){console.error(b,c,d),a.trigger("data:error",a),alert("Error loading data:\n"+b.responseText)}),c},showLoadingIndicator:function(){this.$el.find(".scatterplot-data-info").html(['<div class="loading-indicator">','<span class="fa fa-spinner fa-spin"></span>','<span class="loading-indicator-message">loading...</span>',"</div>"].join(""))},template:function(){var a=['<div class="controls clear">','<div class="right">','<p class="scatterplot-data-info"></p>','<button class="stats-toggle-btn">Stats</button>','<button class="rerender-btn">Redraw</button>',"</div>",'<div class="left">','<div class="page-control"></div>',"</div>","</div>","<svg/>",'<div class="stats-display"></div>'].join("");return a},render:function(){return this.$el.addClass("scatterplot-display").html(this.template()),this.data&&this.renderData(),this},renderData:function(){this.renderLeftControls(),this.renderRightControls(),this.renderPlot(this.data),this.getStats()},renderLeftControls:function(){var a=this,b=this.model.get("config");return this.$el.find(".controls .left .page-control").pagination({startingPage:b.pagination.currPage,perPage:b.pagination.perPage,totalDataSize:this.lineCount,currDataSize:this.data.length}).off().on("pagination.page-change",function(c,d){b.pagination.currPage=d,a.model.set("config",{pagination:b.pagination}),a.resetZoom(),a.fetchData()}),this},renderRightControls:function(){var a=this;this.setLineInfo(this.data),this.$el.find(".stats-toggle-btn").off().click(function(){a.toggleStats()}),this.$el.find(".rerender-btn").off().click(function(){a.resetZoom(),a.renderPlot(this.data)})},renderPlot:function(){var a=this,b=this.$el.find("svg");this.toggleStats(!1),b.off().empty().show().on("zoom.scatterplot",function(b,c){a.model.set("config",c)}),scatterplot(b.get(0),this.model.get("config"),this.data)},setLineInfo:function(a,b){if(a){var c=this.model.get("config"),d=this.lineCount||"an unknown total",e=c.pagination.currPage*c.pagination.perPage,f=e+a.length;this.$el.find(".controls p.scatterplot-data-info").text([e+1,"to",f,"of",d].join(" "))}else this.$el.find(".controls p.scatterplot-data-info").html(b||"");return this},resetZoom:function(a,b){return a=void 0!==a?a:1,b=void 0!==b?b:[0,0],this.model.set("config",{scale:a,translate:b}),this},getStats:function(){if(this.data){var a=this,b=this.model.get("config"),c=new Worker("/plugins/visualizations/scatterplot/static/worker-stats.js");c.postMessage({data:this.data,keys:[b.xColumn,b.yColumn]}),c.onerror=function(){c.terminate()},c.onmessage=function(b){a.renderStats(b.data)}}},renderStats:function(a){var b=this.model.get("config"),c=this.$el.find(".stats-display"),d=b.xLabel,e=b.yLabel,f=$("<table/>").addClass("table").append(["<thead><th></th><th>",d,"</th><th>",e,"</th></thead>"].join("")).append(_.map(a,function(a,b){return $(["<tr><td>",b,"</td><td>",a[0],"</td><td>",a[1],"</td></tr>"].join(""))}));c.empty().append(f)},toggleStats:function(a){var b=this.$el.find(".stats-display");a=void 0===a?b.is(":hidden"):a,a?(this.$el.find("svg").hide(),b.show(),this.$el.find(".controls .stats-toggle-btn").text("Plot")):(b.hide(),this.$el.find("svg").show(),this.$el.find(".controls .stats-toggle-btn").text("Stats"))},toString:function(){return"ScatterplotView()"}}),ScatterplotModel=Visualization.extend({defaults:{type:"scatterplot",config:{pagination:{currPage:0,perPage:3e3},width:400,height:400,margin:{top:16,right:16,bottom:40,left:54},xTicks:10,xLabel:"X",yTicks:10,yLabel:"Y",datapointSize:4,animDuration:500,scale:1,translate:[0,0]}}});
\ No newline at end of file
+function scatterplot(a,b,c){function d(){var a={v:{},h:{}};return a.v.lines=p.selectAll("line.v-grid-line").data(m.x.ticks(q.x.fn.ticks()[0])),a.v.lines.enter().append("svg:line").classed("grid-line v-grid-line",!0),a.v.lines.attr("x1",m.x).attr("x2",m.x).attr("y1",0).attr("y2",b.height),a.v.lines.exit().remove(),a.h.lines=p.selectAll("line.h-grid-line").data(m.y.ticks(q.y.fn.ticks()[0])),a.h.lines.enter().append("svg:line").classed("grid-line h-grid-line",!0),a.h.lines.attr("x1",0).attr("x2",b.width).attr("y1",m.y).attr("y2",m.y),a.h.lines.exit().remove(),a}function e(){return t.attr("cx",function(a,b){return m.x(j(a,b))}).attr("cy",function(a,b){return m.y(k(a,b))}).style("display","block").filter(function(){var a=d3.select(this).attr("cx"),c=d3.select(this).attr("cy");return 0>a||a>b.width?!0:0>c||c>b.height?!0:!1}).style("display","none")}function f(){$(".chart-info-box").remove(),q.redraw(),e(),s=d(),$(o.node()).trigger("zoom.scatterplot",{scale:n.scale(),translate:n.translate()})}function g(a,c,d){return c+=8,$(['<div class="chart-info-box" style="position: absolute">',void 0!==b.idColumn?"<div>"+d[b.idColumn]+"</div>":"","<div>",j(d),"</div>","<div>",k(d),"</div>","</div>"].join("")).css({top:a,left:c,"z-index":2})}var h=function(a,b){return"translate("+a+","+b+")"},i=function(a,b,c){return"rotate("+a+","+b+","+c+")"},j=function(a){return a[b.xColumn]},k=function(a){return a[b.yColumn]},l={x:{extent:d3.extent(c,j)},y:{extent:d3.extent(c,k)}},m={x:d3.scale.linear().domain(l.x.extent).range([0,b.width]),y:d3.scale.linear().domain(l.y.extent).range([b.height,0])},n=d3.behavior.zoom().x(m.x).y(m.y).scaleExtent([1,30]).scale(b.scale||1).translate(b.translate||[0,0]),o=d3.select(a).attr("class","scatterplot").attr("width","100%").attr("height",b.height+(b.margin.top+b.margin.bottom)),p=o.append("g").attr("class","content").attr("transform",h(b.margin.left,b.margin.top)).call(n);p.append("rect").attr("class","zoom-rect").attr("width",b.width).attr("height",b.height).style("fill","transparent");var q={x:{},y:{}};q.x.fn=d3.svg.axis().orient("bottom").scale(m.x).ticks(b.xTicks).tickFormat(d3.format("s")),q.y.fn=d3.svg.axis().orient("left").scale(m.y).ticks(b.yTicks).tickFormat(d3.format("s")),q.x.g=p.append("g").attr("class","x axis").attr("transform",h(0,b.height)).call(q.x.fn),q.y.g=p.append("g").attr("class","y axis").call(q.y.fn);var r=6;q.x.label=o.append("text").attr("id","x-axis-label").attr("class","axis-label").text(b.xLabel).attr("text-anchor","middle").attr("dominant-baseline","text-after-edge").attr("x",b.width/2+b.margin.left).attr("y",b.height+b.margin.bottom+b.margin.top-r),q.y.label=o.append("text").attr("id","y-axis-label").attr("class","axis-label").text(b.yLabel).attr("text-anchor","middle").attr("dominant-baseline","text-before-edge").attr("x",r).attr("y",b.height/2).attr("transform",i(-90,r,b.height/2)),q.redraw=function(){o.select(".x.axis").call(q.x.fn),o.select(".y.axis").call(q.y.fn)};var s=d(),t=p.selectAll(".glyph").data(c).enter().append("svg:circle").classed("glyph",!0).attr("cx",function(a,b){return m.x(j(a,b))}).attr("cy",function(a,b){return m.y(k(a,b))}).attr("r",0);t.transition().duration(b.animDuration).attr("r",b.datapointSize),e(),n.on("zoom",f),t.on("mouseover",function(a,c){var d=d3.select(this);d.classed("highlight",!0).style("fill","red").style("fill-opacity",1),p.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",d.attr("cx")-b.datapointSize).attr("y1",d.attr("cy")).attr("x2",0).attr("y2",d.attr("cy")).classed("hoverline",!0),d.attr("cy")<b.height&&p.append("line").attr("stroke","red").attr("stroke-width",1).attr("x1",d.attr("cx")).attr("y1",+d.attr("cy")+b.datapointSize).attr("x2",d.attr("cx")).attr("y2",b.height).classed("hoverline",!0);var e=this.getBoundingClientRect();$("body").append(g(e.top,e.right,a)),$(o.node()).trigger("mouseover-datapoint.scatterplot",[this,a,c])}),t.on("mouseout",function(){d3.select(this).classed("highlight",!1).style("fill","black").style("fill-opacity",.2),p.selectAll(".hoverline").remove(),$(".chart-info-box").remove()})}this.scatterplot=this.scatterplot||{},this.scatterplot.chartcontrol=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f,g="",h="function",i=this.escapeExpression;return g+='<p class="help-text">\n Use the following controls to how the chart is displayed.\n The slide controls can be moved by the mouse or, if the \'handle\' is in focus, your keyboard\'s arrow keys.\n Move the focus between controls by using the tab or shift+tab keys on your keyboard.\n Use the \'Draw\' button to render (or re-render) the chart with the current settings.\n</p>\n\n<div data-config-key="datapointSize" class="form-input numeric-slider-input">\n <label for="datapointSize">Size of data point: </label>\n <div class="slider-output">',(f=c.datapointSize)?f=f.call(b,{hash:{},data:e}):(f=b.datapointSize,f=typeof f===h?f.apply(b):f),g+=i(f)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n Size of the graphic representation of each data point\n </p>\n</div>\n\n<div data-config-key="width" class="form-input numeric-slider-input">\n <label for="width">Chart width: </label>\n <div class="slider-output">',(f=c.width)?f=f.call(b,{hash:{},data:e}):(f=b.width,f=typeof f===h?f.apply(b):f),g+=i(f)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including chart margins and axes)\n </p>\n</div>\n\n<div data-config-key="height" class="form-input numeric-slider-input">\n <label for="height">Chart height: </label>\n <div class="slider-output">',(f=c.height)?f=f.call(b,{hash:{},data:e}):(f=b.height,f=typeof f===h?f.apply(b):f),g+=i(f)+'</div>\n <div class="slider"></div>\n <p class="form-help help-text-small">\n (not including chart margins and axes)\n </p>\n</div>\n\n<div data-config-key="X-axis-label"class="text-input form-input">\n <label for="X-axis-label">Re-label the X axis: </label>\n <input type="text" name="X-axis-label" id="X-axis-label" value="',(f=c.xLabel)?f=f.call(b,{hash:{},data:e}):(f=b.xLabel,f=typeof f===h?f.apply(b):f),g+=i(f)+'" />\n <p class="form-help help-text-small"></p>\n</div>\n\n<div data-config-key="Y-axis-label" class="text-input form-input">\n <label for="Y-axis-label">Re-label the Y axis: </label>\n <input type="text" name="Y-axis-label" id="Y-axis-label" value="',(f=c.yLabel)?f=f.call(b,{hash:{},data:e}):(f=b.yLabel,f=typeof f===h?f.apply(b):f),g+=i(f)+'" />\n <p class="form-help help-text-small"></p>\n</div>\n\n<button class="render-button btn btn-primary active">Draw</button>\n'}),this.scatterplot.datacontrol=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f,g="",h="function";return g+='<p class="help-text">\n Use the following control to change which columns are used by the chart. Click any cell\n from the last three rows of the table to select the column for the appropriate data.\n Use the \'Draw\' button to render (or re-render) the chart with the current settings.\n</p>\n\n<ul class="help-text" style="margin-left: 8px">\n <li><b>X Column</b>: which column values will be used for the x axis of the chart.</li>\n <li><b>Y Column</b>: which column values will be used for the y axis of the chart.</li>\n <li><b>ID Column</b>: an additional column value displayed when the user hovers over a data point.\n It may be useful to select unique or categorical identifiers here (such as gene ids).\n </li>\n</ul>\n\n<div class="column-selection">\n <pre class="peek">',(f=c.peek)?f=f.call(b,{hash:{},data:e}):(f=b.peek,f=typeof f===h?f.apply(b):f),(f||0===f)&&(g+=f),g+='</pre>\n</div>\n\n<p class="help-text help-text-small">\n <b>Note</b>: If it can be determined from the dataset\'s filetype that a column is not numeric,\n that column choice may be disabled for either the x or y axis.\n</p>\n\n<button class="render-button btn btn-primary active">Draw</button>\n'}),this.scatterplot.editor=Handlebars.template(function(a,b,c,d,e){this.compilerInfo=[4,">= 1.0.0"],c=this.merge(c,a.helpers),e=e||{};var f="";return f+='<div class="scatterplot-editor tabbable tabs-left">\n \n <ul class="nav nav-tabs">\n \n <li class="active">\n <a title="Use this tab to change which data are used"\n href="#data-control" data-toggle="tab">Data Controls</a>\n </li>\n <li>\n <a title="Use this tab to change how the chart is drawn"\n href="#chart-control" data-toggle="tab" >Chart Controls</a>\n </li>\n \n <li class="disabled">\n <a title="This tab will display the chart"\n href="#chart-display" data-toggle="tab">Chart</a>\n </li>\n \n <li class="file-controls">\n<!-- <button class="copy-btn btn btn-default"\n title="Save this as a new visualization">Save to new</button>-->\n <button class="save-btn btn btn-default">Save</button>\n </li>\n </ul>\n\n \n <div class="tab-content">\n \n <div id="data-control" class="scatterplot-config-control tab-pane active">\n \n </div>\n \n \n <div id="chart-control" class="scatterplot-config-control tab-pane">\n \n </div>\n\n \n <div id="chart-display" class="scatterplot-display tab-pane"></div>\n\n </div>\n</div>\n'});var ScatterplotConfigEditor=Backbone.View.extend({className:"scatterplot-control-form",initialize:function(a){if(this.model||(this.model=new Visualization({type:"scatterplot"})),!a||!a.dataset)throw new Error("ScatterplotConfigEditor requires a dataset");this.dataset=a.dataset,this.display=new ScatterplotDisplay({dataset:a.dataset,model:this.model})},render:function(){this.$el.empty().append(ScatterplotConfigEditor.templates.mainLayout({})),this.model.id&&(this.$el.find(".copy-btn").show(),this.$el.find(".save-btn").text("Update saved")),this.$el.find("[title]").tooltip(),this._render_dataControl(),this._render_chartControls(),this._render_chartDisplay();var a=this.model.get("config");return this.model.id&&_.isFinite(a.xColumn)&&_.isFinite(a.yColumn)&&this.renderChart(),this},_getColumnIndecesByType:function(){var a={numeric:[],text:[],all:[]};return _.each(this.dataset.metadata_column_types||[],function(b,c){"int"===b||"float"===b?a.numeric.push(c):("str"===b||"list"===b)&&a.text.push(c),a.all.push(c)}),a.numeric.length<2&&(a.numeric=[]),a},_render_dataControl:function(a){a=a||this.$el;var b=this,c=this.model.get("config"),d=this._getColumnIndecesByType(),e=a.find(".tab-pane#data-control");return e.html(ScatterplotConfigEditor.templates.dataControl({peek:this.dataset.peek})),e.find(".peek").peekColumnSelector({controls:[{label:"X Column",id:"xColumn",selected:c.xColumn,disabled:d.text},{label:"Y Column",id:"yColumn",selected:c.yColumn,disabled:d.text},{label:"ID Column",id:"idColumn",selected:c.idColumn}]}).on("peek-column-selector.change",function(a,c){b.model.set("config",c)}).on("peek-column-selector.rename",function(){}),e.find("[title]").tooltip(),e},_render_chartControls:function(a){function b(){var a=$(this),b=a.slider("value");c.model.set("config",_.object([[a.parent().data("config-key"),b]])),a.siblings(".slider-output").text(b)}a=a||this.$el;var c=this,d=this.model.get("config"),e=a.find("#chart-control");e.html(ScatterplotConfigEditor.templates.chartControl(d));var f={datapointSize:{min:2,max:10,step:1},width:{min:200,max:800,step:20},height:{min:200,max:800,step:20}};e.find(".numeric-slider-input").each(function(){var a=$(this),c=a.attr("data-config-key"),e=_.extend(f[c],{value:d[c],change:b,slide:b});a.find(".slider").slider(e),a.children(".slider-output").text(d[c])});var g=this.dataset.metadata_column_names||[],h=d.xLabel||g[d.xColumn]||"X",i=d.yLabel||g[d.yColumn]||"Y";return e.find('input[name="X-axis-label"]').val(h).on("change",function(){c.model.set("config",{xLabel:$(this).val()})}),e.find('input[name="Y-axis-label"]').val(i).on("change",function(){c.model.set("config",{yLabel:$(this).val()})}),e.find("[title]").tooltip(),e},_render_chartDisplay:function(a){a=a||this.$el;var b=a.find(".tab-pane#chart-display");return this.display.setElement(b),this.display.render(),b.find("[title]").tooltip(),b},events:{"change #include-id-checkbox":"toggleThirdColumnSelector","click #data-control .render-button":"renderChart","click #chart-control .render-button":"renderChart","click .save-btn":"saveVisualization"},saveVisualization:function(){var a=this;this.model.save().fail(function(b,c,d){console.error(b,c,d),a.trigger("save:error",view),alert("Error loading data:\n"+b.responseText)}).then(function(){a.display.render()})},toggleThirdColumnSelector:function(){this.$el.find('select[name="idColumn"]').parent().toggle()},renderChart:function(){this.$el.find(".nav li.disabled").removeClass("disabled"),this.$el.find("ul.nav").find('a[href="#chart-display"]').tab("show"),this.display.fetchData()},toString:function(){return"ScatterplotConfigEditor("+(this.dataset?this.dataset.id:"")+")"}});ScatterplotConfigEditor.templates={mainLayout:scatterplot.editor,dataControl:scatterplot.datacontrol,chartControl:scatterplot.chartcontrol};var ScatterplotDisplay=Backbone.View.extend({initialize:function(a){this.data=null,this.dataset=a.dataset,this.lineCount=this.dataset.metadata_data_lines||null},fetchData:function(){this.showLoadingIndicator();var a=this,b=this.model.get("config"),c=jQuery.getJSON("/api/datasets/"+this.dataset.id,{data_type:"raw_data",provider:"dataset-column",limit:b.pagination.perPage,offset:b.pagination.currPage*b.pagination.perPage});return c.done(function(b){a.data=b.data,a.trigger("data:fetched",a),a.renderData()}),c.fail(function(b,c,d){console.error(b,c,d),a.trigger("data:error",a),alert("Error loading data:\n"+b.responseText)}),c},showLoadingIndicator:function(){this.$el.find(".scatterplot-data-info").html(['<div class="loading-indicator">','<span class="fa fa-spinner fa-spin"></span>','<span class="loading-indicator-message">loading...</span>',"</div>"].join(""))},template:function(){var a=['<div class="controls clear">','<div class="right">','<p class="scatterplot-data-info"></p>','<button class="stats-toggle-btn">Stats</button>','<button class="rerender-btn">Redraw</button>',"</div>",'<div class="left">','<div class="page-control"></div>',"</div>","</div>","<svg/>",'<div class="stats-display"></div>'].join("");return a},render:function(){return this.$el.addClass("scatterplot-display").html(this.template()),this.data&&this.renderData(),this},renderData:function(){this.renderLeftControls(),this.renderRightControls(),this.renderPlot(this.data),this.getStats()},renderLeftControls:function(){var a=this,b=this.model.get("config");return this.$el.find(".controls .left .page-control").pagination({startingPage:b.pagination.currPage,perPage:b.pagination.perPage,totalDataSize:this.lineCount,currDataSize:this.data.length}).off().on("pagination.page-change",function(c,d){b.pagination.currPage=d,a.model.set("config",{pagination:b.pagination}),a.resetZoom(),a.fetchData()}),this},renderRightControls:function(){var a=this;this.setLineInfo(this.data),this.$el.find(".stats-toggle-btn").off().click(function(){a.toggleStats()}),this.$el.find(".rerender-btn").off().click(function(){a.resetZoom(),a.renderPlot(this.data)})},renderPlot:function(){var a=this,b=this.$el.find("svg");this.toggleStats(!1),b.off().empty().show().on("zoom.scatterplot",function(b,c){a.model.set("config",c)}),scatterplot(b.get(0),this.model.get("config"),this.data)},setLineInfo:function(a,b){if(a){var c=this.model.get("config"),d=this.lineCount||"an unknown total",e=c.pagination.currPage*c.pagination.perPage,f=e+a.length;this.$el.find(".controls p.scatterplot-data-info").text([e+1,"to",f,"of",d].join(" "))}else this.$el.find(".controls p.scatterplot-data-info").html(b||"");return this},resetZoom:function(a,b){return a=void 0!==a?a:1,b=void 0!==b?b:[0,0],this.model.set("config",{scale:a,translate:b}),this},getStats:function(){if(this.data){var a=this,b=this.model.get("config"),c=new Worker("/plugins/visualizations/scatterplot/static/worker-stats.js");c.postMessage({data:this.data,keys:[b.xColumn,b.yColumn]}),c.onerror=function(){c.terminate()},c.onmessage=function(b){a.renderStats(b.data)}}},renderStats:function(a){var b=this.model.get("config"),c=this.$el.find(".stats-display"),d=b.xLabel,e=b.yLabel,f=$("<table/>").addClass("table").append(["<thead><th></th><th>",d,"</th><th>",e,"</th></thead>"].join("")).append(_.map(a,function(a,b){return $(["<tr><td>",b,"</td><td>",a[0],"</td><td>",a[1],"</td></tr>"].join(""))}));c.empty().append(f)},toggleStats:function(a){var b=this.$el.find(".stats-display");a=void 0===a?b.is(":hidden"):a,a?(this.$el.find("svg").hide(),b.show(),this.$el.find(".controls .stats-toggle-btn").text("Plot")):(b.hide(),this.$el.find("svg").show(),this.$el.find(".controls .stats-toggle-btn").text("Stats"))},toString:function(){return"ScatterplotView()"}}),ScatterplotModel=Visualization.extend({defaults:{type:"scatterplot",config:{pagination:{currPage:0,perPage:3e3},width:400,height:400,margin:{top:16,right:16,bottom:40,left:54},xTicks:10,xLabel:"X",yTicks:10,yLabel:"Y",datapointSize:4,animDuration:500,scale:1,translate:[0,0]}}});
\ No newline at end of file
diff -r bfc3b5e52781683efc8024f18c4aa57c65af1e15 -r ee995df4a7e548befdfa09196b9665f05cfd55f6 config/plugins/visualizations/scatterplot/templates/scatterplot.mako
--- a/config/plugins/visualizations/scatterplot/templates/scatterplot.mako
+++ b/config/plugins/visualizations/scatterplot/templates/scatterplot.mako
@@ -27,12 +27,12 @@
'libs/jquery/jquery.migrate',
'libs/jquery/jquery-ui',
'libs/bootstrap',
- 'libs/require',
'libs/underscore',
'libs/backbone/backbone',
'libs/d3',
'libs/handlebars.runtime',
'mvc/ui',
+ 'jq-plugins/ui/peek-column-selector',
'mvc/visualization/visualization-model' )}
${h.javascript_link( root + 'plugins/visualizations/scatterplot/static/scatterplot-edit.js' )}
diff -r bfc3b5e52781683efc8024f18c4aa57c65af1e15 -r ee995df4a7e548befdfa09196b9665f05cfd55f6 static/scripts/jq-plugins/ui/peek-column-selector.js
--- /dev/null
+++ b/static/scripts/jq-plugins/ui/peek-column-selector.js
@@ -0,0 +1,317 @@
+// from: https://raw.githubusercontent.com/umdjs/umd/master/jqueryPlugin.js
+// Uses AMD or browser globals to create a jQuery plugin.
+(function (factory) {
+ if (typeof define === 'function' && define.amd) {
+ // AMD. Register as an anonymous module.
+ define(['jquery'], factory);
+ } else {
+ // Browser globals
+ factory(jQuery);
+ }
+
+}(function ($) {
+ //==============================================================================
+ /** Column selection using the peek display as the control.
+ * Adds rows to the bottom of the peek with clickable areas in each cell
+ * to allow the user to select columns.
+ * Column selection can be limited to a single column or multiple.
+ * (Optionally) adds a left hand column of column selection prompts.
+ * (Optionally) allows the column headers to be clicked/renamed
+ * and set to some initial value.
+ * (Optionally) hides comment rows.
+ * (Optionally) allows pre-selecting and disabling certain columns for
+ * each row control.
+ *
+ * Construct by selecting a peek table to be used with jQuery and
+ * calling 'peekColumnSelector' with options.
+ * Options must include a 'controls' array and can include other options
+ * listed below.
+ * @example:
+ * $( 'pre.peek' ).peekColumnSelector({
+ * columnNames : ["Chromosome", "Start", "Base", "", "", "Qual" ],
+ * controls : [
+ * { label: 'X Column', id: 'xColumn' },
+ * { label: 'Y Column', id: 'yColumn', selected: 2 },
+ * { label: 'ID Column', id: 'idColumn', selected: 4, disabled: [ 1, 5 ] },
+ * { label: 'Heatmap', id: 'heatmap', selected: [ 2, 4 ], disabled: [ 0, 1 ], multiselect: true,
+ * selectedText: 'Included', unselectedText: 'Excluded' }
+ * ],
+ * renameColumns : true,
+ * hideCommentRows : true,
+ * includePrompts : true,
+ * topLeftContent : 'Data sample:'
+ * }).on( 'peek-column-selector.change', function( ev, selection ){
+ * console.info( 'new selection:', selection );
+ * //{ yColumn: 2 }
+ * }).on( 'peek-column-selector.rename', function( ev, names ){
+ * console.info( 'column names', names );
+ * //[ 'Bler', 'Start', 'Base', '', '', 'Qual' ]
+ * });
+ *
+ * An event is fired when column selection is changed and the event
+ * is passed an object in the form: { the row id : the new selection value }.
+ * An event is also fired when the table headers are re-named and
+ * is passed the new array of column names.
+ */
+
+ /** option defaults */
+ var defaults = {
+ /** does this control allow renaming headers? */
+ renameColumns : false,
+ /** does this control allow renaming headers? */
+ columnNames : [],
+ /** the comment character used by the peek's datatype */
+ commentChar : '#',
+ /** should comment rows be shown or hidden in the peek */
+ hideCommentRows : false,
+ /** should a column of row control prompts be used */
+ includePrompts : true,
+ /** what is the content of the top left cell (often a title) */
+ topLeftContent : 'Columns:'
+ },
+ /** class added to the pre.peek element (to allow css on just the control) */
+ PEEKCONTROL_CLASS = 'peek-column-selector',
+ /** the string of the event fired when a control row changes */
+ CHANGE_EVENT = 'peek-column-selector.change',
+ /** the string of the event fired when a column is renamed */
+ RENAME_EVENT = 'peek-column-selector.rename',
+ /** class added to the control rows */
+ ROW_CLASS = 'control',
+ /** class added to the left-hand cells that serve as row prompts */
+ PROMPT_CLASS = 'control-prompt',
+ /** class added to selected _cells_/tds */
+ SELECTED_CLASS = 'selected',
+ /** class added to disabled/un-clickable cells/tds */
+ DISABLED_CLASS = 'disabled',
+ /** class added to the clickable surface within a cell to select it */
+ BUTTON_CLASS = 'button',
+ /** class added to peek table header (th) cells to indicate they can be clicked and are renamable */
+ RENAMABLE_HEADER_CLASS = 'renamable-header',
+ /** the data key used for each cell to store the column index ('data-...') */
+ COLUMN_INDEX_DATA_KEY = 'column-index',
+ /** renamable header data key used to store the column name (w/o the number and dot: '1.Bler') */
+ COLUMN_NAME_DATA_KEY = 'column-name';
+
+ //TODO: not happy with pure functional here - rows should polymorph (multi, single, etc.)
+ //TODO: needs clean up, move handlers to outer scope
+
+ // ........................................................................
+ /** validate the control data sent in for each row */
+ function validateControl( control ){
+ if( control.disabled && jQuery.type( control.disabled ) !== 'array' ){
+ throw new Error( '"disabled" must be defined as an array of indeces: ' + JSON.stringify( control ) );
+ }
+ if( control.multiselect && control.selected && jQuery.type( control.selected ) !== 'array' ){
+ throw new Error( 'Mulitselect rows need an array for "selected": ' + JSON.stringify( control ) );
+ }
+ if( !control.label || !control.id ){
+ throw new Error( 'Peek controls need a label and id for each control row: ' + JSON.stringify( control ) );
+ }
+ if( control.disabled && control.disabled.indexOf( control.selected ) !== -1 ){
+ throw new Error( 'Selected column is in the list of disabled columns: ' + JSON.stringify( control ) );
+ }
+ return control;
+ }
+
+ /** build the inner control surface (i.e. button-like) */
+ function buildButton( control, columnIndex ){
+ return $( '<div/>' ).addClass( BUTTON_CLASS ).text( control.label );
+ }
+
+ /** build the basic (shared) cell structure */
+ function buildControlCell( control, columnIndex ){
+ var $td = $( '<td/>' )
+ .html( buildButton( control, columnIndex ) )
+ .attr( 'data-' + COLUMN_INDEX_DATA_KEY, columnIndex );
+
+ // disable if index in disabled array
+ if( control.disabled && control.disabled.indexOf( columnIndex ) !== -1 ){
+ $td.addClass( DISABLED_CLASS );
+ }
+ return $td;
+ }
+
+ /** set the text of the control based on selected/un */
+ function setSelectedText( $cell, control, columnIndex ){
+ var $button = $cell.children( '.' + BUTTON_CLASS );
+ if( $cell.hasClass( SELECTED_CLASS ) ){
+ $button.html( ( control.selectedText !== undefined )?( control.selectedText ):( control.label ) );
+ } else {
+ $button.html( ( control.unselectedText !== undefined )?( control.unselectedText ):( control.label ) );
+ }
+ }
+
+ /** build a cell for a row that only allows one selection */
+ function buildSingleSelectCell( control, columnIndex ){
+ // only one selection - selected is single index
+ var $cell = buildControlCell( control, columnIndex );
+ if( control.selected === columnIndex ){
+ $cell.addClass( SELECTED_CLASS );
+ }
+ setSelectedText( $cell, control, columnIndex );
+
+ // only add the handler to non-disabled controls
+ if( !$cell.hasClass( DISABLED_CLASS ) ){
+ $cell.click( function selectClick( ev ){
+ var $cell = $( this );
+ // don't re-select or fire event if already selected
+ if( !$cell.hasClass( SELECTED_CLASS ) ){
+ // only one can be selected - remove selected on all others, add it here
+ var $otherSelected = $cell.parent().children( '.' + SELECTED_CLASS ).removeClass( SELECTED_CLASS );
+ $otherSelected.each( function(){
+ setSelectedText( $( this ), control, columnIndex );
+ });
+
+ $cell.addClass( SELECTED_CLASS );
+ setSelectedText( $cell, control, columnIndex );
+
+ // fire the event from the table itself, passing the id and index of selected
+ var eventData = {},
+ key = $cell.parent().attr( 'id' ),
+ val = $cell.data( COLUMN_INDEX_DATA_KEY );
+ eventData[ key ] = val;
+ $cell.parents( '.peek' ).trigger( CHANGE_EVENT, eventData );
+ }
+ });
+ }
+ return $cell;
+ }
+
+ /** build a cell for a row that allows multiple selections */
+ function buildMultiSelectCell( control, columnIndex ){
+ var $cell = buildControlCell( control, columnIndex );
+ // multiple selection - selected is an array
+ if( control.selected && control.selected.indexOf( columnIndex ) !== -1 ){
+ $cell.addClass( SELECTED_CLASS );
+ }
+ setSelectedText( $cell, control, columnIndex );
+
+ // only add the handler to non-disabled controls
+ if( !$cell.hasClass( DISABLED_CLASS ) ){
+ $cell.click( function multiselectClick( ev ){
+ var $cell = $( this );
+ // can be more than one selected - toggle selected on this cell
+ $cell.toggleClass( SELECTED_CLASS );
+ setSelectedText( $cell, control, columnIndex );
+ var selectedColumnIndeces = $cell.parent().find( '.' + SELECTED_CLASS ).map( function( i, e ){
+ return $( e ).data( COLUMN_INDEX_DATA_KEY );
+ });
+ // fire the event from the table itself, passing the id and index of selected
+ var eventData = {},
+ key = $cell.parent().attr( 'id' ),
+ val = jQuery.makeArray( selectedColumnIndeces );
+ eventData[ key ] = val;
+ $cell.parents( '.peek' ).trigger( CHANGE_EVENT, eventData );
+ });
+ }
+ return $cell;
+ }
+
+ /** iterate over columns in peek and create a control for each */
+ function buildControlCells( count, control ){
+ var $cells = [];
+ // build a control for each column - using a build fn based on control
+ for( var columnIndex=0; columnIndex<count; columnIndex+=1 ){
+ $cells.push( control.multiselect? buildMultiSelectCell( control, columnIndex )
+ : buildSingleSelectCell( control, columnIndex ) );
+ }
+ return $cells;
+ }
+
+ /** build a row of controls for the peek */
+ function buildControlRow( cellCount, control, includePrompts ){
+ var $controlRow = $( '<tr/>' ).attr( 'id', control.id ).addClass( ROW_CLASS );
+ if( includePrompts ){
+ var $promptCell = $( '<td/>' ).addClass( PROMPT_CLASS ).text( control.label + ':' );
+ $controlRow.append( $promptCell );
+ }
+ $controlRow.append( buildControlCells( cellCount, control ) );
+ return $controlRow;
+ }
+
+ // ........................................................................
+ /** add to the peek, using options for configuration, return the peek */
+ function peekColumnSelector( options ){
+ options = jQuery.extend( true, {}, defaults, options );
+
+ var $peek = $( this ).addClass( PEEKCONTROL_CLASS ),
+ $peektable = $peek.find( 'table' ),
+ // get the size of the tables - width and height, number of comment rows
+ columnCount = $peektable.find( 'th' ).size(),
+ rowCount = $peektable.find( 'tr' ).size(),
+ // get the rows containing text starting with the comment char (also make them grey)
+ $commentRows = $peektable.find( 'td[colspan]' ).map( function( e, i ){
+ var $this = $( this );
+ if( $this.text() && $this.text().match( new RegExp( '^' + options.commentChar ) ) ){
+ return $( this ).css( 'color', 'grey' ).parent().get(0);
+ }
+ return null;
+ });
+
+ // should comment rows in the peek be hidden?
+ if( options.hideCommentRows ){
+ $commentRows.hide();
+ rowCount -= $commentRows.size();
+ }
+ //console.debug( 'rowCount:', rowCount, 'columnCount:', columnCount, '$commentRows:', $commentRows );
+
+ // should a first column of control prompts be added?
+ if( options.includePrompts ){
+ var $topLeft = $( '<th/>' ).addClass( 'top-left' ).text( options.topLeftContent )
+ .attr( 'rowspan', rowCount );
+ $peektable.find( 'tr' ).first().prepend( $topLeft );
+ }
+
+ // save either the options column name or the parsed text of each column header in html5 data attr and text
+ var $headers = $peektable.find( 'th:not(.top-left)' ).each( function( i, e ){
+ var $this = $( this ),
+ // can be '1.name' or '1'
+ text = $this.text().replace( /^\d+\.*/, '' ),
+ name = options.columnNames[ i ] || text;
+ $this.attr( 'data-' + COLUMN_NAME_DATA_KEY, name )
+ .text( ( i + 1 ) + (( name )?( '.' + name ):( '' )) );
+ });
+
+ // allow renaming of columns when the header is clicked
+ if( options.renameColumns ){
+ $headers.addClass( RENAMABLE_HEADER_CLASS )
+ .click( function renameColumn(){
+ // prompt for new name
+ var $this = $( this ),
+ index = $this.index() + ( options.includePrompts? 0: 1 ),
+ prevName = $this.data( COLUMN_NAME_DATA_KEY ),
+ newColumnName = prompt( 'New column name:', prevName );
+ if( newColumnName !== null && newColumnName !== prevName ){
+ // set the new text and data
+ $this.text( index + ( newColumnName?( '.' + newColumnName ):'' ) )
+ .data( COLUMN_NAME_DATA_KEY, newColumnName )
+ .attr( 'data-', COLUMN_NAME_DATA_KEY, newColumnName );
+ // fire event for new column names
+ var columnNames = jQuery.makeArray(
+ $this.parent().children( 'th:not(.top-left)' ).map( function(){
+ return $( this ).data( COLUMN_NAME_DATA_KEY );
+ }));
+ $this.parents( '.peek' ).trigger( RENAME_EVENT, columnNames );
+ }
+ });
+ }
+
+ // build a row for each control
+ options.controls.forEach( function( control, i ){
+ validateControl( control );
+ var $controlRow = buildControlRow( columnCount, control, options.includePrompts );
+ $peektable.find( 'tbody' ).append( $controlRow );
+ });
+ return this;
+ }
+
+ // ........................................................................
+ // as jq plugin
+ jQuery.fn.extend({
+ peekColumnSelector : function $peekColumnSelector( options ){
+ return this.map( function(){
+ return peekColumnSelector.call( this, options );
+ });
+ }
+ });
+}));
diff -r bfc3b5e52781683efc8024f18c4aa57c65af1e15 -r ee995df4a7e548befdfa09196b9665f05cfd55f6 static/scripts/mvc/ui.js
--- a/static/scripts/mvc/ui.js
+++ b/static/scripts/mvc/ui.js
@@ -1344,311 +1344,3 @@
}
});
}());
-
-
-//==============================================================================
-/** Column selection using the peek display as the control.
- * Adds rows to the bottom of the peek with clickable areas in each cell
- * to allow the user to select columns.
- * Column selection can be limited to a single column or multiple.
- * (Optionally) adds a left hand column of column selection prompts.
- * (Optionally) allows the column headers to be clicked/renamed
- * and set to some initial value.
- * (Optionally) hides comment rows.
- * (Optionally) allows pre-selecting and disabling certain columns for
- * each row control.
- *
- * Construct by selecting a peek table to be used with jQuery and
- * calling 'peekControl' with options.
- * Options must include a 'controls' array and can include other options
- * listed below.
- * @example:
- * $( 'pre.peek' ).peekControl({
- * columnNames : ["Chromosome", "Start", "Base", "", "", "Qual" ],
- * controls : [
- * { label: 'X Column', id: 'xColumn' },
- * { label: 'Y Column', id: 'yColumn', selected: 2 },
- * { label: 'ID Column', id: 'idColumn', selected: 4, disabled: [ 1, 5 ] },
- * { label: 'Heatmap', id: 'heatmap', selected: [ 2, 4 ], disabled: [ 0, 1 ], multiselect: true,
- * selectedText: 'Included', unselectedText: 'Excluded' }
- * ],
- * renameColumns : true,
- * hideCommentRows : true,
- * includePrompts : true,
- * topLeftContent : 'Data sample:'
- * }).on( 'peek-control.change', function( ev, selection ){
- * console.info( 'new selection:', selection );
- * //{ yColumn: 2 }
- * }).on( 'peek-control.rename', function( ev, names ){
- * console.info( 'column names', names );
- * //[ 'Bler', 'Start', 'Base', '', '', 'Qual' ]
- * });
- *
- * An event is fired when column selection is changed and the event
- * is passed an object in the form: { the row id : the new selection value }.
- * An event is also fired when the table headers are re-named and
- * is passed the new array of column names.
- */
-(function(){
-
- /** option defaults */
- var defaults = {
- /** does this control allow renaming headers? */
- renameColumns : false,
- /** does this control allow renaming headers? */
- columnNames : [],
- /** the comment character used by the peek's datatype */
- commentChar : '#',
- /** should comment rows be shown or hidden in the peek */
- hideCommentRows : false,
- /** should a column of row control prompts be used */
- includePrompts : true,
- /** what is the content of the top left cell (often a title) */
- topLeftContent : 'Columns:'
- },
- /** the string of the event fired when a control row changes */
- CHANGE_EVENT = 'peek-control.change',
- /** the string of the event fired when a column is renamed */
- RENAME_EVENT = 'peek-control.rename',
- /** class added to the pre.peek element (to allow css on just the control) */
- PEEKCONTROL_CLASS = 'peek-control',
- /** class added to the control rows */
- ROW_CLASS = 'control',
- /** class added to the left-hand cells that serve as row prompts */
- PROMPT_CLASS = 'control-prompt',
- /** class added to selected _cells_/tds */
- SELECTED_CLASS = 'selected',
- /** class added to disabled/un-clickable cells/tds */
- DISABLED_CLASS = 'disabled',
- /** class added to the clickable surface within a cell to select it */
- BUTTON_CLASS = 'button',
- /** class added to peek table header (th) cells to indicate they can be clicked and are renamable */
- RENAMABLE_HEADER_CLASS = 'renamable-header',
- /** the data key used for each cell to store the column index ('data-...') */
- COLUMN_INDEX_DATA_KEY = 'column-index',
- /** renamable header data key used to store the column name (w/o the number and dot: '1.Bler') */
- COLUMN_NAME_DATA_KEY = 'column-name';
-
- //TODO: not happy with pure functional here - rows should polymorph (multi, single, etc.)
- //TODO: needs clean up, move handlers to outer scope
-
- // ........................................................................
- /** validate the control data sent in for each row */
- function validateControl( control ){
- if( control.disabled && jQuery.type( control.disabled ) !== 'array' ){
- throw new Error( '"disabled" must be defined as an array of indeces: ' + JSON.stringify( control ) );
- }
- if( control.multiselect && control.selected && jQuery.type( control.selected ) !== 'array' ){
- throw new Error( 'Mulitselect rows need an array for "selected": ' + JSON.stringify( control ) );
- }
- if( !control.label || !control.id ){
- throw new Error( 'Peek controls need a label and id for each control row: ' + JSON.stringify( control ) );
- }
- if( control.disabled && control.disabled.indexOf( control.selected ) !== -1 ){
- throw new Error( 'Selected column is in the list of disabled columns: ' + JSON.stringify( control ) );
- }
- return control;
- }
-
- /** build the inner control surface (i.e. button-like) */
- function buildButton( control, columnIndex ){
- return $( '<div/>' ).addClass( BUTTON_CLASS ).text( control.label );
- }
-
- /** build the basic (shared) cell structure */
- function buildControlCell( control, columnIndex ){
- var $td = $( '<td/>' )
- .html( buildButton( control, columnIndex ) )
- .attr( 'data-' + COLUMN_INDEX_DATA_KEY, columnIndex );
-
- // disable if index in disabled array
- if( control.disabled && control.disabled.indexOf( columnIndex ) !== -1 ){
- $td.addClass( DISABLED_CLASS );
- }
- return $td;
- }
-
- /** set the text of the control based on selected/un */
- function setSelectedText( $cell, control, columnIndex ){
- var $button = $cell.children( '.' + BUTTON_CLASS );
- if( $cell.hasClass( SELECTED_CLASS ) ){
- $button.html( ( control.selectedText !== undefined )?( control.selectedText ):( control.label ) );
- } else {
- $button.html( ( control.unselectedText !== undefined )?( control.unselectedText ):( control.label ) );
- }
- }
-
- /** build a cell for a row that only allows one selection */
- function buildSingleSelectCell( control, columnIndex ){
- // only one selection - selected is single index
- var $cell = buildControlCell( control, columnIndex );
- if( control.selected === columnIndex ){
- $cell.addClass( SELECTED_CLASS );
- }
- setSelectedText( $cell, control, columnIndex );
-
- // only add the handler to non-disabled controls
- if( !$cell.hasClass( DISABLED_CLASS ) ){
- $cell.click( function selectClick( ev ){
- var $cell = $( this );
- // don't re-select or fire event if already selected
- if( !$cell.hasClass( SELECTED_CLASS ) ){
- // only one can be selected - remove selected on all others, add it here
- var $otherSelected = $cell.parent().children( '.' + SELECTED_CLASS ).removeClass( SELECTED_CLASS );
- $otherSelected.each( function(){
- setSelectedText( $( this ), control, columnIndex );
- });
-
- $cell.addClass( SELECTED_CLASS );
- setSelectedText( $cell, control, columnIndex );
-
- // fire the event from the table itself, passing the id and index of selected
- var eventData = {},
- key = $cell.parent().attr( 'id' ),
- val = $cell.data( COLUMN_INDEX_DATA_KEY );
- eventData[ key ] = val;
- $cell.parents( '.peek' ).trigger( CHANGE_EVENT, eventData );
- }
- });
- }
- return $cell;
- }
-
- /** build a cell for a row that allows multiple selections */
- function buildMultiSelectCell( control, columnIndex ){
- var $cell = buildControlCell( control, columnIndex );
- // multiple selection - selected is an array
- if( control.selected && control.selected.indexOf( columnIndex ) !== -1 ){
- $cell.addClass( SELECTED_CLASS );
- }
- setSelectedText( $cell, control, columnIndex );
-
- // only add the handler to non-disabled controls
- if( !$cell.hasClass( DISABLED_CLASS ) ){
- $cell.click( function multiselectClick( ev ){
- var $cell = $( this );
- // can be more than one selected - toggle selected on this cell
- $cell.toggleClass( SELECTED_CLASS );
- setSelectedText( $cell, control, columnIndex );
- var selectedColumnIndeces = $cell.parent().find( '.' + SELECTED_CLASS ).map( function( i, e ){
- return $( e ).data( COLUMN_INDEX_DATA_KEY );
- });
- // fire the event from the table itself, passing the id and index of selected
- var eventData = {},
- key = $cell.parent().attr( 'id' ),
- val = jQuery.makeArray( selectedColumnIndeces );
- eventData[ key ] = val;
- $cell.parents( '.peek' ).trigger( CHANGE_EVENT, eventData );
- });
- }
- return $cell;
- }
-
- /** iterate over columns in peek and create a control for each */
- function buildControlCells( count, control ){
- var $cells = [];
- // build a control for each column - using a build fn based on control
- for( var columnIndex=0; columnIndex<count; columnIndex+=1 ){
- $cells.push( control.multiselect? buildMultiSelectCell( control, columnIndex )
- : buildSingleSelectCell( control, columnIndex ) );
- }
- return $cells;
- }
-
- /** build a row of controls for the peek */
- function buildControlRow( cellCount, control, includePrompts ){
- var $controlRow = $( '<tr/>' ).attr( 'id', control.id ).addClass( ROW_CLASS );
- if( includePrompts ){
- var $promptCell = $( '<td/>' ).addClass( PROMPT_CLASS ).text( control.label + ':' );
- $controlRow.append( $promptCell );
- }
- $controlRow.append( buildControlCells( cellCount, control ) );
- return $controlRow;
- }
-
- // ........................................................................
- /** add to the peek, using options for configuration, return the peek */
- function peekControl( options ){
- options = jQuery.extend( true, {}, defaults, options );
-
- var $peek = $( this ).addClass( PEEKCONTROL_CLASS ),
- $peektable = $peek.find( 'table' ),
- // get the size of the tables - width and height, number of comment rows
- columnCount = $peektable.find( 'th' ).size(),
- rowCount = $peektable.find( 'tr' ).size(),
- // get the rows containing text starting with the comment char (also make them grey)
- $commentRows = $peektable.find( 'td[colspan]' ).map( function( e, i ){
- var $this = $( this );
- if( $this.text() && $this.text().match( new RegExp( '^' + options.commentChar ) ) ){
- return $( this ).css( 'color', 'grey' ).parent().get(0);
- }
- return null;
- });
-
- // should comment rows in the peek be hidden?
- if( options.hideCommentRows ){
- $commentRows.hide();
- rowCount -= $commentRows.size();
- }
- //console.debug( 'rowCount:', rowCount, 'columnCount:', columnCount, '$commentRows:', $commentRows );
-
- // should a first column of control prompts be added?
- if( options.includePrompts ){
- var $topLeft = $( '<th/>' ).addClass( 'top-left' ).text( options.topLeftContent )
- .attr( 'rowspan', rowCount );
- $peektable.find( 'tr' ).first().prepend( $topLeft );
- }
-
- // save either the options column name or the parsed text of each column header in html5 data attr and text
- var $headers = $peektable.find( 'th:not(.top-left)' ).each( function( i, e ){
- var $this = $( this ),
- // can be '1.name' or '1'
- text = $this.text().replace( /^\d+\.*/, '' ),
- name = options.columnNames[ i ] || text;
- $this.attr( 'data-' + COLUMN_NAME_DATA_KEY, name )
- .text( ( i + 1 ) + (( name )?( '.' + name ):( '' )) );
- });
-
- // allow renaming of columns when the header is clicked
- if( options.renameColumns ){
- $headers.addClass( RENAMABLE_HEADER_CLASS )
- .click( function renameColumn(){
- // prompt for new name
- var $this = $( this ),
- index = $this.index() + ( options.includePrompts? 0: 1 ),
- prevName = $this.data( COLUMN_NAME_DATA_KEY ),
- newColumnName = prompt( 'New column name:', prevName );
- if( newColumnName !== null && newColumnName !== prevName ){
- // set the new text and data
- $this.text( index + ( newColumnName?( '.' + newColumnName ):'' ) )
- .data( COLUMN_NAME_DATA_KEY, newColumnName )
- .attr( 'data-', COLUMN_NAME_DATA_KEY, newColumnName );
- // fire event for new column names
- var columnNames = jQuery.makeArray(
- $this.parent().children( 'th:not(.top-left)' ).map( function(){
- return $( this ).data( COLUMN_NAME_DATA_KEY );
- }));
- $this.parents( '.peek' ).trigger( RENAME_EVENT, columnNames );
- }
- });
- }
-
- // build a row for each control
- options.controls.forEach( function( control, i ){
- validateControl( control );
- var $controlRow = buildControlRow( columnCount, control, options.includePrompts );
- $peektable.find( 'tbody' ).append( $controlRow );
- });
- return this;
- }
-
- // ........................................................................
- // as jq plugin
- jQuery.fn.extend({
- peekControl : function $peekControl( options ){
- return this.map( function(){
- return peekControl.call( this, options );
- });
- }
- });
-}());
diff -r bfc3b5e52781683efc8024f18c4aa57c65af1e15 -r ee995df4a7e548befdfa09196b9665f05cfd55f6 static/scripts/packed/jq-plugins/ui/peek-column-selector.js
--- /dev/null
+++ b/static/scripts/packed/jq-plugins/ui/peek-column-selector.js
@@ -0,0 +1,1 @@
+(function(a){if(typeof define==="function"&&define.amd){define(["jquery"],a)}else{a(jQuery)}}(function(b){var f={renameColumns:false,columnNames:[],commentChar:"#",hideCommentRows:false,includePrompts:true,topLeftContent:"Columns:"},m="peek-column-selector",t="peek-column-selector.change",u="peek-column-selector.rename",w="control",e="control-prompt",c="selected",o="disabled",v="button",r="renamable-header",q="column-index",a="column-name";function i(x){if(x.disabled&&jQuery.type(x.disabled)!=="array"){throw new Error('"disabled" must be defined as an array of indeces: '+JSON.stringify(x))}if(x.multiselect&&x.selected&&jQuery.type(x.selected)!=="array"){throw new Error('Mulitselect rows need an array for "selected": '+JSON.stringify(x))}if(!x.label||!x.id){throw new Error("Peek controls need a label and id for each control row: "+JSON.stringify(x))}if(x.disabled&&x.disabled.indexOf(x.selected)!==-1){throw new Error("Selected column is in the list of disabled columns: "+JSON.stringify(x))}return x}function p(y,x){return b("<div/>").addClass(v).text(y.label)}function l(y,x){var z=b("<td/>").html(p(y,x)).attr("data-"+q,x);if(y.disabled&&y.disabled.indexOf(x)!==-1){z.addClass(o)}return z}function d(z,A,x){var y=z.children("."+v);if(z.hasClass(c)){y.html((A.selectedText!==undefined)?(A.selectedText):(A.label))}else{y.html((A.unselectedText!==undefined)?(A.unselectedText):(A.label))}}function h(A,y){var z=l(A,y);if(A.selected===y){z.addClass(c)}d(z,A,y);if(!z.hasClass(o)){z.click(function x(E){var F=b(this);if(!F.hasClass(c)){var B=F.parent().children("."+c).removeClass(c);B.each(function(){d(b(this),A,y)});F.addClass(c);d(F,A,y);var D={},C=F.parent().attr("id"),G=F.data(q);D[C]=G;F.parents(".peek").trigger(t,D)}})}return z}function n(A,y){var z=l(A,y);if(A.selected&&A.selected.indexOf(y)!==-1){z.addClass(c)}d(z,A,y);if(!z.hasClass(o)){z.click(function x(E){var F=b(this);F.toggleClass(c);d(F,A,y);var D=F.parent().find("."+c).map(function(H,I){return b(I).data(q)});var C={},B=F.parent().attr("id"),G=jQuery.makeArray(D);C[B]=G;F.parents(".peek").trigger(t,C)})}return z}function k(z,A){var x=[];for(var y=0;y<z;y+=1){x.push(A.multiselect?n(A,y):h(A,y))}return x}function s(z,A,y){var B=b("<tr/>").attr("id",A.id).addClass(w);if(y){var x=b("<td/>").addClass(e).text(A.label+":");B.append(x)}B.append(k(z,A));return B}function j(F){F=jQuery.extend(true,{},f,F);var E=b(this).addClass(m),B=E.find("table"),A=B.find("th").size(),D=B.find("tr").size(),x=B.find("td[colspan]").map(function(I,G){var H=b(this);if(H.text()&&H.text().match(new RegExp("^"+F.commentChar))){return b(this).css("color","grey").parent().get(0)}return null});if(F.hideCommentRows){x.hide();D-=x.size()}if(F.includePrompts){var z=b("<th/>").addClass("top-left").text(F.topLeftContent).attr("rowspan",D);B.find("tr").first().prepend(z)}var C=B.find("th:not(.top-left)").each(function(H,J){var I=b(this),K=I.text().replace(/^\d+\.*/,""),G=F.columnNames[H]||K;I.attr("data-"+a,G).text((H+1)+((G)?("."+G):("")))});if(F.renameColumns){C.addClass(r).click(function y(){var H=b(this),G=H.index()+(F.includePrompts?0:1),J=H.data(a),I=prompt("New column name:",J);if(I!==null&&I!==J){H.text(G+(I?("."+I):"")).data(a,I).attr("data-",a,I);var K=jQuery.makeArray(H.parent().children("th:not(.top-left)").map(function(){return b(this).data(a)}));H.parents(".peek").trigger(u,K)}})}F.controls.forEach(function(H,G){i(H);var I=s(A,H,F.includePrompts);B.find("tbody").append(I)});return this}jQuery.fn.extend({peekColumnSelector:function g(x){return this.map(function(){return j.call(this,x)})}})}));
\ No newline at end of file
diff -r bfc3b5e52781683efc8024f18c4aa57c65af1e15 -r ee995df4a7e548befdfa09196b9665f05cfd55f6 static/scripts/packed/mvc/ui.js
--- a/static/scripts/packed/mvc/ui.js
+++ b/static/scripts/packed/mvc/ui.js
@@ -1,1 +1,1 @@
-var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:true,id:null,href:null,target:null,enabled:true,visible:true,tooltip_config:{}}});var IconButtonView=Backbone.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"};this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var a=this.template(this.model.toJSON());a.tooltip(this.model.get("tooltip_config"));this.$el.replaceWith(a);this.setElement(a);return this},events:{click:"click"},click:function(a){if(_.isFunction(this.model.get("on_click"))){this.model.get("on_click")(a);return false}return true},template:function(b){var a='title="'+b.title+'" class="icon-button';if(b.is_menu_button){a+=" menu-button"}a+=" "+b.icon_class;if(!b.enabled){a+="_disabled"}a+='"';if(b.id){a+=' id="'+b.id+'"'}a+=' href="'+b.href+'"';if(b.target){a+=' target="'+b.target+'"'}if(!b.visible){a+=' style="display: none;"'}if(b.enabled){a="<a "+a+"/>"}else{a="<span "+a+"/>"}return $(a)}});var IconButtonCollection=Backbone.Collection.extend({model:IconButton});var IconButtonMenuView=Backbone.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var a=this;this.collection.each(function(d){var b=$("<a/>").attr("href","javascript:void(0)").attr("title",d.attributes.title).addClass("icon-button menu-button").addClass(d.attributes.icon_class).appendTo(a.$el).click(d.attributes.on_click);if(d.attributes.tooltip_config){b.tooltip(d.attributes.tooltip_config)}var c=d.get("options");if(c){make_popupmenu(b,c)}});return this}});var create_icon_buttons_menu=function(b,a){if(!a){a={}}var c=new IconButtonCollection(_.map(b,function(d){return new IconButton(_.extend(d,a))}));return new IconButtonMenuView({collection:c})};var Grid=Backbone.Collection.extend({});var GridView=Backbone.View.extend({});var PopupMenu=Backbone.View.extend({initialize:function(b,a){this.$button=b;if(!this.$button.size()){this.$button=$("<div/>")}this.options=a||[];var c=this;this.$button.click(function(d){$(".popmenu-wrapper").remove();c._renderAndShow(d);return false})},_renderAndShow:function(a){this.render();this.$el.appendTo("body").css(this._getShownPosition(a)).show();this._setUpCloseBehavior()},render:function(){this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options));if(this.options.length){var a=this;this.$el.find("li").each(function(c,b){var d=a.options[c];if(d.func){$(this).children("a.popupmenu-option").click(function(e){d.func.call(a,e,d)})}})}return this},template:function(b,a){return['<ul id="',b,'-menu" class="dropdown-menu">',this._templateOptions(a),"</ul>"].join("")},_templateOptions:function(a){if(!a.length){return"<li>(no options)</li>"}return _.map(a,function(d){if(d.divider){return'<li class="divider"></li>'}else{if(d.header){return['<li class="head"><a href="javascript:void(0);">',d.html,"</a></li>"].join("")}}var c=d.href||"javascript:void(0);",e=(d.target)?(' target="'+d.target+'"'):(""),b=(d.checked)?('<span class="fa fa-check"></span>'):("");return['<li><a class="popupmenu-option" href="',c,'"',e,">",b,d.html,"</a></li>"].join("")}).join("")},_getShownPosition:function(b){var c=this.$el.width();var a=b.pageX-c/2;a=Math.min(a,$(document).scrollLeft()+$(window).width()-c-5);a=Math.max(a,$(document).scrollLeft()+5);return{top:b.pageY,left:a}},_setUpCloseBehavior:function(){var c=this;function a(e){$(document).off("click.close_popup");if(window.parent!==window){try{$(window.parent.document).off("click.close_popup")}catch(d){}}else{try{$("iframe#galaxy_main").contents().off("click.close_popup")}catch(d){}}c.remove()}$("html").one("click.close_popup",a);if(window.parent!==window){try{$(window.parent.document).find("html").one("click.close_popup",a)}catch(b){}}else{try{$("iframe#galaxy_main").contents().one("click.close_popup",a)}catch(b){}}},addItem:function(b,a){a=(a>=0)?a:this.options.length;this.options.splice(a,0,b);return this},removeItem:function(a){if(a>=0){this.options.splice(a,1)}return this},findIndexByHtml:function(b){for(var a=0;a<this.options.length;a++){if(_.has(this.options[a],"html")&&(this.options[a].html===b)){return a}}return null},findItemByHtml:function(a){return this.options[(this.findIndexByHtml(a))]},toString:function(){return"PopupMenu"}});PopupMenu.create=function _create(b,a){return new PopupMenu(b,a)};PopupMenu.make_popupmenu=function(b,c){var a=[];_.each(c,function(f,d){var e={html:d};if(f===null){e.header=true}else{if(jQuery.type(f)==="function"){e.func=f}}a.push(e)});return new PopupMenu($(b),a)};PopupMenu.convertLinksToOptions=function(c,a){c=$(c);a=a||"a";var b=[];c.find(a).each(function(g,e){var f={},d=$(g);f.html=d.text();if(d.attr("href")){var j=d.attr("href"),k=d.attr("target"),h=d.attr("confirm");f.func=function(){if((h)&&(!confirm(h))){return}switch(k){case"_parent":window.parent.location=j;break;case"_top":window.top.location=j;break;default:window.location=j}}}b.push(f)});return b};PopupMenu.fromExistingDom=function(d,c,a){d=$(d);c=$(c);var b=PopupMenu.convertLinksToOptions(c,a);c.remove();return new PopupMenu(d,b)};PopupMenu.make_popup_menus=function(c,b,d){c=c||document;b=b||"div[popupmenu]";d=d||function(e,f){return"#"+e.attr("popupmenu")};var a=[];$(c).find(b).each(function(){var e=$(this),f=$(c).find(d(e,c));a.push(PopupMenu.fromDom(f,e));f.addClass("popup")});return a};var faIconButton=function(a){a=a||{};a.tooltipConfig=a.tooltipConfig||{placement:"bottom"};a.classes=["icon-btn"].concat(a.classes||[]);if(a.disabled){a.classes.push("disabled")}var b=['<a class="',a.classes.join(" "),'"',((a.title)?(' title="'+a.title+'"'):("")),((!a.disabled&&a.target)?(' target="'+a.target+'"'):("")),' href="',((!a.disabled&&a.href)?(a.href):("javascript:void(0);")),'">','<span class="fa ',a.faIcon,'"></span>',"</a>"].join("");var c=$(b).tooltip(a.tooltipConfig);if(_.isFunction(a.onclick)){c.click(a.onclick)}return c};function LoadingIndicator(a,c){var b=this;c=jQuery.extend({cover:false},c||{});function d(){var e=['<div class="loading-indicator">','<div class="loading-indicator-text">','<span class="fa fa-spinner fa-spin fa-lg"></span>','<span class="loading-indicator-message">loading...</span>',"</div>","</div>"].join("\n");var g=$(e).hide().css(c.css||{position:"fixed"}),f=g.children(".loading-indicator-text");if(c.cover){g.css({"z-index":2,top:a.css("top"),bottom:a.css("bottom"),left:a.css("left"),right:a.css("right"),opacity:0.5,"background-color":"white","text-align":"center"});f=g.children(".loading-indicator-text").css({"margin-top":"20px"})}else{f=g.children(".loading-indicator-text").css({margin:"12px 0px 0px 10px",opacity:"0.85",color:"grey"});f.children(".loading-indicator-message").css({margin:"0px 8px 0px 0px","font-style":"italic"})}return g}b.show=function(f,e,g){f=f||"loading...";e=e||"fast";a.parent().find(".loading-indicator").remove();b.$indicator=d().insertBefore(a);b.message(f);b.$indicator.fadeIn(e,g);return b};b.message=function(e){b.$indicator.find("i").text(e)};b.hide=function(e,f){e=e||"fast";if(b.$indicator&&b.$indicator.size()){b.$indicator.fadeOut(e,function(){b.$indicator.remove();if(f){f()}})}else{if(f){f()}}return b};return b}(function(){var b=window._l||function(d){return d};function a(k,q){var e=27,n=13,d=$(k),f=true,h={initialVal:"",name:"search",placeholder:"search",classes:"",onclear:function(){},onfirstsearch:null,onsearch:function(r){},minSearchLen:0,escWillClear:true,oninit:function(){}};function j(r){var s=$(this).parent().children("input");s.focus().val("").trigger("clear:searchInput");q.onclear()}function p(s,r){$(this).trigger("search:searchInput",r);if(typeof q.onfirstsearch==="function"&&f){f=false;q.onfirstsearch(r)}else{q.onsearch(r)}}function g(){return['<input type="text" name="',q.name,'" placeholder="',q.placeholder,'" ','class="search-query ',q.classes,'" ',"/>"].join("")}function m(){return $(g()).focus(function(r){$(this).select()}).keyup(function(s){s.preventDefault();s.stopPropagation();if(!$(this).val()){$(this).blur()}if(s.which===e&&q.escWillClear){j.call(this,s)}else{var r=$(this).val();if((s.which===n)||(q.minSearchLen&&r.length>=q.minSearchLen)){p.call(this,s,r)}else{if(!r.length){j.call(this,s)}}}}).on("change",function(r){p.call(this,r,$(this).val())}).val(q.initialVal)}function l(){return $(['<span class="search-clear fa fa-times-circle" ','title="',b("clear search (esc)"),'"></span>'].join("")).tooltip({placement:"bottom"}).click(function(r){j.call(this,r)})}function o(){return $(['<span class="search-loading fa fa-spinner fa-spin" ','title="',b("loading..."),'"></span>'].join("")).hide().tooltip({placement:"bottom"})}function i(){d.find(".search-loading").toggle();d.find(".search-clear").toggle()}if(jQuery.type(q)==="string"){if(q==="toggle-loading"){i()}return d}if(jQuery.type(q)==="object"){q=jQuery.extend(true,{},h,q)}return d.addClass("search-input").prepend([m(),l(),o()])}jQuery.fn.extend({searchInput:function c(d){return this.each(function(){return a(this,d)})}})}());(function(){function c(o,n){this.currModeIndex=0;return this._init(o,n)}c.prototype.DATA_KEY="mode-button";c.prototype.defaults={switchModesOnClick:true};c.prototype._init=function j(o,n){n=n||{};this.$element=$(o);this.options=jQuery.extend(true,{},this.defaults,n);if(!n.modes){throw new Error('ModeButton requires a "modes" array')}var q=this;this.$element.click(function p(r){q.callModeFn();if(q.options.switchModesOnClick){q._incModeIndex()}$(this).html(q.options.modes[q.currModeIndex].html)});return this.reset()};c.prototype._incModeIndex=function l(){this.currModeIndex+=1;if(this.currModeIndex>=this.options.modes.length){this.currModeIndex=0}return this};c.prototype._getModeIndex=function f(n){for(var o=0;o<this.options.modes.length;o+=1){if(this.options.modes[o].mode===n){return o}}throw new Error("mode not found: "+n)};c.prototype._setModeByIndex=function m(n){var o=this.options.modes[n];if(!o){throw new Error("mode index not found: "+n)}this.currModeIndex=n;if(o.html){this.$element.html(o.html)}return this};c.prototype.currentMode=function d(){return this.options.modes[this.currModeIndex]};c.prototype.current=function i(){return this.currentMode().mode};c.prototype.getMode=function g(n){if(!n){return this.currentMode()}return this.options.modes[(this._getModeIndex(n))]};c.prototype.hasMode=function b(n){try{return !!this.getMode(n)}catch(o){}return false};c.prototype.setMode=function a(n){return this._setModeByIndex(this._getModeIndex(n))};c.prototype.reset=function h(){this.currModeIndex=0;if(this.options.initialMode){this.currModeIndex=this._getModeIndex(this.options.initialMode)}return this._setModeByIndex(this.currModeIndex)};c.prototype.callModeFn=function e(n){var o=this.getMode(n).onclick;if(o&&jQuery.type(o==="function")){return o.call(this.$element.get(0))}return undefined};jQuery.fn.extend({modeButton:function k(n){if(!this.size()){return this}if(jQuery.type(n)==="object"){return this.map(function(){var r=$(this);r.data("mode-button",new c(r,n));return this})}var p=$(this[0]),o=p.data("mode-button");if(!o){throw new Error("modeButton needs an options object or string name of a function")}if(o&&jQuery.type(n)==="string"){var q=n;if(o&&jQuery.type(o[q])==="function"){return o[q].apply(o,jQuery.makeArray(arguments).slice(1))}}return o}})}());function dropDownSelect(b,c){c=c||((!_.isEmpty(b))?(b[0]):(""));var a=$(['<div class="dropdown-select btn-group">','<button type="button" class="btn btn-default">','<span class="dropdown-select-selected">'+c+"</span>","</button>","</div>"].join("\n"));if(b&&b.length>1){a.find("button").addClass("dropdown-toggle").attr("data-toggle","dropdown").append(' <span class="caret"></span>');a.append(['<ul class="dropdown-menu" role="menu">',_.map(b,function(e){return['<li><a href="javascript:void(0)">',e,"</a></li>"].join("")}).join("\n"),"</ul>"].join("\n"))}function d(g){var h=$(this),f=h.parents(".dropdown-select"),e=h.text();f.find(".dropdown-select-selected").text(e);f.trigger("change.dropdown-select",e)}a.find("a").click(d);return a}(function(){function e(k,j){return this.init(k,j)}e.prototype.DATA_KEY="filter-control";e.prototype.init=function g(k,j){j=j||{filters:[]};this.$element=$(k).addClass("filter-control btn-group");this.options=jQuery.extend(true,{},this.defaults,j);this.currFilter=this.options.filters[0];return this.render()};e.prototype.render=function d(){this.$element.empty().append([this._renderKeySelect(),this._renderOpSelect(),this._renderValueInput()]);return this};e.prototype._renderKeySelect=function a(){var j=this;var k=this.options.filters.map(function(l){return l.key});this.$keySelect=dropDownSelect(k,this.currFilter.key).addClass("filter-control-key").on("change.dropdown-select",function(m,l){j.currFilter=_.findWhere(j.options.filters,{key:l});j.render()._triggerChange()});return this.$keySelect};e.prototype._renderOpSelect=function i(){var j=this,k=this.currFilter.ops;this.$opSelect=dropDownSelect(k,k[0]).addClass("filter-control-op").on("change.dropdown-select",function(m,l){j._triggerChange()});return this.$opSelect};e.prototype._renderValueInput=function c(){var j=this;if(this.currFilter.values){this.$valueSelect=dropDownSelect(this.currFilter.values,this.currFilter.values[0]).on("change.dropdown-select",function(l,k){j._triggerChange()})}else{this.$valueSelect=$("<input/>").addClass("form-control").on("change",function(k,l){j._triggerChange()})}this.$valueSelect.addClass("filter-control-value");return this.$valueSelect};e.prototype.val=function b(){var k=this.$element.find(".filter-control-key .dropdown-select-selected").text(),m=this.$element.find(".filter-control-op .dropdown-select-selected").text(),j=this.$element.find(".filter-control-value"),l=(j.hasClass("dropdown-select"))?(j.find(".dropdown-select-selected").text()):(j.val());return{key:k,op:m,value:l}};e.prototype._triggerChange=function h(){this.$element.trigger("change.filter-control",this.val())};jQuery.fn.extend({filterControl:function f(k){var j=jQuery.makeArray(arguments).slice(1);return this.map(function(){var n=$(this),m=n.data(e.prototype.DATA_KEY);if(jQuery.type(k)==="object"){m=new e(n,k);n.data(e.prototype.DATA_KEY,m)}if(m&&jQuery.type(k)==="string"){var l=m[k];if(jQuery.type(l)==="function"){return l.apply(m,j)}}return this})}})}());(function(){function i(o,n){this.numPages=null;this.currPage=0;return this.init(o,n)}i.prototype.DATA_KEY="pagination";i.prototype.defaults={startingPage:0,perPage:20,totalDataSize:null,currDataSize:null};i.prototype.init=function g(n,o){o=o||{};this.$element=n;this.options=jQuery.extend(true,{},this.defaults,o);this.currPage=this.options.startingPage;if(this.options.totalDataSize!==null){this.numPages=Math.ceil(this.options.totalDataSize/this.options.perPage);if(this.currPage>=this.numPages){this.currPage=this.numPages-1}}this.$element.data(i.prototype.DATA_KEY,this);this._render();return this};function m(n){return $(['<li><a href="javascript:void(0);">',n,"</a></li>"].join(""))}i.prototype._render=function e(){if(this.options.totalDataSize===0){return this}if(this.numPages===1){return this}if(this.numPages>0){this._renderPages();this._scrollToActivePage()}else{this._renderPrevNext()}return this};i.prototype._renderPrevNext=function b(){var o=this,p=m("Prev"),n=m("Next"),q=$("<ul/>").addClass("pagination pagination-prev-next");if(this.currPage===0){p.addClass("disabled")}else{p.click(function(){o.prevPage()})}if((this.numPages&&this.currPage===(this.numPages-1))||(this.options.currDataSize&&this.options.currDataSize<this.options.perPage)){n.addClass("disabled")}else{n.click(function(){o.nextPage()})}this.$element.html(q.append([p,n]));return this.$element};i.prototype._renderPages=function a(){var n=this,q=$("<div>").addClass("pagination-scroll-container"),s=$("<ul/>").addClass("pagination pagination-page-list"),r=function(t){n.goToPage($(this).data("page"))};for(var o=0;o<this.numPages;o+=1){var p=m(o+1).attr("data-page",o).click(r);if(o===this.currPage){p.addClass("active")}s.append(p)}return this.$element.html(q.html(s))};i.prototype._scrollToActivePage=function l(){var p=this.$element.find(".pagination-scroll-container");if(!p.size()){return this}var o=this.$element.find("li.active"),n=p.width()/2;p.scrollLeft(p.scrollLeft()+o.position().left-n);return this};i.prototype.goToPage=function j(n){if(n<=0){n=0}if(this.numPages&&n>=this.numPages){n=this.numPages-1}if(n===this.currPage){return this}this.currPage=n;this.$element.trigger("pagination.page-change",this.currPage);this._render();return this};i.prototype.prevPage=function c(){return this.goToPage(this.currPage-1)};i.prototype.nextPage=function h(){return this.goToPage(this.currPage+1)};i.prototype.page=function f(){return this.currPage};i.create=function k(n,o){return new i(n,o)};jQuery.fn.extend({pagination:function d(o){var n=jQuery.makeArray(arguments).slice(1);if(jQuery.type(o)==="object"){return this.map(function(){i.create($(this),o);return this})}var q=$(this[0]),r=q.data(i.prototype.DATA_KEY);if(r){if(jQuery.type(o)==="string"){var p=r[o];if(jQuery.type(p)==="function"){return p.apply(r,n)}}else{return r}}return undefined}})}());(function(){var g={renameColumns:false,columnNames:[],commentChar:"#",hideCommentRows:false,includePrompts:true,topLeftContent:"Columns:"},s="peek-control.change",t="peek-control.rename",l="peek-control",v="control",f="control-prompt",c="selected",n="disabled",u="button",q="renamable-header",p="column-index",a="column-name";function i(w){if(w.disabled&&jQuery.type(w.disabled)!=="array"){throw new Error('"disabled" must be defined as an array of indeces: '+JSON.stringify(w))}if(w.multiselect&&w.selected&&jQuery.type(w.selected)!=="array"){throw new Error('Mulitselect rows need an array for "selected": '+JSON.stringify(w))}if(!w.label||!w.id){throw new Error("Peek controls need a label and id for each control row: "+JSON.stringify(w))}if(w.disabled&&w.disabled.indexOf(w.selected)!==-1){throw new Error("Selected column is in the list of disabled columns: "+JSON.stringify(w))}return w}function o(x,w){return $("<div/>").addClass(u).text(x.label)}function k(x,w){var y=$("<td/>").html(o(x,w)).attr("data-"+p,w);if(x.disabled&&x.disabled.indexOf(w)!==-1){y.addClass(n)}return y}function e(y,z,w){var x=y.children("."+u);if(y.hasClass(c)){x.html((z.selectedText!==undefined)?(z.selectedText):(z.label))}else{x.html((z.unselectedText!==undefined)?(z.unselectedText):(z.label))}}function h(z,x){var y=k(z,x);if(z.selected===x){y.addClass(c)}e(y,z,x);if(!y.hasClass(n)){y.click(function w(D){var E=$(this);if(!E.hasClass(c)){var A=E.parent().children("."+c).removeClass(c);A.each(function(){e($(this),z,x)});E.addClass(c);e(E,z,x);var C={},B=E.parent().attr("id"),F=E.data(p);C[B]=F;E.parents(".peek").trigger(s,C)}})}return y}function m(z,x){var y=k(z,x);if(z.selected&&z.selected.indexOf(x)!==-1){y.addClass(c)}e(y,z,x);if(!y.hasClass(n)){y.click(function w(D){var E=$(this);E.toggleClass(c);e(E,z,x);var C=E.parent().find("."+c).map(function(G,H){return $(H).data(p)});var B={},A=E.parent().attr("id"),F=jQuery.makeArray(C);B[A]=F;E.parents(".peek").trigger(s,B)})}return y}function j(y,z){var w=[];for(var x=0;x<y;x+=1){w.push(z.multiselect?m(z,x):h(z,x))}return w}function r(y,z,x){var A=$("<tr/>").attr("id",z.id).addClass(v);if(x){var w=$("<td/>").addClass(f).text(z.label+":");A.append(w)}A.append(j(y,z));return A}function b(E){E=jQuery.extend(true,{},g,E);var D=$(this).addClass(l),A=D.find("table"),z=A.find("th").size(),C=A.find("tr").size(),w=A.find("td[colspan]").map(function(H,F){var G=$(this);if(G.text()&&G.text().match(new RegExp("^"+E.commentChar))){return $(this).css("color","grey").parent().get(0)}return null});if(E.hideCommentRows){w.hide();C-=w.size()}if(E.includePrompts){var y=$("<th/>").addClass("top-left").text(E.topLeftContent).attr("rowspan",C);A.find("tr").first().prepend(y)}var B=A.find("th:not(.top-left)").each(function(G,I){var H=$(this),J=H.text().replace(/^\d+\.*/,""),F=E.columnNames[G]||J;H.attr("data-"+a,F).text((G+1)+((F)?("."+F):("")))});if(E.renameColumns){B.addClass(q).click(function x(){var G=$(this),F=G.index()+(E.includePrompts?0:1),I=G.data(a),H=prompt("New column name:",I);if(H!==null&&H!==I){G.text(F+(H?("."+H):"")).data(a,H).attr("data-",a,H);var J=jQuery.makeArray(G.parent().children("th:not(.top-left)").map(function(){return $(this).data(a)}));G.parents(".peek").trigger(t,J)}})}E.controls.forEach(function(G,F){i(G);var H=r(z,G,E.includePrompts);A.find("tbody").append(H)});return this}jQuery.fn.extend({peekControl:function d(w){return this.map(function(){return b.call(this,w)})}})}());
\ No newline at end of file
+var IconButton=Backbone.Model.extend({defaults:{title:"",icon_class:"",on_click:null,menu_options:null,is_menu_button:true,id:null,href:null,target:null,enabled:true,visible:true,tooltip_config:{}}});var IconButtonView=Backbone.View.extend({initialize:function(){this.model.attributes.tooltip_config={placement:"bottom"};this.model.bind("change",this.render,this)},render:function(){this.$el.tooltip("hide");var a=this.template(this.model.toJSON());a.tooltip(this.model.get("tooltip_config"));this.$el.replaceWith(a);this.setElement(a);return this},events:{click:"click"},click:function(a){if(_.isFunction(this.model.get("on_click"))){this.model.get("on_click")(a);return false}return true},template:function(b){var a='title="'+b.title+'" class="icon-button';if(b.is_menu_button){a+=" menu-button"}a+=" "+b.icon_class;if(!b.enabled){a+="_disabled"}a+='"';if(b.id){a+=' id="'+b.id+'"'}a+=' href="'+b.href+'"';if(b.target){a+=' target="'+b.target+'"'}if(!b.visible){a+=' style="display: none;"'}if(b.enabled){a="<a "+a+"/>"}else{a="<span "+a+"/>"}return $(a)}});var IconButtonCollection=Backbone.Collection.extend({model:IconButton});var IconButtonMenuView=Backbone.View.extend({tagName:"div",initialize:function(){this.render()},render:function(){var a=this;this.collection.each(function(d){var b=$("<a/>").attr("href","javascript:void(0)").attr("title",d.attributes.title).addClass("icon-button menu-button").addClass(d.attributes.icon_class).appendTo(a.$el).click(d.attributes.on_click);if(d.attributes.tooltip_config){b.tooltip(d.attributes.tooltip_config)}var c=d.get("options");if(c){make_popupmenu(b,c)}});return this}});var create_icon_buttons_menu=function(b,a){if(!a){a={}}var c=new IconButtonCollection(_.map(b,function(d){return new IconButton(_.extend(d,a))}));return new IconButtonMenuView({collection:c})};var Grid=Backbone.Collection.extend({});var GridView=Backbone.View.extend({});var PopupMenu=Backbone.View.extend({initialize:function(b,a){this.$button=b;if(!this.$button.size()){this.$button=$("<div/>")}this.options=a||[];var c=this;this.$button.click(function(d){$(".popmenu-wrapper").remove();c._renderAndShow(d);return false})},_renderAndShow:function(a){this.render();this.$el.appendTo("body").css(this._getShownPosition(a)).show();this._setUpCloseBehavior()},render:function(){this.$el.addClass("popmenu-wrapper").hide().css({position:"absolute"}).html(this.template(this.$button.attr("id"),this.options));if(this.options.length){var a=this;this.$el.find("li").each(function(c,b){var d=a.options[c];if(d.func){$(this).children("a.popupmenu-option").click(function(e){d.func.call(a,e,d)})}})}return this},template:function(b,a){return['<ul id="',b,'-menu" class="dropdown-menu">',this._templateOptions(a),"</ul>"].join("")},_templateOptions:function(a){if(!a.length){return"<li>(no options)</li>"}return _.map(a,function(d){if(d.divider){return'<li class="divider"></li>'}else{if(d.header){return['<li class="head"><a href="javascript:void(0);">',d.html,"</a></li>"].join("")}}var c=d.href||"javascript:void(0);",e=(d.target)?(' target="'+d.target+'"'):(""),b=(d.checked)?('<span class="fa fa-check"></span>'):("");return['<li><a class="popupmenu-option" href="',c,'"',e,">",b,d.html,"</a></li>"].join("")}).join("")},_getShownPosition:function(b){var c=this.$el.width();var a=b.pageX-c/2;a=Math.min(a,$(document).scrollLeft()+$(window).width()-c-5);a=Math.max(a,$(document).scrollLeft()+5);return{top:b.pageY,left:a}},_setUpCloseBehavior:function(){var c=this;function a(e){$(document).off("click.close_popup");if(window.parent!==window){try{$(window.parent.document).off("click.close_popup")}catch(d){}}else{try{$("iframe#galaxy_main").contents().off("click.close_popup")}catch(d){}}c.remove()}$("html").one("click.close_popup",a);if(window.parent!==window){try{$(window.parent.document).find("html").one("click.close_popup",a)}catch(b){}}else{try{$("iframe#galaxy_main").contents().one("click.close_popup",a)}catch(b){}}},addItem:function(b,a){a=(a>=0)?a:this.options.length;this.options.splice(a,0,b);return this},removeItem:function(a){if(a>=0){this.options.splice(a,1)}return this},findIndexByHtml:function(b){for(var a=0;a<this.options.length;a++){if(_.has(this.options[a],"html")&&(this.options[a].html===b)){return a}}return null},findItemByHtml:function(a){return this.options[(this.findIndexByHtml(a))]},toString:function(){return"PopupMenu"}});PopupMenu.create=function _create(b,a){return new PopupMenu(b,a)};PopupMenu.make_popupmenu=function(b,c){var a=[];_.each(c,function(f,d){var e={html:d};if(f===null){e.header=true}else{if(jQuery.type(f)==="function"){e.func=f}}a.push(e)});return new PopupMenu($(b),a)};PopupMenu.convertLinksToOptions=function(c,a){c=$(c);a=a||"a";var b=[];c.find(a).each(function(g,e){var f={},d=$(g);f.html=d.text();if(d.attr("href")){var j=d.attr("href"),k=d.attr("target"),h=d.attr("confirm");f.func=function(){if((h)&&(!confirm(h))){return}switch(k){case"_parent":window.parent.location=j;break;case"_top":window.top.location=j;break;default:window.location=j}}}b.push(f)});return b};PopupMenu.fromExistingDom=function(d,c,a){d=$(d);c=$(c);var b=PopupMenu.convertLinksToOptions(c,a);c.remove();return new PopupMenu(d,b)};PopupMenu.make_popup_menus=function(c,b,d){c=c||document;b=b||"div[popupmenu]";d=d||function(e,f){return"#"+e.attr("popupmenu")};var a=[];$(c).find(b).each(function(){var e=$(this),f=$(c).find(d(e,c));a.push(PopupMenu.fromDom(f,e));f.addClass("popup")});return a};var faIconButton=function(a){a=a||{};a.tooltipConfig=a.tooltipConfig||{placement:"bottom"};a.classes=["icon-btn"].concat(a.classes||[]);if(a.disabled){a.classes.push("disabled")}var b=['<a class="',a.classes.join(" "),'"',((a.title)?(' title="'+a.title+'"'):("")),((!a.disabled&&a.target)?(' target="'+a.target+'"'):("")),' href="',((!a.disabled&&a.href)?(a.href):("javascript:void(0);")),'">','<span class="fa ',a.faIcon,'"></span>',"</a>"].join("");var c=$(b).tooltip(a.tooltipConfig);if(_.isFunction(a.onclick)){c.click(a.onclick)}return c};function LoadingIndicator(a,c){var b=this;c=jQuery.extend({cover:false},c||{});function d(){var e=['<div class="loading-indicator">','<div class="loading-indicator-text">','<span class="fa fa-spinner fa-spin fa-lg"></span>','<span class="loading-indicator-message">loading...</span>',"</div>","</div>"].join("\n");var g=$(e).hide().css(c.css||{position:"fixed"}),f=g.children(".loading-indicator-text");if(c.cover){g.css({"z-index":2,top:a.css("top"),bottom:a.css("bottom"),left:a.css("left"),right:a.css("right"),opacity:0.5,"background-color":"white","text-align":"center"});f=g.children(".loading-indicator-text").css({"margin-top":"20px"})}else{f=g.children(".loading-indicator-text").css({margin:"12px 0px 0px 10px",opacity:"0.85",color:"grey"});f.children(".loading-indicator-message").css({margin:"0px 8px 0px 0px","font-style":"italic"})}return g}b.show=function(f,e,g){f=f||"loading...";e=e||"fast";a.parent().find(".loading-indicator").remove();b.$indicator=d().insertBefore(a);b.message(f);b.$indicator.fadeIn(e,g);return b};b.message=function(e){b.$indicator.find("i").text(e)};b.hide=function(e,f){e=e||"fast";if(b.$indicator&&b.$indicator.size()){b.$indicator.fadeOut(e,function(){b.$indicator.remove();if(f){f()}})}else{if(f){f()}}return b};return b}(function(){var b=window._l||function(d){return d};function a(k,q){var e=27,n=13,d=$(k),f=true,h={initialVal:"",name:"search",placeholder:"search",classes:"",onclear:function(){},onfirstsearch:null,onsearch:function(r){},minSearchLen:0,escWillClear:true,oninit:function(){}};function j(r){var s=$(this).parent().children("input");s.focus().val("").trigger("clear:searchInput");q.onclear()}function p(s,r){$(this).trigger("search:searchInput",r);if(typeof q.onfirstsearch==="function"&&f){f=false;q.onfirstsearch(r)}else{q.onsearch(r)}}function g(){return['<input type="text" name="',q.name,'" placeholder="',q.placeholder,'" ','class="search-query ',q.classes,'" ',"/>"].join("")}function m(){return $(g()).focus(function(r){$(this).select()}).keyup(function(s){s.preventDefault();s.stopPropagation();if(!$(this).val()){$(this).blur()}if(s.which===e&&q.escWillClear){j.call(this,s)}else{var r=$(this).val();if((s.which===n)||(q.minSearchLen&&r.length>=q.minSearchLen)){p.call(this,s,r)}else{if(!r.length){j.call(this,s)}}}}).on("change",function(r){p.call(this,r,$(this).val())}).val(q.initialVal)}function l(){return $(['<span class="search-clear fa fa-times-circle" ','title="',b("clear search (esc)"),'"></span>'].join("")).tooltip({placement:"bottom"}).click(function(r){j.call(this,r)})}function o(){return $(['<span class="search-loading fa fa-spinner fa-spin" ','title="',b("loading..."),'"></span>'].join("")).hide().tooltip({placement:"bottom"})}function i(){d.find(".search-loading").toggle();d.find(".search-clear").toggle()}if(jQuery.type(q)==="string"){if(q==="toggle-loading"){i()}return d}if(jQuery.type(q)==="object"){q=jQuery.extend(true,{},h,q)}return d.addClass("search-input").prepend([m(),l(),o()])}jQuery.fn.extend({searchInput:function c(d){return this.each(function(){return a(this,d)})}})}());(function(){function c(o,n){this.currModeIndex=0;return this._init(o,n)}c.prototype.DATA_KEY="mode-button";c.prototype.defaults={switchModesOnClick:true};c.prototype._init=function j(o,n){n=n||{};this.$element=$(o);this.options=jQuery.extend(true,{},this.defaults,n);if(!n.modes){throw new Error('ModeButton requires a "modes" array')}var q=this;this.$element.click(function p(r){q.callModeFn();if(q.options.switchModesOnClick){q._incModeIndex()}$(this).html(q.options.modes[q.currModeIndex].html)});return this.reset()};c.prototype._incModeIndex=function l(){this.currModeIndex+=1;if(this.currModeIndex>=this.options.modes.length){this.currModeIndex=0}return this};c.prototype._getModeIndex=function f(n){for(var o=0;o<this.options.modes.length;o+=1){if(this.options.modes[o].mode===n){return o}}throw new Error("mode not found: "+n)};c.prototype._setModeByIndex=function m(n){var o=this.options.modes[n];if(!o){throw new Error("mode index not found: "+n)}this.currModeIndex=n;if(o.html){this.$element.html(o.html)}return this};c.prototype.currentMode=function d(){return this.options.modes[this.currModeIndex]};c.prototype.current=function i(){return this.currentMode().mode};c.prototype.getMode=function g(n){if(!n){return this.currentMode()}return this.options.modes[(this._getModeIndex(n))]};c.prototype.hasMode=function b(n){try{return !!this.getMode(n)}catch(o){}return false};c.prototype.setMode=function a(n){return this._setModeByIndex(this._getModeIndex(n))};c.prototype.reset=function h(){this.currModeIndex=0;if(this.options.initialMode){this.currModeIndex=this._getModeIndex(this.options.initialMode)}return this._setModeByIndex(this.currModeIndex)};c.prototype.callModeFn=function e(n){var o=this.getMode(n).onclick;if(o&&jQuery.type(o==="function")){return o.call(this.$element.get(0))}return undefined};jQuery.fn.extend({modeButton:function k(n){if(!this.size()){return this}if(jQuery.type(n)==="object"){return this.map(function(){var r=$(this);r.data("mode-button",new c(r,n));return this})}var p=$(this[0]),o=p.data("mode-button");if(!o){throw new Error("modeButton needs an options object or string name of a function")}if(o&&jQuery.type(n)==="string"){var q=n;if(o&&jQuery.type(o[q])==="function"){return o[q].apply(o,jQuery.makeArray(arguments).slice(1))}}return o}})}());function dropDownSelect(b,c){c=c||((!_.isEmpty(b))?(b[0]):(""));var a=$(['<div class="dropdown-select btn-group">','<button type="button" class="btn btn-default">','<span class="dropdown-select-selected">'+c+"</span>","</button>","</div>"].join("\n"));if(b&&b.length>1){a.find("button").addClass("dropdown-toggle").attr("data-toggle","dropdown").append(' <span class="caret"></span>');a.append(['<ul class="dropdown-menu" role="menu">',_.map(b,function(e){return['<li><a href="javascript:void(0)">',e,"</a></li>"].join("")}).join("\n"),"</ul>"].join("\n"))}function d(g){var h=$(this),f=h.parents(".dropdown-select"),e=h.text();f.find(".dropdown-select-selected").text(e);f.trigger("change.dropdown-select",e)}a.find("a").click(d);return a}(function(){function e(k,j){return this.init(k,j)}e.prototype.DATA_KEY="filter-control";e.prototype.init=function g(k,j){j=j||{filters:[]};this.$element=$(k).addClass("filter-control btn-group");this.options=jQuery.extend(true,{},this.defaults,j);this.currFilter=this.options.filters[0];return this.render()};e.prototype.render=function d(){this.$element.empty().append([this._renderKeySelect(),this._renderOpSelect(),this._renderValueInput()]);return this};e.prototype._renderKeySelect=function a(){var j=this;var k=this.options.filters.map(function(l){return l.key});this.$keySelect=dropDownSelect(k,this.currFilter.key).addClass("filter-control-key").on("change.dropdown-select",function(m,l){j.currFilter=_.findWhere(j.options.filters,{key:l});j.render()._triggerChange()});return this.$keySelect};e.prototype._renderOpSelect=function i(){var j=this,k=this.currFilter.ops;this.$opSelect=dropDownSelect(k,k[0]).addClass("filter-control-op").on("change.dropdown-select",function(m,l){j._triggerChange()});return this.$opSelect};e.prototype._renderValueInput=function c(){var j=this;if(this.currFilter.values){this.$valueSelect=dropDownSelect(this.currFilter.values,this.currFilter.values[0]).on("change.dropdown-select",function(l,k){j._triggerChange()})}else{this.$valueSelect=$("<input/>").addClass("form-control").on("change",function(k,l){j._triggerChange()})}this.$valueSelect.addClass("filter-control-value");return this.$valueSelect};e.prototype.val=function b(){var k=this.$element.find(".filter-control-key .dropdown-select-selected").text(),m=this.$element.find(".filter-control-op .dropdown-select-selected").text(),j=this.$element.find(".filter-control-value"),l=(j.hasClass("dropdown-select"))?(j.find(".dropdown-select-selected").text()):(j.val());return{key:k,op:m,value:l}};e.prototype._triggerChange=function h(){this.$element.trigger("change.filter-control",this.val())};jQuery.fn.extend({filterControl:function f(k){var j=jQuery.makeArray(arguments).slice(1);return this.map(function(){var n=$(this),m=n.data(e.prototype.DATA_KEY);if(jQuery.type(k)==="object"){m=new e(n,k);n.data(e.prototype.DATA_KEY,m)}if(m&&jQuery.type(k)==="string"){var l=m[k];if(jQuery.type(l)==="function"){return l.apply(m,j)}}return this})}})}());(function(){function i(o,n){this.numPages=null;this.currPage=0;return this.init(o,n)}i.prototype.DATA_KEY="pagination";i.prototype.defaults={startingPage:0,perPage:20,totalDataSize:null,currDataSize:null};i.prototype.init=function g(n,o){o=o||{};this.$element=n;this.options=jQuery.extend(true,{},this.defaults,o);this.currPage=this.options.startingPage;if(this.options.totalDataSize!==null){this.numPages=Math.ceil(this.options.totalDataSize/this.options.perPage);if(this.currPage>=this.numPages){this.currPage=this.numPages-1}}this.$element.data(i.prototype.DATA_KEY,this);this._render();return this};function m(n){return $(['<li><a href="javascript:void(0);">',n,"</a></li>"].join(""))}i.prototype._render=function e(){if(this.options.totalDataSize===0){return this}if(this.numPages===1){return this}if(this.numPages>0){this._renderPages();this._scrollToActivePage()}else{this._renderPrevNext()}return this};i.prototype._renderPrevNext=function b(){var o=this,p=m("Prev"),n=m("Next"),q=$("<ul/>").addClass("pagination pagination-prev-next");if(this.currPage===0){p.addClass("disabled")}else{p.click(function(){o.prevPage()})}if((this.numPages&&this.currPage===(this.numPages-1))||(this.options.currDataSize&&this.options.currDataSize<this.options.perPage)){n.addClass("disabled")}else{n.click(function(){o.nextPage()})}this.$element.html(q.append([p,n]));return this.$element};i.prototype._renderPages=function a(){var n=this,q=$("<div>").addClass("pagination-scroll-container"),s=$("<ul/>").addClass("pagination pagination-page-list"),r=function(t){n.goToPage($(this).data("page"))};for(var o=0;o<this.numPages;o+=1){var p=m(o+1).attr("data-page",o).click(r);if(o===this.currPage){p.addClass("active")}s.append(p)}return this.$element.html(q.html(s))};i.prototype._scrollToActivePage=function l(){var p=this.$element.find(".pagination-scroll-container");if(!p.size()){return this}var o=this.$element.find("li.active"),n=p.width()/2;p.scrollLeft(p.scrollLeft()+o.position().left-n);return this};i.prototype.goToPage=function j(n){if(n<=0){n=0}if(this.numPages&&n>=this.numPages){n=this.numPages-1}if(n===this.currPage){return this}this.currPage=n;this.$element.trigger("pagination.page-change",this.currPage);this._render();return this};i.prototype.prevPage=function c(){return this.goToPage(this.currPage-1)};i.prototype.nextPage=function h(){return this.goToPage(this.currPage+1)};i.prototype.page=function f(){return this.currPage};i.create=function k(n,o){return new i(n,o)};jQuery.fn.extend({pagination:function d(o){var n=jQuery.makeArray(arguments).slice(1);if(jQuery.type(o)==="object"){return this.map(function(){i.create($(this),o);return this})}var q=$(this[0]),r=q.data(i.prototype.DATA_KEY);if(r){if(jQuery.type(o)==="string"){var p=r[o];if(jQuery.type(p)==="function"){return p.apply(r,n)}}else{return r}}return undefined}})}());
\ No newline at end of file
diff -r bfc3b5e52781683efc8024f18c4aa57c65af1e15 -r ee995df4a7e548befdfa09196b9665f05cfd55f6 static/style/blue/base.css
--- a/static/style/blue/base.css
+++ b/static/style/blue/base.css
@@ -1648,17 +1648,6 @@
.pagination-scroll-container .pagination-page-list>li:first-child>a,.pagination-scroll-container .pagination-page-list>li:first-child>span{border-radius:0px;border-left:0px}
.pagination-scroll-container .pagination-page-list>li:last-child>a,.pagination-scroll-container .pagination-page-list>li:last-child>span{border-radius:0px}
.pagination-scroll-container .pagination-page-list>li>a{float:none;position:static;border:1px solid #BFBFBF;border-width:0px 0px 0px 1px}
-.peek-control{border-radius:3px;border:1px solid #5f6990}
-.peek-control td,.peek-control th{padding:4px 10px 4px 4px}
-.peek-control th:last-child{width:100%}
-.peek-control .top-left{width:10%;white-space:normal;vertical-align:top;text-align:right;font-family:"Lucida Grande",verdana,arial,helvetica,sans-serif;font-weight:normal}
-.peek-control .renamable-header:hover{background-color:black}
-.peek-control .control td.control-prompt{background-color:#5f6990;padding:0px 4px 0px 8px;text-align:right;color:white}
-.peek-control .control td{padding:1px;font-family:"Lucida Grande",verdana,arial,helvetica,sans-serif;color:grey}
-.peek-control .control td .button{min-width:28px;border:1px solid grey;border-radius:3px;padding:4px;color:grey}
-.peek-control .control td:hover .button{background-color:#EEE;border:1px solid black;cursor:pointer;color:black}
-.peek-control .control td.disabled .button,.peek-control .control td.disabled:hover .button{background-color:transparent;border:1px solid #CCC;cursor:not-allowed;color:#CCC}
-.peek-control .control td.selected .button{background-color:black;border:1px solid black;color:white}
div.metadataForm{border:solid #aaaaaa 1px}
div.metadataFormTitle{font-weight:bold;padding:5px;padding-left:10px;padding-right:10px;background:#cccccc;background-repeat:repeat-x;background-position:top;border-bottom:solid #aaaaaa 1px}
div.metadataFormBody{background:#FFFFFF;padding:5px 0}
@@ -2112,6 +2101,17 @@
.dataset-choice.multi table th:not(:last-child),.dataset-choice.multi table td:not(:last-child){padding-right:8px}
.dataset-choice.multi table td.cell-name{font-weight:bold}
.dataset-choice-modal .list-panel .controls .title .name{font-size:120%}
+.peek-column-selector{border-radius:3px;border:1px solid #5f6990}
+.peek-column-selector td,.peek-column-selector th{padding:4px 10px 4px 4px}
+.peek-column-selector th:last-child{width:100%}
+.peek-column-selector .top-left{width:10%;white-space:normal;vertical-align:top;text-align:right;font-family:"Lucida Grande",verdana,arial,helvetica,sans-serif;font-weight:normal}
+.peek-column-selector .renamable-header:hover{background-color:black}
+.peek-column-selector .control td.control-prompt{background-color:#5f6990;padding:0px 4px 0px 8px;text-align:right;color:white}
+.peek-column-selector .control td{padding:1px;font-family:"Lucida Grande",verdana,arial,helvetica,sans-serif;color:grey}
+.peek-column-selector .control td .button{min-width:28px;border:1px solid grey;border-radius:3px;padding:4px;color:grey}
+.peek-column-selector .control td:hover .button{background-color:#EEE;border:1px solid black;cursor:pointer;color:black}
+.peek-column-selector .control td.disabled .button,.peek-column-selector .control td.disabled:hover .button{background-color:transparent;border:1px solid #CCC;cursor:not-allowed;color:#CCC}
+.peek-column-selector .control td.selected .button{background-color:black;border:1px solid black;color:white}
.toolMenuContainer{color:#000;background:#dfe5f9;min-height:100%;padding:5px 10px}
div.toolSectionPad{margin:0;padding:0;height:5px;font-size:0px}
div.toolSectionWrapper{margin-bottom:5px}
diff -r bfc3b5e52781683efc8024f18c4aa57c65af1e15 -r ee995df4a7e548befdfa09196b9665f05cfd55f6 static/style/src/less/base.less
--- a/static/style/src/less/base.less
+++ b/static/style/src/less/base.less
@@ -545,77 +545,6 @@
}
-// ---------------------------------------------------------------------------- peek-based column chooser
-.peek-control {
- border-radius: 3px;
- border: 1px solid rgb(95, 105, 144);
-}
-
-.peek-control td,
-.peek-control th {
- padding: 4px 10px 4px 4px;
-}
-
-.peek-control th:last-child {
- width: 100%;
-}
-
-.peek-control .top-left {
- width: 10%;
- white-space: normal;
- vertical-align: top;
- text-align: right;
- font-family: "Lucida Grande", verdana, arial, helvetica, sans-serif;
- font-weight: normal;
-}
-
-.peek-control .renamable-header:hover {
- background-color: black;
-}
-
-.peek-control .control td.control-prompt {
- background-color: rgb(95, 105, 144);
- padding: 0px 4px 0px 8px;
- text-align: right;
- color: white;
-}
-
-.peek-control .control td {
- padding: 1px;
- font-family: "Lucida Grande", verdana, arial, helvetica, sans-serif;
- color: grey;
-}
-
-.peek-control .control td .button {
- min-width: 28px;
- border: 1px solid grey;
- border-radius: 3px;
- padding: 4px;
- color: grey;
-}
-
-.peek-control .control td:hover .button {
- background-color: #EEE;
- border: 1px solid black;
- cursor: pointer;
- color: black;
-}
-
-.peek-control .control td.disabled .button,
-.peek-control .control td.disabled:hover .button {
- background-color: transparent;
- border: 1px solid #CCC;
- cursor: not-allowed;
- color: #CCC;
-}
-
-.peek-control .control td.selected .button {
- background-color: black;
- border: 1px solid black;
- color: white;
-}
-
-
// ==== Tool form styles ====
div.metadataForm {
@@ -1565,6 +1494,7 @@
@import "ui/paired-collection-creator.less";
@import "ui/dataset-choice.less";
+@import "ui/peek-column-selector.less";
// ==== Tool menu styles
diff -r bfc3b5e52781683efc8024f18c4aa57c65af1e15 -r ee995df4a7e548befdfa09196b9665f05cfd55f6 static/style/src/less/ui/peek-column-selector.less
--- /dev/null
+++ b/static/style/src/less/ui/peek-column-selector.less
@@ -0,0 +1,69 @@
+// peek-based column chooser, see: scripts/ui/peek-column-selector.js
+.peek-column-selector {
+ border-radius: 3px;
+ border: 1px solid rgb(95, 105, 144);
+}
+
+.peek-column-selector td,
+.peek-column-selector th {
+ padding: 4px 10px 4px 4px;
+}
+
+.peek-column-selector th:last-child {
+ width: 100%;
+}
+
+.peek-column-selector .top-left {
+ width: 10%;
+ white-space: normal;
+ vertical-align: top;
+ text-align: right;
+ font-family: "Lucida Grande", verdana, arial, helvetica, sans-serif;
+ font-weight: normal;
+}
+
+.peek-column-selector .renamable-header:hover {
+ background-color: black;
+}
+
+.peek-column-selector .control td.control-prompt {
+ background-color: rgb(95, 105, 144);
+ padding: 0px 4px 0px 8px;
+ text-align: right;
+ color: white;
+}
+
+.peek-column-selector .control td {
+ padding: 1px;
+ font-family: "Lucida Grande", verdana, arial, helvetica, sans-serif;
+ color: grey;
+}
+
+.peek-column-selector .control td .button {
+ min-width: 28px;
+ border: 1px solid grey;
+ border-radius: 3px;
+ padding: 4px;
+ color: grey;
+}
+
+.peek-column-selector .control td:hover .button {
+ background-color: #EEE;
+ border: 1px solid black;
+ cursor: pointer;
+ color: black;
+}
+
+.peek-column-selector .control td.disabled .button,
+.peek-column-selector .control td.disabled:hover .button {
+ background-color: transparent;
+ border: 1px solid #CCC;
+ cursor: not-allowed;
+ color: #CCC;
+}
+
+.peek-column-selector .control td.selected .button {
+ background-color: black;
+ border: 1px solid black;
+ color: white;
+}
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
commit/galaxy-central: dannon: Merged in guerler/guerler-galaxy-central/stable (pull request #598)
by commits-noreply@bitbucket.org 08 Dec '14
by commits-noreply@bitbucket.org 08 Dec '14
08 Dec '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/704efd2f5e06/
Changeset: 704efd2f5e06
Branch: stable
User: dannon
Date: 2014-12-08 19:11:14+00:00
Summary: Merged in guerler/guerler-galaxy-central/stable (pull request #598)
Security fixes for assigned templates
Affected #: 12 files
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a client/galaxy/scripts/mvc/grid/grid-template.js
--- a/client/galaxy/scripts/mvc/grid/grid-template.js
+++ b/client/galaxy/scripts/mvc/grid/grid-template.js
@@ -1,5 +1,5 @@
// dependencies
-define([], function() {
+define(['utils/utils'], function(Utils) {
// grid view templates
return {
@@ -605,6 +605,7 @@
// template for filter items
filter_element: function(filter_key, filter_value) {
+ filter_value = Utils.sanitize(filter_value);
return '<span class="text-filter-val">' + filter_value +
'<a href="javascript:void(0);" filter_key="' + filter_key + '" filter_val="' + filter_value + '">' +
'<i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/>' +
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a client/galaxy/scripts/utils/utils.js
--- a/client/galaxy/scripts/utils/utils.js
+++ b/client/galaxy/scripts/utils/utils.js
@@ -6,6 +6,14 @@
// dependencies
define(["libs/underscore"], function(_) {
+/**
+ * Sanitize/escape a string
+ * @param{String} content - Content to be sanitized
+ */
+function sanitize(content) {
+ return $('<div/>').text(content).html();
+};
+
// generic function to recieve json from url
function get (url, success, error) {
request('GET', url, {}, success, error);
@@ -151,7 +159,8 @@
uuid: uuid,
time: time,
wrap: wrap,
- request: request
+ request: request,
+ sanitize: sanitize
};
});
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a lib/galaxy/web/framework/helpers/grids.py
--- a/lib/galaxy/web/framework/helpers/grids.py
+++ b/lib/galaxy/web/framework/helpers/grids.py
@@ -8,6 +8,7 @@
from galaxy.web.framework import decorators
from galaxy.web.framework import url_for
from galaxy.web.framework.helpers import iff
+from markupsafe import escape
from sqlalchemy.sql.expression import and_, func, or_
@@ -362,7 +363,7 @@
value = None
if self.format:
value = self.format( value )
- return value
+ return escape(value)
def get_link( self, trans, grid, item ):
if self.link and self.link( item ):
return self.link( item )
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a lib/galaxy/webapps/galaxy/controllers/page.py
--- a/lib/galaxy/webapps/galaxy/controllers/page.py
+++ b/lib/galaxy/webapps/galaxy/controllers/page.py
@@ -8,6 +8,7 @@
from galaxy import util
from galaxy.util.sanitize_html import sanitize_html, _BaseHTMLProcessor
from galaxy.util.json import loads
+from markupsafe import escape
def format_bool( b ):
if b:
@@ -89,9 +90,9 @@
class NameColumn( grids.TextColumn ):
def get_value(self, trans, grid, item):
if hasattr( item, "get_display_name" ):
- return item.get_display_name()
+ return escape(item.get_display_name())
else:
- return item.name
+ return escape(item.name)
# Grid definition.
show_item_checkboxes = True
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a static/scripts/mvc/grid/grid-template.js
--- a/static/scripts/mvc/grid/grid-template.js
+++ b/static/scripts/mvc/grid/grid-template.js
@@ -1,5 +1,5 @@
// dependencies
-define([], function() {
+define(['utils/utils'], function(Utils) {
// grid view templates
return {
@@ -605,6 +605,7 @@
// template for filter items
filter_element: function(filter_key, filter_value) {
+ filter_value = Utils.sanitize(filter_value);
return '<span class="text-filter-val">' + filter_value +
'<a href="javascript:void(0);" filter_key="' + filter_key + '" filter_val="' + filter_value + '">' +
'<i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/>' +
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a static/scripts/packed/mvc/grid/grid-template.js
--- a/static/scripts/packed/mvc/grid/grid-template.js
+++ b/static/scripts/packed/mvc/grid/grid-template.js
@@ -1,1 +1,1 @@
-define([],function(){return{grid:function(b){var a="";if(b.embedded){a=this.grid_header(b)+this.grid_table(b)}else{a='<div class="loading-elt-overlay"></div><table><tr><td width="75%">'+this.grid_header(b)+'</td><td></td><td></td></tr><tr><td width="100%" id="grid-message" valign="top"></td><td></td><td></td></tr></table>'+this.grid_table(b)}if(b.info_text){a+='<br><div class="toolParamHelp" style="clear: both;">'+b.info_text+"</div>"}return a},grid_table:function(a){return'<form method="post" onsubmit="return false;"><table id="grid-table" class="grid"><thead id="grid-table-header"></thead><tbody id="grid-table-body"></tbody><tfoot id="grid-table-footer"></tfoot></table></form>'},grid_header:function(c){var b='<div class="grid-header">';if(!c.embedded){b+="<h2>"+c.title+"</h2>"}if(c.global_actions){b+='<ul class="manage-table-actions">';var d=(c.global_actions.length>=3);if(d){b+='<li><a class="action-button" id="popup-global-actions" class="menubutton">Actions</a></li><div popupmenu="popup-global-actions">'}for(i in c.global_actions){var e=c.global_actions[i];var a="";if(e.inbound){a="use-inbound"}else{a="use-outbound"}b+='<li><a class="action-button '+a+'" href="'+e.url_args+'" onclick="return false;">'+e.label+"</a></li>"}if(d){b+="</div>"}b+="</ul>"}if(c.insert){b+=c.insert}b+=this.grid_filters(c);b+="</div>";return b},header:function(b){var a="<tr>";if(b.show_item_checkboxes){a+="<th>";if(b.items.length>0){a+='<input type="checkbox" id="check_all" name=select_all_checkbox value="true"><input type="hidden" name=select_all_checkbox value="true">'}a+="</th>"}for(var c in b.columns){var d=b.columns[c];if(d.visible){a+='<th id="'+d.key+'-header">';if(d.href){a+='<a href="'+d.href+'" class="sort-link" sort_key="'+d.key+'">'+d.label+"</a>"}else{a+=d.label}a+='<span class="sort-arrow">'+d.extra+"</span></th>"}}a+="</tr>";return a},body:function(r){var k="";var s=0;var e=r.items.length;if(e==0){k+='<tr><td colspan="100"><em>No Items</em></td></tr>';s=1}for(var f in r.items){var p=r.items[f];var a=p.encode_id;var g="grid-"+f+"-popup";k+="<tr ";if(r.current_item_id==p.id){k+='class="current"'}k+=">";if(r.show_item_checkboxes){k+='<td style="width: 1.5em;"><input type="checkbox" name="id" value="'+a+'" id="'+a+'" class="grid-row-select-checkbox" /></td>'}for(j in r.columns){var d=r.columns[j];if(d.visible){var c="";if(d.nowrap){c='style="white-space:nowrap;"'}var q=p.column_config[d.label];var h=q.link;var l=q.value;var o=q.inbound;if(jQuery.type(l)==="string"){l=l.replace(/\/\//g,"/")}var b="";var n="";if(d.attach_popup){b="grid-"+f+"-popup";n="menubutton";if(h!=""){n+=" split"}n+=" popup"}k+="<td "+c+">";if(h){if(r.operations.length!=0){k+='<div id="'+b+'" class="'+n+'" style="float: left;">'}var m="";if(o){m="use-inbound"}else{m="use-outbound"}k+='<a class="label '+m+'" href="'+h+'" onclick="return false;">'+l+"</a>";if(r.operations.length!=0){k+="</div>"}}else{k+='<div id="'+b+'" class="'+n+'"><label id="'+d.label_id_prefix+a+'" for="'+a+'">'+(l||"")+"</label></div>"}k+="</td>"}}k+="</tr>";s++}return k},footer:function(o){var k="";if(o.use_paging&&o.num_pages>1){var m=o.num_page_links;var a=o.cur_page_num;var n=o.num_pages;var h=m/2;var g=a-h;var e=0;if(g<=0){g=1;e=h-(a-g)}var d=h+e;var c=a+d;if(c<=n){max_offset=0}else{c=n;max_offset=d-(c+1-a)}if(max_offset!=0){g-=max_offset;if(g<1){g=1}}k+='<tr id="page-links-row">';if(o.show_item_checkboxes){k+="<td></td>"}k+='<td colspan="100"><span id="page-link-container">Page:';if(g>1){k+='<span class="page-link" id="page-link-1"><a href="javascript:void(0);" page_num="1" onclick="return false;">1</a></span> ...'}for(var l=g;l<c+1;l++){if(l==o.cur_page_num){k+='<span class="page-link inactive-link" id="page-link-'+l+'">'+l+"</span>"}else{k+='<span class="page-link" id="page-link-'+l+'"><a href="javascript:void(0);" onclick="return false;" page_num="'+l+'">'+l+"</a></span>"}}if(c<n){k+='...<span class="page-link" id="page-link-'+n+'"><a href="javascript:void(0);" onclick="return false;" page_num="'+n+'">'+n+"</a></span>"}k+="</span>";k+='<span class="page-link" id="show-all-link-span"> | <a href="javascript:void(0);" onclick="return false;" page_num="all">Show All</a></span></td></tr>'}if(o.show_item_checkboxes){k+='<tr><input type="hidden" id="operation" name="operation" value=""><td></td><td colspan="100">For <span class="grid-selected-count"></span> selected '+o.get_class_plural+": ";for(i in o.operations){var b=o.operations[i];if(b.allow_multiple){k+='<input type="button" value="'+b.label+'" class="operation-button action-button"> '}}k+="</td></tr>"}var f=false;for(i in o.operations){if(o.operations[i].global_operation){f=true;break}}if(f){k+='<tr><td colspan="100">';for(i in o.operations){var b=o.operations[i];if(b.global_operation){k+='<a class="action-button" href="'+b.global_operation+'">'+b.label+"</a>"}}k+="</td></tr>"}if(o.legend){k+='<tr><td colspan="100">'+o.legend+"</td></tr>"}return k},message:function(a){return'<p><div class="'+a.status+'message transient-message">'+a.message+'</div><div style="clear: both"></div></p>'},grid_filters:function(n){var a=n.default_filter_dict;var b=n.filters;var e="none";if(n.advanced_search){e="block"}var m=false;for(var g in n.columns){var d=n.columns[g];if(d.filterable=="advanced"){var l=d.key;var c=b[l];var f=a[l];if(c&&f&&c!=f){e="block"}m=true}}var k="block";if(e=="block"){k="none"}var h='<div id="standard-search" style="display: '+k+';"><table><tr><td style="padding: 0;"><table>';for(var g in n.columns){var d=n.columns[g];if(d.filterable=="standard"){h+=this.grid_column_filter(n,d)}}h+="</table></td></tr><tr><td>";if(m){h+='<a href="" class="advanced-search-toggle">Advanced Search</a>'}h+="</td></tr></table></div>";h+='<div id="advanced-search" style="display: '+e+'; margin-top: 5px; border: 1px solid #ccc;"><table><tr><td style="text-align: left" colspan="100"><a href="" class="advanced-search-toggle">Close Advanced Search</a></td></tr>';for(var g in n.columns){var d=n.columns[g];if(d.filterable=="advanced"){h+=this.grid_column_filter(n,d)}}h+="</table></div>";return h},grid_column_filter:function(e,c){var t=e.default_filter_dict;var l=e.filters;var a=c.label;var b=c.key;if(c.filterable=="advanced"){a=a.toLowerCase()}var k="<tr>";if(c.filterable=="advanced"){k+='<td align="left" style="padding-left: 10px">'+a+":</td>"}k+='<td style="padding-bottom: 1px;">';if(c.is_text){k+='<form class="text-filter-form" column_key="'+b+'" action="'+e.url+'" method="get" >';for(u in e.columns){var g=e.columns[u];var p=l[g.key];if(p){if(p!="All"){if(g.is_text){p=JSON.stringify(p)}k+='<input type="hidden" id="'+g.key+'" name="f-'+g.key+'" value="'+p+'"/>'}}}k+='<span id="'+b+'-filtering-criteria">';var h=l[b];if(h){var f=jQuery.type(h);if(f=="string"){if(h!="All"){k+=this.filter_element(b,h)}}if(f=="array"){for(var u in h){var n=h[u];var v=h;v=v.slice(u);k+=this.filter_element(b,n)}}}k+="</span>";var r="";if(c.filterable=="standard"){r=c.label.toLowerCase();var q=r.length;if(q<20){q=20}q=q+4}k+='<span class="search-box"><input class="search-box-input" id="input-'+b+'-filter" name="f-'+b+'" type="text" placeholder="'+r+'" size="'+q+'"/><button type="submit" style="background: transparent; border: none; padding: 4px; margin: 0px;"><i class="fa fa-search"></i></button></span></form>'}else{k+='<span id="'+b+'-filtering-criteria">';var s=false;for(cf_label in e.categorical_filters[b]){var o=e.categorical_filters[b][cf_label];var d="";var m="";for(key in o){d=key;m=o[key]}if(s){k+=" | "}s=true;var n=l[b];if(n&&o[b]&&n==m){k+='<span class="categorical-filter '+b+'-filter current-filter">'+cf_label+"</span>"}else{k+='<span class="categorical-filter '+b+'-filter"><a href="javascript:void(0);" filter_key="'+d+'" filter_val="'+m+'">'+cf_label+"</a></span>"}}k+="</span>"}k+="</td></tr>";return k},filter_element:function(b,a){return'<span class="text-filter-val">'+a+'<a href="javascript:void(0);" filter_key="'+b+'" filter_val="'+a+'"><i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/></a></span>'}}});
\ No newline at end of file
+define(["utils/utils"],function(a){return{grid:function(c){var b="";if(c.embedded){b=this.grid_header(c)+this.grid_table(c)}else{b='<div class="loading-elt-overlay"></div><table><tr><td width="75%">'+this.grid_header(c)+'</td><td></td><td></td></tr><tr><td width="100%" id="grid-message" valign="top"></td><td></td><td></td></tr></table>'+this.grid_table(c)}if(c.info_text){b+='<br><div class="toolParamHelp" style="clear: both;">'+c.info_text+"</div>"}return b},grid_table:function(b){return'<form method="post" onsubmit="return false;"><table id="grid-table" class="grid"><thead id="grid-table-header"></thead><tbody id="grid-table-body"></tbody><tfoot id="grid-table-footer"></tfoot></table></form>'},grid_header:function(d){var c='<div class="grid-header">';if(!d.embedded){c+="<h2>"+d.title+"</h2>"}if(d.global_actions){c+='<ul class="manage-table-actions">';var e=(d.global_actions.length>=3);if(e){c+='<li><a class="action-button" id="popup-global-actions" class="menubutton">Actions</a></li><div popupmenu="popup-global-actions">'}for(i in d.global_actions){var f=d.global_actions[i];var b="";if(f.inbound){b="use-inbound"}else{b="use-outbound"}c+='<li><a class="action-button '+b+'" href="'+f.url_args+'" onclick="return false;">'+f.label+"</a></li>"}if(e){c+="</div>"}c+="</ul>"}if(d.insert){c+=d.insert}c+=this.grid_filters(d);c+="</div>";return c},header:function(c){var b="<tr>";if(c.show_item_checkboxes){b+="<th>";if(c.items.length>0){b+='<input type="checkbox" id="check_all" name=select_all_checkbox value="true"><input type="hidden" name=select_all_checkbox value="true">'}b+="</th>"}for(var d in c.columns){var e=c.columns[d];if(e.visible){b+='<th id="'+e.key+'-header">';if(e.href){b+='<a href="'+e.href+'" class="sort-link" sort_key="'+e.key+'">'+e.label+"</a>"}else{b+=e.label}b+='<span class="sort-arrow">'+e.extra+"</span></th>"}}b+="</tr>";return b},body:function(s){var l="";var t=0;var f=s.items.length;if(f==0){l+='<tr><td colspan="100"><em>No Items</em></td></tr>';t=1}for(var g in s.items){var q=s.items[g];var b=q.encode_id;var h="grid-"+g+"-popup";l+="<tr ";if(s.current_item_id==q.id){l+='class="current"'}l+=">";if(s.show_item_checkboxes){l+='<td style="width: 1.5em;"><input type="checkbox" name="id" value="'+b+'" id="'+b+'" class="grid-row-select-checkbox" /></td>'}for(j in s.columns){var e=s.columns[j];if(e.visible){var d="";if(e.nowrap){d='style="white-space:nowrap;"'}var r=q.column_config[e.label];var k=r.link;var m=r.value;var p=r.inbound;if(jQuery.type(m)==="string"){m=m.replace(/\/\//g,"/")}var c="";var o="";if(e.attach_popup){c="grid-"+g+"-popup";o="menubutton";if(k!=""){o+=" split"}o+=" popup"}l+="<td "+d+">";if(k){if(s.operations.length!=0){l+='<div id="'+c+'" class="'+o+'" style="float: left;">'}var n="";if(p){n="use-inbound"}else{n="use-outbound"}l+='<a class="label '+n+'" href="'+k+'" onclick="return false;">'+m+"</a>";if(s.operations.length!=0){l+="</div>"}}else{l+='<div id="'+c+'" class="'+o+'"><label id="'+e.label_id_prefix+b+'" for="'+b+'">'+(m||"")+"</label></div>"}l+="</td>"}}l+="</tr>";t++}return l},footer:function(p){var l="";if(p.use_paging&&p.num_pages>1){var n=p.num_page_links;var b=p.cur_page_num;var o=p.num_pages;var k=n/2;var h=b-k;var f=0;if(h<=0){h=1;f=k-(b-h)}var e=k+f;var d=b+e;if(d<=o){max_offset=0}else{d=o;max_offset=e-(d+1-b)}if(max_offset!=0){h-=max_offset;if(h<1){h=1}}l+='<tr id="page-links-row">';if(p.show_item_checkboxes){l+="<td></td>"}l+='<td colspan="100"><span id="page-link-container">Page:';if(h>1){l+='<span class="page-link" id="page-link-1"><a href="javascript:void(0);" page_num="1" onclick="return false;">1</a></span> ...'}for(var m=h;m<d+1;m++){if(m==p.cur_page_num){l+='<span class="page-link inactive-link" id="page-link-'+m+'">'+m+"</span>"}else{l+='<span class="page-link" id="page-link-'+m+'"><a href="javascript:void(0);" onclick="return false;" page_num="'+m+'">'+m+"</a></span>"}}if(d<o){l+='...<span class="page-link" id="page-link-'+o+'"><a href="javascript:void(0);" onclick="return false;" page_num="'+o+'">'+o+"</a></span>"}l+="</span>";l+='<span class="page-link" id="show-all-link-span"> | <a href="javascript:void(0);" onclick="return false;" page_num="all">Show All</a></span></td></tr>'}if(p.show_item_checkboxes){l+='<tr><input type="hidden" id="operation" name="operation" value=""><td></td><td colspan="100">For <span class="grid-selected-count"></span> selected '+p.get_class_plural+": ";for(i in p.operations){var c=p.operations[i];if(c.allow_multiple){l+='<input type="button" value="'+c.label+'" class="operation-button action-button"> '}}l+="</td></tr>"}var g=false;for(i in p.operations){if(p.operations[i].global_operation){g=true;break}}if(g){l+='<tr><td colspan="100">';for(i in p.operations){var c=p.operations[i];if(c.global_operation){l+='<a class="action-button" href="'+c.global_operation+'">'+c.label+"</a>"}}l+="</td></tr>"}if(p.legend){l+='<tr><td colspan="100">'+p.legend+"</td></tr>"}return l},message:function(b){return'<p><div class="'+b.status+'message transient-message">'+b.message+'</div><div style="clear: both"></div></p>'},grid_filters:function(o){var b=o.default_filter_dict;var c=o.filters;var f="none";if(o.advanced_search){f="block"}var n=false;for(var h in o.columns){var e=o.columns[h];if(e.filterable=="advanced"){var m=e.key;var d=c[m];var g=b[m];if(d&&g&&d!=g){f="block"}n=true}}var l="block";if(f=="block"){l="none"}var k='<div id="standard-search" style="display: '+l+';"><table><tr><td style="padding: 0;"><table>';for(var h in o.columns){var e=o.columns[h];if(e.filterable=="standard"){k+=this.grid_column_filter(o,e)}}k+="</table></td></tr><tr><td>";if(n){k+='<a href="" class="advanced-search-toggle">Advanced Search</a>'}k+="</td></tr></table></div>";k+='<div id="advanced-search" style="display: '+f+'; margin-top: 5px; border: 1px solid #ccc;"><table><tr><td style="text-align: left" colspan="100"><a href="" class="advanced-search-toggle">Close Advanced Search</a></td></tr>';for(var h in o.columns){var e=o.columns[h];if(e.filterable=="advanced"){k+=this.grid_column_filter(o,e)}}k+="</table></div>";return k},grid_column_filter:function(f,d){var u=f.default_filter_dict;var m=f.filters;var b=d.label;var c=d.key;if(d.filterable=="advanced"){b=b.toLowerCase()}var l="<tr>";if(d.filterable=="advanced"){l+='<td align="left" style="padding-left: 10px">'+b+":</td>"}l+='<td style="padding-bottom: 1px;">';if(d.is_text){l+='<form class="text-filter-form" column_key="'+c+'" action="'+f.url+'" method="get" >';for(v in f.columns){var h=f.columns[v];var q=m[h.key];if(q){if(q!="All"){if(h.is_text){q=JSON.stringify(q)}l+='<input type="hidden" id="'+h.key+'" name="f-'+h.key+'" value="'+q+'"/>'}}}l+='<span id="'+c+'-filtering-criteria">';var k=m[c];if(k){var g=jQuery.type(k);if(g=="string"){if(k!="All"){l+=this.filter_element(c,k)}}if(g=="array"){for(var v in k){var o=k[v];var w=k;w=w.slice(v);l+=this.filter_element(c,o)}}}l+="</span>";var s="";if(d.filterable=="standard"){s=d.label.toLowerCase();var r=s.length;if(r<20){r=20}r=r+4}l+='<span class="search-box"><input class="search-box-input" id="input-'+c+'-filter" name="f-'+c+'" type="text" placeholder="'+s+'" size="'+r+'"/><button type="submit" style="background: transparent; border: none; padding: 4px; margin: 0px;"><i class="fa fa-search"></i></button></span></form>'}else{l+='<span id="'+c+'-filtering-criteria">';var t=false;for(cf_label in f.categorical_filters[c]){var p=f.categorical_filters[c][cf_label];var e="";var n="";for(key in p){e=key;n=p[key]}if(t){l+=" | "}t=true;var o=m[c];if(o&&p[c]&&o==n){l+='<span class="categorical-filter '+c+'-filter current-filter">'+cf_label+"</span>"}else{l+='<span class="categorical-filter '+c+'-filter"><a href="javascript:void(0);" filter_key="'+e+'" filter_val="'+n+'">'+cf_label+"</a></span>"}}l+="</span>"}l+="</td></tr>";return l},filter_element:function(c,b){b=a.sanitize(b);return'<span class="text-filter-val">'+b+'<a href="javascript:void(0);" filter_key="'+c+'" filter_val="'+b+'"><i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/></a></span>'}}});
\ No newline at end of file
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a static/scripts/packed/utils/utils.js
--- a/static/scripts/packed/utils/utils.js
+++ b/static/scripts/packed/utils/utils.js
@@ -1,1 +1,1 @@
-define(["libs/underscore"],function(j){function d(l,m,k){g("GET",l,{},m,k)}function g(p,l,m,o,k){if(p=="GET"||p=="DELETE"){if(l.indexOf("?")==-1){l+="?"}else{l+="&"}l+=$.param(m)}var n=new XMLHttpRequest();n.open(p,l,true);n.setRequestHeader("Accept","application/json");n.setRequestHeader("Cache-Control","no-cache");n.setRequestHeader("X-Requested-With","XMLHttpRequest");n.setRequestHeader("Content-Type","application/json");n.onloadend=function(){var q=n.status;try{response=jQuery.parseJSON(n.responseText)}catch(r){response=n.responseText}if(q==200){o&&o(response)}else{k&&k(response)}};if(p=="GET"||p=="DELETE"){n.send()}else{n.send(JSON.stringify(m))}}function h(n,k){var l=$('<div class="'+n+'"></div>');l.appendTo(":eq(0)");var m=l.css(k);l.remove();return m}function f(k){if(!$('link[href^="'+k+'"]').length){$('<link href="'+galaxy_config.root+k+'" rel="stylesheet">').appendTo("head")}}function i(k,l){if(k){return j.defaults(k,l)}else{return l}}function b(l,n){var m="";if(l>=100000000000){l=l/100000000000;m="TB"}else{if(l>=100000000){l=l/100000000;m="GB"}else{if(l>=100000){l=l/100000;m="MB"}else{if(l>=100){l=l/100;m="KB"}else{if(l>0){l=l*10;m="b"}else{return"<strong>-</strong>"}}}}}var k=(Math.round(l)/10);if(n){return k+" "+m}else{return"<strong>"+k+"</strong> "+m}}function a(){return"x"+Math.random().toString(36).substring(2,9)}function c(k){var l=$("<p></p>");l.append(k);return l}function e(){var m=new Date();var k=(m.getHours()<10?"0":"")+m.getHours();var l=(m.getMinutes()<10?"0":"")+m.getMinutes();var n=m.getDate()+"/"+(m.getMonth()+1)+"/"+m.getFullYear()+", "+k+":"+l;return n}return{cssLoadFile:f,cssGetAttribute:h,get:d,merge:i,bytesToString:b,uuid:a,time:e,wrap:c,request:g}});
\ No newline at end of file
+define(["libs/underscore"],function(k){function d(l){return $("<div/>").text(l).html()}function e(m,n,l){h("GET",m,{},n,l)}function h(q,m,n,p,l){if(q=="GET"||q=="DELETE"){if(m.indexOf("?")==-1){m+="?"}else{m+="&"}m+=$.param(n)}var o=new XMLHttpRequest();o.open(q,m,true);o.setRequestHeader("Accept","application/json");o.setRequestHeader("Cache-Control","no-cache");o.setRequestHeader("X-Requested-With","XMLHttpRequest");o.setRequestHeader("Content-Type","application/json");o.onloadend=function(){var r=o.status;try{response=jQuery.parseJSON(o.responseText)}catch(s){response=o.responseText}if(r==200){p&&p(response)}else{l&&l(response)}};if(q=="GET"||q=="DELETE"){o.send()}else{o.send(JSON.stringify(n))}}function i(o,l){var m=$('<div class="'+o+'"></div>');m.appendTo(":eq(0)");var n=m.css(l);m.remove();return n}function g(l){if(!$('link[href^="'+l+'"]').length){$('<link href="'+galaxy_config.root+l+'" rel="stylesheet">').appendTo("head")}}function j(l,m){if(l){return k.defaults(l,m)}else{return m}}function b(m,o){var n="";if(m>=100000000000){m=m/100000000000;n="TB"}else{if(m>=100000000){m=m/100000000;n="GB"}else{if(m>=100000){m=m/100000;n="MB"}else{if(m>=100){m=m/100;n="KB"}else{if(m>0){m=m*10;n="b"}else{return"<strong>-</strong>"}}}}}var l=(Math.round(m)/10);if(o){return l+" "+n}else{return"<strong>"+l+"</strong> "+n}}function a(){return"x"+Math.random().toString(36).substring(2,9)}function c(l){var m=$("<p></p>");m.append(l);return m}function f(){var n=new Date();var l=(n.getHours()<10?"0":"")+n.getHours();var m=(n.getMinutes()<10?"0":"")+n.getMinutes();var o=n.getDate()+"/"+(n.getMonth()+1)+"/"+n.getFullYear()+", "+l+":"+m;return o}return{cssLoadFile:g,cssGetAttribute:i,get:e,merge:j,bytesToString:b,uuid:a,time:f,wrap:c,request:h,sanitize:d}});
\ No newline at end of file
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a static/scripts/utils/utils.js
--- a/static/scripts/utils/utils.js
+++ b/static/scripts/utils/utils.js
@@ -6,6 +6,14 @@
// dependencies
define(["libs/underscore"], function(_) {
+/**
+ * Sanitize/escape a string
+ * @param{String} content - Content to be sanitized
+ */
+function sanitize(content) {
+ return $('<div/>').text(content).html();
+};
+
// generic function to recieve json from url
function get (url, success, error) {
request('GET', url, {}, success, error);
@@ -151,7 +159,8 @@
uuid: uuid,
time: time,
wrap: wrap,
- request: request
+ request: request,
+ sanitize: sanitize
};
});
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a templates/webapps/galaxy/tracks/history_datasets_select_grid.mako
--- a/templates/webapps/galaxy/tracks/history_datasets_select_grid.mako
+++ b/templates/webapps/galaxy/tracks/history_datasets_select_grid.mako
@@ -1,5 +1,5 @@
<%inherit file="/tracks/history_select_grid.mako"/><%def name="title()">
- <h2>History '${grid.get_current_item( trans, **kwargs ).name}'</h2>
+ <h2>History '${grid.get_current_item( trans, **kwargs ).name | h}'</h2></%def>
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a templates/webapps/galaxy/tracks/index.mako
--- a/templates/webapps/galaxy/tracks/index.mako
+++ /dev/null
@@ -1,38 +0,0 @@
-<form id="form" method="POST">
- <div class="form-row">
- <label for="dbkey">Browser name:</label>
- <div class="form-row-input">
- <input type="text" name="title" id="title" value="Unnamed Browser"></input>
- </div>
- <div style="clear: both;"></div>
- </div>
- <div class="form-row">
- <label for="dbkey">Reference genome build (dbkey): </label>
- <div class="form-row-input">
- <select name="dbkey" id="dbkey" refresh_on_change="true">
- %for tmp_dbkey in dbkey_set:
- <option value="${tmp_dbkey}"
- %if tmp_dbkey == dbkey:
- selected="selected"
- %endif
- >${tmp_dbkey}</option>
- %endfor
- </select>
- </div>
- <div style="clear: both;"></div>
- </div>
- <div class="form-row">
- <label for="dataset_ids">Datasets to visualize: (${", ".join(available_tracks)} files are supported)</label>
- %for dataset_id, (dataset_ext, dataset_name) in datasets.iteritems():
- <div>
- <input type="checkbox" id="${dataset_id}" name="dataset_ids" value="${dataset_id}" />
- <label style="display:inline; font-weight: normal" for="${dataset_id}">[${dataset_ext}] ${dataset_name}</label>
- </div>
- %endfor
-
- <div style="clear: both;"></div>
- </div>
- <div class="form-row">
- <input type="submit" name="browse" value="Browse"/>
- </div>
-</form>
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a templates/webapps/galaxy/tracks/library_datasets_select_grid.mako
--- a/templates/webapps/galaxy/tracks/library_datasets_select_grid.mako
+++ b/templates/webapps/galaxy/tracks/library_datasets_select_grid.mako
@@ -2,7 +2,7 @@
<%namespace file='/library/common/browse_library.mako' import="render_content, grid_javascripts" /><%def name="title()">
- <h2>History '${grid.get_current_item( trans, **kwargs ).name}'</h2>
+ <h2>History '${grid.get_current_item( trans, **kwargs ).name | h}'</h2></%def>
${select_header()}
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a templates/webapps/galaxy/visualization/phyloviz.mako
--- a/templates/webapps/galaxy/visualization/phyloviz.mako
+++ b/templates/webapps/galaxy/visualization/phyloviz.mako
@@ -180,7 +180,6 @@
<%def name="center_panel()">
-
<div class="unified-panel-header" unselectable="on"><div class="unified-panel-header-inner"><div style="float:left;" id="title"></div>
@@ -196,7 +195,7 @@
<p>Select a tree to view:
<select id="phylovizNexSelector">
% for tree, index in data["trees"]:
- <option value="${index}">${tree}</option>
+ <option value="${index | h}">${tree | h}</option>
% endfor
</select></p>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0
6 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/37e0a3b5e39b/
Changeset: 37e0a3b5e39b
Branch: stable
User: guerler
Date: 2014-12-08 17:50:09+00:00
Summary: Security fixes for assigned templates
Affected #: 4 files
diff -r 546ff6ef27b4b83e26ae228c292fd981173ac550 -r 37e0a3b5e39b4b9f300790c79336b08ad959c2ea templates/webapps/galaxy/tracks/history_datasets_select_grid.mako
--- a/templates/webapps/galaxy/tracks/history_datasets_select_grid.mako
+++ b/templates/webapps/galaxy/tracks/history_datasets_select_grid.mako
@@ -1,5 +1,6 @@
<%inherit file="/tracks/history_select_grid.mako"/><%def name="title()">
- <h2>History '${grid.get_current_item( trans, **kwargs ).name}'</h2>
+ <%from galaxy.web.framework.helpers import escape%>
+ <h2>History '${escape(grid.get_current_item( trans, **kwargs ).name)}'</h2></%def>
diff -r 546ff6ef27b4b83e26ae228c292fd981173ac550 -r 37e0a3b5e39b4b9f300790c79336b08ad959c2ea templates/webapps/galaxy/tracks/index.mako
--- a/templates/webapps/galaxy/tracks/index.mako
+++ /dev/null
@@ -1,38 +0,0 @@
-<form id="form" method="POST">
- <div class="form-row">
- <label for="dbkey">Browser name:</label>
- <div class="form-row-input">
- <input type="text" name="title" id="title" value="Unnamed Browser"></input>
- </div>
- <div style="clear: both;"></div>
- </div>
- <div class="form-row">
- <label for="dbkey">Reference genome build (dbkey): </label>
- <div class="form-row-input">
- <select name="dbkey" id="dbkey" refresh_on_change="true">
- %for tmp_dbkey in dbkey_set:
- <option value="${tmp_dbkey}"
- %if tmp_dbkey == dbkey:
- selected="selected"
- %endif
- >${tmp_dbkey}</option>
- %endfor
- </select>
- </div>
- <div style="clear: both;"></div>
- </div>
- <div class="form-row">
- <label for="dataset_ids">Datasets to visualize: (${", ".join(available_tracks)} files are supported)</label>
- %for dataset_id, (dataset_ext, dataset_name) in datasets.iteritems():
- <div>
- <input type="checkbox" id="${dataset_id}" name="dataset_ids" value="${dataset_id}" />
- <label style="display:inline; font-weight: normal" for="${dataset_id}">[${dataset_ext}] ${dataset_name}</label>
- </div>
- %endfor
-
- <div style="clear: both;"></div>
- </div>
- <div class="form-row">
- <input type="submit" name="browse" value="Browse"/>
- </div>
-</form>
diff -r 546ff6ef27b4b83e26ae228c292fd981173ac550 -r 37e0a3b5e39b4b9f300790c79336b08ad959c2ea templates/webapps/galaxy/tracks/library_datasets_select_grid.mako
--- a/templates/webapps/galaxy/tracks/library_datasets_select_grid.mako
+++ b/templates/webapps/galaxy/tracks/library_datasets_select_grid.mako
@@ -2,7 +2,8 @@
<%namespace file='/library/common/browse_library.mako' import="render_content, grid_javascripts" /><%def name="title()">
- <h2>History '${grid.get_current_item( trans, **kwargs ).name}'</h2>
+ <%from galaxy.web.framework.helpers import escape%>
+ <h2>History '${escape(grid.get_current_item( trans, **kwargs ).name)}'</h2></%def>
${select_header()}
diff -r 546ff6ef27b4b83e26ae228c292fd981173ac550 -r 37e0a3b5e39b4b9f300790c79336b08ad959c2ea templates/webapps/galaxy/visualization/phyloviz.mako
--- a/templates/webapps/galaxy/visualization/phyloviz.mako
+++ b/templates/webapps/galaxy/visualization/phyloviz.mako
@@ -180,7 +180,7 @@
<%def name="center_panel()">
-
+ <%from galaxy.web.framework.helpers import escape%><div class="unified-panel-header" unselectable="on"><div class="unified-panel-header-inner"><div style="float:left;" id="title"></div>
@@ -196,7 +196,7 @@
<p>Select a tree to view:
<select id="phylovizNexSelector">
% for tree, index in data["trees"]:
- <option value="${index}">${tree}</option>
+ <option value="${index}">${escape(tree)}</option>
% endfor
</select></p>
https://bitbucket.org/galaxy/galaxy-central/commits/dad0f6d2a871/
Changeset: dad0f6d2a871
Branch: stable
User: guerler
Date: 2014-11-21 21:41:44+00:00
Summary: JS-santization of filter options before displaying them FIX 2.2
Affected #: 6 files
diff -r 37e0a3b5e39b4b9f300790c79336b08ad959c2ea -r dad0f6d2a871f089d74b35b880e4354fbf050ccb client/galaxy/scripts/mvc/grid/grid-template.js
--- a/client/galaxy/scripts/mvc/grid/grid-template.js
+++ b/client/galaxy/scripts/mvc/grid/grid-template.js
@@ -1,5 +1,5 @@
// dependencies
-define([], function() {
+define(['utils/utils'], function(Utils) {
// grid view templates
return {
@@ -605,6 +605,7 @@
// template for filter items
filter_element: function(filter_key, filter_value) {
+ filter_value = Utils.sanitize(filter_value);
return '<span class="text-filter-val">' + filter_value +
'<a href="javascript:void(0);" filter_key="' + filter_key + '" filter_val="' + filter_value + '">' +
'<i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/>' +
diff -r 37e0a3b5e39b4b9f300790c79336b08ad959c2ea -r dad0f6d2a871f089d74b35b880e4354fbf050ccb client/galaxy/scripts/utils/utils.js
--- a/client/galaxy/scripts/utils/utils.js
+++ b/client/galaxy/scripts/utils/utils.js
@@ -6,6 +6,14 @@
// dependencies
define(["libs/underscore"], function(_) {
+/**
+ * Sanitize/escape a string
+ * @param{String} content - Content to be sanitized
+ */
+function sanitize(content) {
+ return $('<div/>').text(content).html();
+};
+
// generic function to recieve json from url
function get (url, success, error) {
request('GET', url, {}, success, error);
@@ -151,7 +159,8 @@
uuid: uuid,
time: time,
wrap: wrap,
- request: request
+ request: request,
+ sanitize: sanitize
};
});
diff -r 37e0a3b5e39b4b9f300790c79336b08ad959c2ea -r dad0f6d2a871f089d74b35b880e4354fbf050ccb static/scripts/mvc/grid/grid-template.js
--- a/static/scripts/mvc/grid/grid-template.js
+++ b/static/scripts/mvc/grid/grid-template.js
@@ -1,5 +1,5 @@
// dependencies
-define([], function() {
+define(['utils/utils'], function(Utils) {
// grid view templates
return {
@@ -605,6 +605,7 @@
// template for filter items
filter_element: function(filter_key, filter_value) {
+ filter_value = Utils.sanitize(filter_value);
return '<span class="text-filter-val">' + filter_value +
'<a href="javascript:void(0);" filter_key="' + filter_key + '" filter_val="' + filter_value + '">' +
'<i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/>' +
diff -r 37e0a3b5e39b4b9f300790c79336b08ad959c2ea -r dad0f6d2a871f089d74b35b880e4354fbf050ccb static/scripts/packed/mvc/grid/grid-template.js
--- a/static/scripts/packed/mvc/grid/grid-template.js
+++ b/static/scripts/packed/mvc/grid/grid-template.js
@@ -1,1 +1,1 @@
-define([],function(){return{grid:function(b){var a="";if(b.embedded){a=this.grid_header(b)+this.grid_table(b)}else{a='<div class="loading-elt-overlay"></div><table><tr><td width="75%">'+this.grid_header(b)+'</td><td></td><td></td></tr><tr><td width="100%" id="grid-message" valign="top"></td><td></td><td></td></tr></table>'+this.grid_table(b)}if(b.info_text){a+='<br><div class="toolParamHelp" style="clear: both;">'+b.info_text+"</div>"}return a},grid_table:function(a){return'<form method="post" onsubmit="return false;"><table id="grid-table" class="grid"><thead id="grid-table-header"></thead><tbody id="grid-table-body"></tbody><tfoot id="grid-table-footer"></tfoot></table></form>'},grid_header:function(c){var b='<div class="grid-header">';if(!c.embedded){b+="<h2>"+c.title+"</h2>"}if(c.global_actions){b+='<ul class="manage-table-actions">';var d=(c.global_actions.length>=3);if(d){b+='<li><a class="action-button" id="popup-global-actions" class="menubutton">Actions</a></li><div popupmenu="popup-global-actions">'}for(i in c.global_actions){var e=c.global_actions[i];var a="";if(e.inbound){a="use-inbound"}else{a="use-outbound"}b+='<li><a class="action-button '+a+'" href="'+e.url_args+'" onclick="return false;">'+e.label+"</a></li>"}if(d){b+="</div>"}b+="</ul>"}if(c.insert){b+=c.insert}b+=this.grid_filters(c);b+="</div>";return b},header:function(b){var a="<tr>";if(b.show_item_checkboxes){a+="<th>";if(b.items.length>0){a+='<input type="checkbox" id="check_all" name=select_all_checkbox value="true"><input type="hidden" name=select_all_checkbox value="true">'}a+="</th>"}for(var c in b.columns){var d=b.columns[c];if(d.visible){a+='<th id="'+d.key+'-header">';if(d.href){a+='<a href="'+d.href+'" class="sort-link" sort_key="'+d.key+'">'+d.label+"</a>"}else{a+=d.label}a+='<span class="sort-arrow">'+d.extra+"</span></th>"}}a+="</tr>";return a},body:function(r){var k="";var s=0;var e=r.items.length;if(e==0){k+='<tr><td colspan="100"><em>No Items</em></td></tr>';s=1}for(var f in r.items){var p=r.items[f];var a=p.encode_id;var g="grid-"+f+"-popup";k+="<tr ";if(r.current_item_id==p.id){k+='class="current"'}k+=">";if(r.show_item_checkboxes){k+='<td style="width: 1.5em;"><input type="checkbox" name="id" value="'+a+'" id="'+a+'" class="grid-row-select-checkbox" /></td>'}for(j in r.columns){var d=r.columns[j];if(d.visible){var c="";if(d.nowrap){c='style="white-space:nowrap;"'}var q=p.column_config[d.label];var h=q.link;var l=q.value;var o=q.inbound;if(jQuery.type(l)==="string"){l=l.replace(/\/\//g,"/")}var b="";var n="";if(d.attach_popup){b="grid-"+f+"-popup";n="menubutton";if(h!=""){n+=" split"}n+=" popup"}k+="<td "+c+">";if(h){if(r.operations.length!=0){k+='<div id="'+b+'" class="'+n+'" style="float: left;">'}var m="";if(o){m="use-inbound"}else{m="use-outbound"}k+='<a class="label '+m+'" href="'+h+'" onclick="return false;">'+l+"</a>";if(r.operations.length!=0){k+="</div>"}}else{k+='<div id="'+b+'" class="'+n+'"><label id="'+d.label_id_prefix+a+'" for="'+a+'">'+(l||"")+"</label></div>"}k+="</td>"}}k+="</tr>";s++}return k},footer:function(o){var k="";if(o.use_paging&&o.num_pages>1){var m=o.num_page_links;var a=o.cur_page_num;var n=o.num_pages;var h=m/2;var g=a-h;var e=0;if(g<=0){g=1;e=h-(a-g)}var d=h+e;var c=a+d;if(c<=n){max_offset=0}else{c=n;max_offset=d-(c+1-a)}if(max_offset!=0){g-=max_offset;if(g<1){g=1}}k+='<tr id="page-links-row">';if(o.show_item_checkboxes){k+="<td></td>"}k+='<td colspan="100"><span id="page-link-container">Page:';if(g>1){k+='<span class="page-link" id="page-link-1"><a href="javascript:void(0);" page_num="1" onclick="return false;">1</a></span> ...'}for(var l=g;l<c+1;l++){if(l==o.cur_page_num){k+='<span class="page-link inactive-link" id="page-link-'+l+'">'+l+"</span>"}else{k+='<span class="page-link" id="page-link-'+l+'"><a href="javascript:void(0);" onclick="return false;" page_num="'+l+'">'+l+"</a></span>"}}if(c<n){k+='...<span class="page-link" id="page-link-'+n+'"><a href="javascript:void(0);" onclick="return false;" page_num="'+n+'">'+n+"</a></span>"}k+="</span>";k+='<span class="page-link" id="show-all-link-span"> | <a href="javascript:void(0);" onclick="return false;" page_num="all">Show All</a></span></td></tr>'}if(o.show_item_checkboxes){k+='<tr><input type="hidden" id="operation" name="operation" value=""><td></td><td colspan="100">For <span class="grid-selected-count"></span> selected '+o.get_class_plural+": ";for(i in o.operations){var b=o.operations[i];if(b.allow_multiple){k+='<input type="button" value="'+b.label+'" class="operation-button action-button"> '}}k+="</td></tr>"}var f=false;for(i in o.operations){if(o.operations[i].global_operation){f=true;break}}if(f){k+='<tr><td colspan="100">';for(i in o.operations){var b=o.operations[i];if(b.global_operation){k+='<a class="action-button" href="'+b.global_operation+'">'+b.label+"</a>"}}k+="</td></tr>"}if(o.legend){k+='<tr><td colspan="100">'+o.legend+"</td></tr>"}return k},message:function(a){return'<p><div class="'+a.status+'message transient-message">'+a.message+'</div><div style="clear: both"></div></p>'},grid_filters:function(n){var a=n.default_filter_dict;var b=n.filters;var e="none";if(n.advanced_search){e="block"}var m=false;for(var g in n.columns){var d=n.columns[g];if(d.filterable=="advanced"){var l=d.key;var c=b[l];var f=a[l];if(c&&f&&c!=f){e="block"}m=true}}var k="block";if(e=="block"){k="none"}var h='<div id="standard-search" style="display: '+k+';"><table><tr><td style="padding: 0;"><table>';for(var g in n.columns){var d=n.columns[g];if(d.filterable=="standard"){h+=this.grid_column_filter(n,d)}}h+="</table></td></tr><tr><td>";if(m){h+='<a href="" class="advanced-search-toggle">Advanced Search</a>'}h+="</td></tr></table></div>";h+='<div id="advanced-search" style="display: '+e+'; margin-top: 5px; border: 1px solid #ccc;"><table><tr><td style="text-align: left" colspan="100"><a href="" class="advanced-search-toggle">Close Advanced Search</a></td></tr>';for(var g in n.columns){var d=n.columns[g];if(d.filterable=="advanced"){h+=this.grid_column_filter(n,d)}}h+="</table></div>";return h},grid_column_filter:function(e,c){var t=e.default_filter_dict;var l=e.filters;var a=c.label;var b=c.key;if(c.filterable=="advanced"){a=a.toLowerCase()}var k="<tr>";if(c.filterable=="advanced"){k+='<td align="left" style="padding-left: 10px">'+a+":</td>"}k+='<td style="padding-bottom: 1px;">';if(c.is_text){k+='<form class="text-filter-form" column_key="'+b+'" action="'+e.url+'" method="get" >';for(u in e.columns){var g=e.columns[u];var p=l[g.key];if(p){if(p!="All"){if(g.is_text){p=JSON.stringify(p)}k+='<input type="hidden" id="'+g.key+'" name="f-'+g.key+'" value="'+p+'"/>'}}}k+='<span id="'+b+'-filtering-criteria">';var h=l[b];if(h){var f=jQuery.type(h);if(f=="string"){if(h!="All"){k+=this.filter_element(b,h)}}if(f=="array"){for(var u in h){var n=h[u];var v=h;v=v.slice(u);k+=this.filter_element(b,n)}}}k+="</span>";var r="";if(c.filterable=="standard"){r=c.label.toLowerCase();var q=r.length;if(q<20){q=20}q=q+4}k+='<span class="search-box"><input class="search-box-input" id="input-'+b+'-filter" name="f-'+b+'" type="text" placeholder="'+r+'" size="'+q+'"/><button type="submit" style="background: transparent; border: none; padding: 4px; margin: 0px;"><i class="fa fa-search"></i></button></span></form>'}else{k+='<span id="'+b+'-filtering-criteria">';var s=false;for(cf_label in e.categorical_filters[b]){var o=e.categorical_filters[b][cf_label];var d="";var m="";for(key in o){d=key;m=o[key]}if(s){k+=" | "}s=true;var n=l[b];if(n&&o[b]&&n==m){k+='<span class="categorical-filter '+b+'-filter current-filter">'+cf_label+"</span>"}else{k+='<span class="categorical-filter '+b+'-filter"><a href="javascript:void(0);" filter_key="'+d+'" filter_val="'+m+'">'+cf_label+"</a></span>"}}k+="</span>"}k+="</td></tr>";return k},filter_element:function(b,a){return'<span class="text-filter-val">'+a+'<a href="javascript:void(0);" filter_key="'+b+'" filter_val="'+a+'"><i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/></a></span>'}}});
\ No newline at end of file
+define(["utils/utils"],function(a){return{grid:function(c){var b="";if(c.embedded){b=this.grid_header(c)+this.grid_table(c)}else{b='<div class="loading-elt-overlay"></div><table><tr><td width="75%">'+this.grid_header(c)+'</td><td></td><td></td></tr><tr><td width="100%" id="grid-message" valign="top"></td><td></td><td></td></tr></table>'+this.grid_table(c)}if(c.info_text){b+='<br><div class="toolParamHelp" style="clear: both;">'+c.info_text+"</div>"}return b},grid_table:function(b){return'<form method="post" onsubmit="return false;"><table id="grid-table" class="grid"><thead id="grid-table-header"></thead><tbody id="grid-table-body"></tbody><tfoot id="grid-table-footer"></tfoot></table></form>'},grid_header:function(d){var c='<div class="grid-header">';if(!d.embedded){c+="<h2>"+d.title+"</h2>"}if(d.global_actions){c+='<ul class="manage-table-actions">';var e=(d.global_actions.length>=3);if(e){c+='<li><a class="action-button" id="popup-global-actions" class="menubutton">Actions</a></li><div popupmenu="popup-global-actions">'}for(i in d.global_actions){var f=d.global_actions[i];var b="";if(f.inbound){b="use-inbound"}else{b="use-outbound"}c+='<li><a class="action-button '+b+'" href="'+f.url_args+'" onclick="return false;">'+f.label+"</a></li>"}if(e){c+="</div>"}c+="</ul>"}if(d.insert){c+=d.insert}c+=this.grid_filters(d);c+="</div>";return c},header:function(c){var b="<tr>";if(c.show_item_checkboxes){b+="<th>";if(c.items.length>0){b+='<input type="checkbox" id="check_all" name=select_all_checkbox value="true"><input type="hidden" name=select_all_checkbox value="true">'}b+="</th>"}for(var d in c.columns){var e=c.columns[d];if(e.visible){b+='<th id="'+e.key+'-header">';if(e.href){b+='<a href="'+e.href+'" class="sort-link" sort_key="'+e.key+'">'+e.label+"</a>"}else{b+=e.label}b+='<span class="sort-arrow">'+e.extra+"</span></th>"}}b+="</tr>";return b},body:function(s){var l="";var t=0;var f=s.items.length;if(f==0){l+='<tr><td colspan="100"><em>No Items</em></td></tr>';t=1}for(var g in s.items){var q=s.items[g];var b=q.encode_id;var h="grid-"+g+"-popup";l+="<tr ";if(s.current_item_id==q.id){l+='class="current"'}l+=">";if(s.show_item_checkboxes){l+='<td style="width: 1.5em;"><input type="checkbox" name="id" value="'+b+'" id="'+b+'" class="grid-row-select-checkbox" /></td>'}for(j in s.columns){var e=s.columns[j];if(e.visible){var d="";if(e.nowrap){d='style="white-space:nowrap;"'}var r=q.column_config[e.label];var k=r.link;var m=r.value;var p=r.inbound;if(jQuery.type(m)==="string"){m=m.replace(/\/\//g,"/")}var c="";var o="";if(e.attach_popup){c="grid-"+g+"-popup";o="menubutton";if(k!=""){o+=" split"}o+=" popup"}l+="<td "+d+">";if(k){if(s.operations.length!=0){l+='<div id="'+c+'" class="'+o+'" style="float: left;">'}var n="";if(p){n="use-inbound"}else{n="use-outbound"}l+='<a class="label '+n+'" href="'+k+'" onclick="return false;">'+m+"</a>";if(s.operations.length!=0){l+="</div>"}}else{l+='<div id="'+c+'" class="'+o+'"><label id="'+e.label_id_prefix+b+'" for="'+b+'">'+(m||"")+"</label></div>"}l+="</td>"}}l+="</tr>";t++}return l},footer:function(p){var l="";if(p.use_paging&&p.num_pages>1){var n=p.num_page_links;var b=p.cur_page_num;var o=p.num_pages;var k=n/2;var h=b-k;var f=0;if(h<=0){h=1;f=k-(b-h)}var e=k+f;var d=b+e;if(d<=o){max_offset=0}else{d=o;max_offset=e-(d+1-b)}if(max_offset!=0){h-=max_offset;if(h<1){h=1}}l+='<tr id="page-links-row">';if(p.show_item_checkboxes){l+="<td></td>"}l+='<td colspan="100"><span id="page-link-container">Page:';if(h>1){l+='<span class="page-link" id="page-link-1"><a href="javascript:void(0);" page_num="1" onclick="return false;">1</a></span> ...'}for(var m=h;m<d+1;m++){if(m==p.cur_page_num){l+='<span class="page-link inactive-link" id="page-link-'+m+'">'+m+"</span>"}else{l+='<span class="page-link" id="page-link-'+m+'"><a href="javascript:void(0);" onclick="return false;" page_num="'+m+'">'+m+"</a></span>"}}if(d<o){l+='...<span class="page-link" id="page-link-'+o+'"><a href="javascript:void(0);" onclick="return false;" page_num="'+o+'">'+o+"</a></span>"}l+="</span>";l+='<span class="page-link" id="show-all-link-span"> | <a href="javascript:void(0);" onclick="return false;" page_num="all">Show All</a></span></td></tr>'}if(p.show_item_checkboxes){l+='<tr><input type="hidden" id="operation" name="operation" value=""><td></td><td colspan="100">For <span class="grid-selected-count"></span> selected '+p.get_class_plural+": ";for(i in p.operations){var c=p.operations[i];if(c.allow_multiple){l+='<input type="button" value="'+c.label+'" class="operation-button action-button"> '}}l+="</td></tr>"}var g=false;for(i in p.operations){if(p.operations[i].global_operation){g=true;break}}if(g){l+='<tr><td colspan="100">';for(i in p.operations){var c=p.operations[i];if(c.global_operation){l+='<a class="action-button" href="'+c.global_operation+'">'+c.label+"</a>"}}l+="</td></tr>"}if(p.legend){l+='<tr><td colspan="100">'+p.legend+"</td></tr>"}return l},message:function(b){return'<p><div class="'+b.status+'message transient-message">'+b.message+'</div><div style="clear: both"></div></p>'},grid_filters:function(o){var b=o.default_filter_dict;var c=o.filters;var f="none";if(o.advanced_search){f="block"}var n=false;for(var h in o.columns){var e=o.columns[h];if(e.filterable=="advanced"){var m=e.key;var d=c[m];var g=b[m];if(d&&g&&d!=g){f="block"}n=true}}var l="block";if(f=="block"){l="none"}var k='<div id="standard-search" style="display: '+l+';"><table><tr><td style="padding: 0;"><table>';for(var h in o.columns){var e=o.columns[h];if(e.filterable=="standard"){k+=this.grid_column_filter(o,e)}}k+="</table></td></tr><tr><td>";if(n){k+='<a href="" class="advanced-search-toggle">Advanced Search</a>'}k+="</td></tr></table></div>";k+='<div id="advanced-search" style="display: '+f+'; margin-top: 5px; border: 1px solid #ccc;"><table><tr><td style="text-align: left" colspan="100"><a href="" class="advanced-search-toggle">Close Advanced Search</a></td></tr>';for(var h in o.columns){var e=o.columns[h];if(e.filterable=="advanced"){k+=this.grid_column_filter(o,e)}}k+="</table></div>";return k},grid_column_filter:function(f,d){var u=f.default_filter_dict;var m=f.filters;var b=d.label;var c=d.key;if(d.filterable=="advanced"){b=b.toLowerCase()}var l="<tr>";if(d.filterable=="advanced"){l+='<td align="left" style="padding-left: 10px">'+b+":</td>"}l+='<td style="padding-bottom: 1px;">';if(d.is_text){l+='<form class="text-filter-form" column_key="'+c+'" action="'+f.url+'" method="get" >';for(v in f.columns){var h=f.columns[v];var q=m[h.key];if(q){if(q!="All"){if(h.is_text){q=JSON.stringify(q)}l+='<input type="hidden" id="'+h.key+'" name="f-'+h.key+'" value="'+q+'"/>'}}}l+='<span id="'+c+'-filtering-criteria">';var k=m[c];if(k){var g=jQuery.type(k);if(g=="string"){if(k!="All"){l+=this.filter_element(c,k)}}if(g=="array"){for(var v in k){var o=k[v];var w=k;w=w.slice(v);l+=this.filter_element(c,o)}}}l+="</span>";var s="";if(d.filterable=="standard"){s=d.label.toLowerCase();var r=s.length;if(r<20){r=20}r=r+4}l+='<span class="search-box"><input class="search-box-input" id="input-'+c+'-filter" name="f-'+c+'" type="text" placeholder="'+s+'" size="'+r+'"/><button type="submit" style="background: transparent; border: none; padding: 4px; margin: 0px;"><i class="fa fa-search"></i></button></span></form>'}else{l+='<span id="'+c+'-filtering-criteria">';var t=false;for(cf_label in f.categorical_filters[c]){var p=f.categorical_filters[c][cf_label];var e="";var n="";for(key in p){e=key;n=p[key]}if(t){l+=" | "}t=true;var o=m[c];if(o&&p[c]&&o==n){l+='<span class="categorical-filter '+c+'-filter current-filter">'+cf_label+"</span>"}else{l+='<span class="categorical-filter '+c+'-filter"><a href="javascript:void(0);" filter_key="'+e+'" filter_val="'+n+'">'+cf_label+"</a></span>"}}l+="</span>"}l+="</td></tr>";return l},filter_element:function(c,b){b=a.sanitize(b);return'<span class="text-filter-val">'+b+'<a href="javascript:void(0);" filter_key="'+c+'" filter_val="'+b+'"><i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/></a></span>'}}});
\ No newline at end of file
diff -r 37e0a3b5e39b4b9f300790c79336b08ad959c2ea -r dad0f6d2a871f089d74b35b880e4354fbf050ccb static/scripts/packed/utils/utils.js
--- a/static/scripts/packed/utils/utils.js
+++ b/static/scripts/packed/utils/utils.js
@@ -1,1 +1,1 @@
-define(["libs/underscore"],function(j){function d(l,m,k){g("GET",l,{},m,k)}function g(p,l,m,o,k){if(p=="GET"||p=="DELETE"){if(l.indexOf("?")==-1){l+="?"}else{l+="&"}l+=$.param(m)}var n=new XMLHttpRequest();n.open(p,l,true);n.setRequestHeader("Accept","application/json");n.setRequestHeader("Cache-Control","no-cache");n.setRequestHeader("X-Requested-With","XMLHttpRequest");n.setRequestHeader("Content-Type","application/json");n.onloadend=function(){var q=n.status;try{response=jQuery.parseJSON(n.responseText)}catch(r){response=n.responseText}if(q==200){o&&o(response)}else{k&&k(response)}};if(p=="GET"||p=="DELETE"){n.send()}else{n.send(JSON.stringify(m))}}function h(n,k){var l=$('<div class="'+n+'"></div>');l.appendTo(":eq(0)");var m=l.css(k);l.remove();return m}function f(k){if(!$('link[href^="'+k+'"]').length){$('<link href="'+galaxy_config.root+k+'" rel="stylesheet">').appendTo("head")}}function i(k,l){if(k){return j.defaults(k,l)}else{return l}}function b(l,n){var m="";if(l>=100000000000){l=l/100000000000;m="TB"}else{if(l>=100000000){l=l/100000000;m="GB"}else{if(l>=100000){l=l/100000;m="MB"}else{if(l>=100){l=l/100;m="KB"}else{if(l>0){l=l*10;m="b"}else{return"<strong>-</strong>"}}}}}var k=(Math.round(l)/10);if(n){return k+" "+m}else{return"<strong>"+k+"</strong> "+m}}function a(){return"x"+Math.random().toString(36).substring(2,9)}function c(k){var l=$("<p></p>");l.append(k);return l}function e(){var m=new Date();var k=(m.getHours()<10?"0":"")+m.getHours();var l=(m.getMinutes()<10?"0":"")+m.getMinutes();var n=m.getDate()+"/"+(m.getMonth()+1)+"/"+m.getFullYear()+", "+k+":"+l;return n}return{cssLoadFile:f,cssGetAttribute:h,get:d,merge:i,bytesToString:b,uuid:a,time:e,wrap:c,request:g}});
\ No newline at end of file
+define(["libs/underscore"],function(k){function d(l){return $("<div/>").text(l).html()}function e(m,n,l){h("GET",m,{},n,l)}function h(q,m,n,p,l){if(q=="GET"||q=="DELETE"){if(m.indexOf("?")==-1){m+="?"}else{m+="&"}m+=$.param(n)}var o=new XMLHttpRequest();o.open(q,m,true);o.setRequestHeader("Accept","application/json");o.setRequestHeader("Cache-Control","no-cache");o.setRequestHeader("X-Requested-With","XMLHttpRequest");o.setRequestHeader("Content-Type","application/json");o.onloadend=function(){var r=o.status;try{response=jQuery.parseJSON(o.responseText)}catch(s){response=o.responseText}if(r==200){p&&p(response)}else{l&&l(response)}};if(q=="GET"||q=="DELETE"){o.send()}else{o.send(JSON.stringify(n))}}function i(o,l){var m=$('<div class="'+o+'"></div>');m.appendTo(":eq(0)");var n=m.css(l);m.remove();return n}function g(l){if(!$('link[href^="'+l+'"]').length){$('<link href="'+galaxy_config.root+l+'" rel="stylesheet">').appendTo("head")}}function j(l,m){if(l){return k.defaults(l,m)}else{return m}}function b(m,o){var n="";if(m>=100000000000){m=m/100000000000;n="TB"}else{if(m>=100000000){m=m/100000000;n="GB"}else{if(m>=100000){m=m/100000;n="MB"}else{if(m>=100){m=m/100;n="KB"}else{if(m>0){m=m*10;n="b"}else{return"<strong>-</strong>"}}}}}var l=(Math.round(m)/10);if(o){return l+" "+n}else{return"<strong>"+l+"</strong> "+n}}function a(){return"x"+Math.random().toString(36).substring(2,9)}function c(l){var m=$("<p></p>");m.append(l);return m}function f(){var n=new Date();var l=(n.getHours()<10?"0":"")+n.getHours();var m=(n.getMinutes()<10?"0":"")+n.getMinutes();var o=n.getDate()+"/"+(n.getMonth()+1)+"/"+n.getFullYear()+", "+l+":"+m;return o}return{cssLoadFile:g,cssGetAttribute:i,get:e,merge:j,bytesToString:b,uuid:a,time:f,wrap:c,request:h,sanitize:d}});
\ No newline at end of file
diff -r 37e0a3b5e39b4b9f300790c79336b08ad959c2ea -r dad0f6d2a871f089d74b35b880e4354fbf050ccb static/scripts/utils/utils.js
--- a/static/scripts/utils/utils.js
+++ b/static/scripts/utils/utils.js
@@ -6,6 +6,14 @@
// dependencies
define(["libs/underscore"], function(_) {
+/**
+ * Sanitize/escape a string
+ * @param{String} content - Content to be sanitized
+ */
+function sanitize(content) {
+ return $('<div/>').text(content).html();
+};
+
// generic function to recieve json from url
function get (url, success, error) {
request('GET', url, {}, success, error);
@@ -151,7 +159,8 @@
uuid: uuid,
time: time,
wrap: wrap,
- request: request
+ request: request,
+ sanitize: sanitize
};
});
https://bitbucket.org/galaxy/galaxy-central/commits/c8b92d4eddcc/
Changeset: c8b92d4eddcc
Branch: stable
User: guerler
Date: 2014-11-25 19:04:43+00:00
Summary: Fixes security issue 2.2 for regular grid values
Affected #: 1 file
diff -r dad0f6d2a871f089d74b35b880e4354fbf050ccb -r c8b92d4eddcc6f2cf4ae8c4ca43b7f80b10af67f lib/galaxy/web/framework/helpers/grids.py
--- a/lib/galaxy/web/framework/helpers/grids.py
+++ b/lib/galaxy/web/framework/helpers/grids.py
@@ -8,6 +8,7 @@
from galaxy.web.framework import decorators
from galaxy.web.framework import url_for
from galaxy.web.framework.helpers import iff
+from markupsafe import escape
from sqlalchemy.sql.expression import and_, func, or_
@@ -362,7 +363,7 @@
value = None
if self.format:
value = self.format( value )
- return value
+ return escape(value)
def get_link( self, trans, grid, item ):
if self.link and self.link( item ):
return self.link( item )
https://bitbucket.org/galaxy/galaxy-central/commits/04967539e326/
Changeset: 04967539e326
Branch: stable
User: guerler
Date: 2014-12-01 17:52:01+00:00
Summary: Pages: Sanitize name column in items grid
Affected #: 1 file
diff -r c8b92d4eddcc6f2cf4ae8c4ca43b7f80b10af67f -r 04967539e32612f432b8ee96975de3b7d1f4ecc1 lib/galaxy/webapps/galaxy/controllers/page.py
--- a/lib/galaxy/webapps/galaxy/controllers/page.py
+++ b/lib/galaxy/webapps/galaxy/controllers/page.py
@@ -8,6 +8,7 @@
from galaxy import util
from galaxy.util.sanitize_html import sanitize_html, _BaseHTMLProcessor
from galaxy.util.json import loads
+from markupsafe import escape
def format_bool( b ):
if b:
@@ -89,9 +90,9 @@
class NameColumn( grids.TextColumn ):
def get_value(self, trans, grid, item):
if hasattr( item, "get_display_name" ):
- return item.get_display_name()
+ return escape(item.get_display_name())
else:
- return item.name
+ return escape(item.name)
# Grid definition.
show_item_checkboxes = True
https://bitbucket.org/galaxy/galaxy-central/commits/c92310019777/
Changeset: c92310019777
Branch: stable
User: guerler
Date: 2014-12-08 19:00:57+00:00
Summary: Use h instead of escape for sanitization
Affected #: 3 files
diff -r 04967539e32612f432b8ee96975de3b7d1f4ecc1 -r c923100197773cae2795fcc5741a7917ffd1e907 templates/webapps/galaxy/tracks/history_datasets_select_grid.mako
--- a/templates/webapps/galaxy/tracks/history_datasets_select_grid.mako
+++ b/templates/webapps/galaxy/tracks/history_datasets_select_grid.mako
@@ -1,6 +1,5 @@
<%inherit file="/tracks/history_select_grid.mako"/><%def name="title()">
- <%from galaxy.web.framework.helpers import escape%>
- <h2>History '${escape(grid.get_current_item( trans, **kwargs ).name)}'</h2>
+ <h2>History '${grid.get_current_item( trans, **kwargs ).name | h}'</h2></%def>
diff -r 04967539e32612f432b8ee96975de3b7d1f4ecc1 -r c923100197773cae2795fcc5741a7917ffd1e907 templates/webapps/galaxy/tracks/library_datasets_select_grid.mako
--- a/templates/webapps/galaxy/tracks/library_datasets_select_grid.mako
+++ b/templates/webapps/galaxy/tracks/library_datasets_select_grid.mako
@@ -2,8 +2,7 @@
<%namespace file='/library/common/browse_library.mako' import="render_content, grid_javascripts" /><%def name="title()">
- <%from galaxy.web.framework.helpers import escape%>
- <h2>History '${escape(grid.get_current_item( trans, **kwargs ).name)}'</h2>
+ <h2>History '${grid.get_current_item( trans, **kwargs ).name | h}'</h2></%def>
${select_header()}
diff -r 04967539e32612f432b8ee96975de3b7d1f4ecc1 -r c923100197773cae2795fcc5741a7917ffd1e907 templates/webapps/galaxy/visualization/phyloviz.mako
--- a/templates/webapps/galaxy/visualization/phyloviz.mako
+++ b/templates/webapps/galaxy/visualization/phyloviz.mako
@@ -180,7 +180,6 @@
<%def name="center_panel()">
- <%from galaxy.web.framework.helpers import escape%><div class="unified-panel-header" unselectable="on"><div class="unified-panel-header-inner"><div style="float:left;" id="title"></div>
@@ -196,7 +195,7 @@
<p>Select a tree to view:
<select id="phylovizNexSelector">
% for tree, index in data["trees"]:
- <option value="${index}">${escape(tree)}</option>
+ <option value="${index | h}">${tree | h}</option>
% endfor
</select></p>
https://bitbucket.org/galaxy/galaxy-central/commits/704efd2f5e06/
Changeset: 704efd2f5e06
Branch: stable
User: dannon
Date: 2014-12-08 19:11:14+00:00
Summary: Merged in guerler/guerler-galaxy-central/stable (pull request #598)
Security fixes for assigned templates
Affected #: 12 files
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a client/galaxy/scripts/mvc/grid/grid-template.js
--- a/client/galaxy/scripts/mvc/grid/grid-template.js
+++ b/client/galaxy/scripts/mvc/grid/grid-template.js
@@ -1,5 +1,5 @@
// dependencies
-define([], function() {
+define(['utils/utils'], function(Utils) {
// grid view templates
return {
@@ -605,6 +605,7 @@
// template for filter items
filter_element: function(filter_key, filter_value) {
+ filter_value = Utils.sanitize(filter_value);
return '<span class="text-filter-val">' + filter_value +
'<a href="javascript:void(0);" filter_key="' + filter_key + '" filter_val="' + filter_value + '">' +
'<i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/>' +
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a client/galaxy/scripts/utils/utils.js
--- a/client/galaxy/scripts/utils/utils.js
+++ b/client/galaxy/scripts/utils/utils.js
@@ -6,6 +6,14 @@
// dependencies
define(["libs/underscore"], function(_) {
+/**
+ * Sanitize/escape a string
+ * @param{String} content - Content to be sanitized
+ */
+function sanitize(content) {
+ return $('<div/>').text(content).html();
+};
+
// generic function to recieve json from url
function get (url, success, error) {
request('GET', url, {}, success, error);
@@ -151,7 +159,8 @@
uuid: uuid,
time: time,
wrap: wrap,
- request: request
+ request: request,
+ sanitize: sanitize
};
});
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a lib/galaxy/web/framework/helpers/grids.py
--- a/lib/galaxy/web/framework/helpers/grids.py
+++ b/lib/galaxy/web/framework/helpers/grids.py
@@ -8,6 +8,7 @@
from galaxy.web.framework import decorators
from galaxy.web.framework import url_for
from galaxy.web.framework.helpers import iff
+from markupsafe import escape
from sqlalchemy.sql.expression import and_, func, or_
@@ -362,7 +363,7 @@
value = None
if self.format:
value = self.format( value )
- return value
+ return escape(value)
def get_link( self, trans, grid, item ):
if self.link and self.link( item ):
return self.link( item )
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a lib/galaxy/webapps/galaxy/controllers/page.py
--- a/lib/galaxy/webapps/galaxy/controllers/page.py
+++ b/lib/galaxy/webapps/galaxy/controllers/page.py
@@ -8,6 +8,7 @@
from galaxy import util
from galaxy.util.sanitize_html import sanitize_html, _BaseHTMLProcessor
from galaxy.util.json import loads
+from markupsafe import escape
def format_bool( b ):
if b:
@@ -89,9 +90,9 @@
class NameColumn( grids.TextColumn ):
def get_value(self, trans, grid, item):
if hasattr( item, "get_display_name" ):
- return item.get_display_name()
+ return escape(item.get_display_name())
else:
- return item.name
+ return escape(item.name)
# Grid definition.
show_item_checkboxes = True
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a static/scripts/mvc/grid/grid-template.js
--- a/static/scripts/mvc/grid/grid-template.js
+++ b/static/scripts/mvc/grid/grid-template.js
@@ -1,5 +1,5 @@
// dependencies
-define([], function() {
+define(['utils/utils'], function(Utils) {
// grid view templates
return {
@@ -605,6 +605,7 @@
// template for filter items
filter_element: function(filter_key, filter_value) {
+ filter_value = Utils.sanitize(filter_value);
return '<span class="text-filter-val">' + filter_value +
'<a href="javascript:void(0);" filter_key="' + filter_key + '" filter_val="' + filter_value + '">' +
'<i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/>' +
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a static/scripts/packed/mvc/grid/grid-template.js
--- a/static/scripts/packed/mvc/grid/grid-template.js
+++ b/static/scripts/packed/mvc/grid/grid-template.js
@@ -1,1 +1,1 @@
-define([],function(){return{grid:function(b){var a="";if(b.embedded){a=this.grid_header(b)+this.grid_table(b)}else{a='<div class="loading-elt-overlay"></div><table><tr><td width="75%">'+this.grid_header(b)+'</td><td></td><td></td></tr><tr><td width="100%" id="grid-message" valign="top"></td><td></td><td></td></tr></table>'+this.grid_table(b)}if(b.info_text){a+='<br><div class="toolParamHelp" style="clear: both;">'+b.info_text+"</div>"}return a},grid_table:function(a){return'<form method="post" onsubmit="return false;"><table id="grid-table" class="grid"><thead id="grid-table-header"></thead><tbody id="grid-table-body"></tbody><tfoot id="grid-table-footer"></tfoot></table></form>'},grid_header:function(c){var b='<div class="grid-header">';if(!c.embedded){b+="<h2>"+c.title+"</h2>"}if(c.global_actions){b+='<ul class="manage-table-actions">';var d=(c.global_actions.length>=3);if(d){b+='<li><a class="action-button" id="popup-global-actions" class="menubutton">Actions</a></li><div popupmenu="popup-global-actions">'}for(i in c.global_actions){var e=c.global_actions[i];var a="";if(e.inbound){a="use-inbound"}else{a="use-outbound"}b+='<li><a class="action-button '+a+'" href="'+e.url_args+'" onclick="return false;">'+e.label+"</a></li>"}if(d){b+="</div>"}b+="</ul>"}if(c.insert){b+=c.insert}b+=this.grid_filters(c);b+="</div>";return b},header:function(b){var a="<tr>";if(b.show_item_checkboxes){a+="<th>";if(b.items.length>0){a+='<input type="checkbox" id="check_all" name=select_all_checkbox value="true"><input type="hidden" name=select_all_checkbox value="true">'}a+="</th>"}for(var c in b.columns){var d=b.columns[c];if(d.visible){a+='<th id="'+d.key+'-header">';if(d.href){a+='<a href="'+d.href+'" class="sort-link" sort_key="'+d.key+'">'+d.label+"</a>"}else{a+=d.label}a+='<span class="sort-arrow">'+d.extra+"</span></th>"}}a+="</tr>";return a},body:function(r){var k="";var s=0;var e=r.items.length;if(e==0){k+='<tr><td colspan="100"><em>No Items</em></td></tr>';s=1}for(var f in r.items){var p=r.items[f];var a=p.encode_id;var g="grid-"+f+"-popup";k+="<tr ";if(r.current_item_id==p.id){k+='class="current"'}k+=">";if(r.show_item_checkboxes){k+='<td style="width: 1.5em;"><input type="checkbox" name="id" value="'+a+'" id="'+a+'" class="grid-row-select-checkbox" /></td>'}for(j in r.columns){var d=r.columns[j];if(d.visible){var c="";if(d.nowrap){c='style="white-space:nowrap;"'}var q=p.column_config[d.label];var h=q.link;var l=q.value;var o=q.inbound;if(jQuery.type(l)==="string"){l=l.replace(/\/\//g,"/")}var b="";var n="";if(d.attach_popup){b="grid-"+f+"-popup";n="menubutton";if(h!=""){n+=" split"}n+=" popup"}k+="<td "+c+">";if(h){if(r.operations.length!=0){k+='<div id="'+b+'" class="'+n+'" style="float: left;">'}var m="";if(o){m="use-inbound"}else{m="use-outbound"}k+='<a class="label '+m+'" href="'+h+'" onclick="return false;">'+l+"</a>";if(r.operations.length!=0){k+="</div>"}}else{k+='<div id="'+b+'" class="'+n+'"><label id="'+d.label_id_prefix+a+'" for="'+a+'">'+(l||"")+"</label></div>"}k+="</td>"}}k+="</tr>";s++}return k},footer:function(o){var k="";if(o.use_paging&&o.num_pages>1){var m=o.num_page_links;var a=o.cur_page_num;var n=o.num_pages;var h=m/2;var g=a-h;var e=0;if(g<=0){g=1;e=h-(a-g)}var d=h+e;var c=a+d;if(c<=n){max_offset=0}else{c=n;max_offset=d-(c+1-a)}if(max_offset!=0){g-=max_offset;if(g<1){g=1}}k+='<tr id="page-links-row">';if(o.show_item_checkboxes){k+="<td></td>"}k+='<td colspan="100"><span id="page-link-container">Page:';if(g>1){k+='<span class="page-link" id="page-link-1"><a href="javascript:void(0);" page_num="1" onclick="return false;">1</a></span> ...'}for(var l=g;l<c+1;l++){if(l==o.cur_page_num){k+='<span class="page-link inactive-link" id="page-link-'+l+'">'+l+"</span>"}else{k+='<span class="page-link" id="page-link-'+l+'"><a href="javascript:void(0);" onclick="return false;" page_num="'+l+'">'+l+"</a></span>"}}if(c<n){k+='...<span class="page-link" id="page-link-'+n+'"><a href="javascript:void(0);" onclick="return false;" page_num="'+n+'">'+n+"</a></span>"}k+="</span>";k+='<span class="page-link" id="show-all-link-span"> | <a href="javascript:void(0);" onclick="return false;" page_num="all">Show All</a></span></td></tr>'}if(o.show_item_checkboxes){k+='<tr><input type="hidden" id="operation" name="operation" value=""><td></td><td colspan="100">For <span class="grid-selected-count"></span> selected '+o.get_class_plural+": ";for(i in o.operations){var b=o.operations[i];if(b.allow_multiple){k+='<input type="button" value="'+b.label+'" class="operation-button action-button"> '}}k+="</td></tr>"}var f=false;for(i in o.operations){if(o.operations[i].global_operation){f=true;break}}if(f){k+='<tr><td colspan="100">';for(i in o.operations){var b=o.operations[i];if(b.global_operation){k+='<a class="action-button" href="'+b.global_operation+'">'+b.label+"</a>"}}k+="</td></tr>"}if(o.legend){k+='<tr><td colspan="100">'+o.legend+"</td></tr>"}return k},message:function(a){return'<p><div class="'+a.status+'message transient-message">'+a.message+'</div><div style="clear: both"></div></p>'},grid_filters:function(n){var a=n.default_filter_dict;var b=n.filters;var e="none";if(n.advanced_search){e="block"}var m=false;for(var g in n.columns){var d=n.columns[g];if(d.filterable=="advanced"){var l=d.key;var c=b[l];var f=a[l];if(c&&f&&c!=f){e="block"}m=true}}var k="block";if(e=="block"){k="none"}var h='<div id="standard-search" style="display: '+k+';"><table><tr><td style="padding: 0;"><table>';for(var g in n.columns){var d=n.columns[g];if(d.filterable=="standard"){h+=this.grid_column_filter(n,d)}}h+="</table></td></tr><tr><td>";if(m){h+='<a href="" class="advanced-search-toggle">Advanced Search</a>'}h+="</td></tr></table></div>";h+='<div id="advanced-search" style="display: '+e+'; margin-top: 5px; border: 1px solid #ccc;"><table><tr><td style="text-align: left" colspan="100"><a href="" class="advanced-search-toggle">Close Advanced Search</a></td></tr>';for(var g in n.columns){var d=n.columns[g];if(d.filterable=="advanced"){h+=this.grid_column_filter(n,d)}}h+="</table></div>";return h},grid_column_filter:function(e,c){var t=e.default_filter_dict;var l=e.filters;var a=c.label;var b=c.key;if(c.filterable=="advanced"){a=a.toLowerCase()}var k="<tr>";if(c.filterable=="advanced"){k+='<td align="left" style="padding-left: 10px">'+a+":</td>"}k+='<td style="padding-bottom: 1px;">';if(c.is_text){k+='<form class="text-filter-form" column_key="'+b+'" action="'+e.url+'" method="get" >';for(u in e.columns){var g=e.columns[u];var p=l[g.key];if(p){if(p!="All"){if(g.is_text){p=JSON.stringify(p)}k+='<input type="hidden" id="'+g.key+'" name="f-'+g.key+'" value="'+p+'"/>'}}}k+='<span id="'+b+'-filtering-criteria">';var h=l[b];if(h){var f=jQuery.type(h);if(f=="string"){if(h!="All"){k+=this.filter_element(b,h)}}if(f=="array"){for(var u in h){var n=h[u];var v=h;v=v.slice(u);k+=this.filter_element(b,n)}}}k+="</span>";var r="";if(c.filterable=="standard"){r=c.label.toLowerCase();var q=r.length;if(q<20){q=20}q=q+4}k+='<span class="search-box"><input class="search-box-input" id="input-'+b+'-filter" name="f-'+b+'" type="text" placeholder="'+r+'" size="'+q+'"/><button type="submit" style="background: transparent; border: none; padding: 4px; margin: 0px;"><i class="fa fa-search"></i></button></span></form>'}else{k+='<span id="'+b+'-filtering-criteria">';var s=false;for(cf_label in e.categorical_filters[b]){var o=e.categorical_filters[b][cf_label];var d="";var m="";for(key in o){d=key;m=o[key]}if(s){k+=" | "}s=true;var n=l[b];if(n&&o[b]&&n==m){k+='<span class="categorical-filter '+b+'-filter current-filter">'+cf_label+"</span>"}else{k+='<span class="categorical-filter '+b+'-filter"><a href="javascript:void(0);" filter_key="'+d+'" filter_val="'+m+'">'+cf_label+"</a></span>"}}k+="</span>"}k+="</td></tr>";return k},filter_element:function(b,a){return'<span class="text-filter-val">'+a+'<a href="javascript:void(0);" filter_key="'+b+'" filter_val="'+a+'"><i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/></a></span>'}}});
\ No newline at end of file
+define(["utils/utils"],function(a){return{grid:function(c){var b="";if(c.embedded){b=this.grid_header(c)+this.grid_table(c)}else{b='<div class="loading-elt-overlay"></div><table><tr><td width="75%">'+this.grid_header(c)+'</td><td></td><td></td></tr><tr><td width="100%" id="grid-message" valign="top"></td><td></td><td></td></tr></table>'+this.grid_table(c)}if(c.info_text){b+='<br><div class="toolParamHelp" style="clear: both;">'+c.info_text+"</div>"}return b},grid_table:function(b){return'<form method="post" onsubmit="return false;"><table id="grid-table" class="grid"><thead id="grid-table-header"></thead><tbody id="grid-table-body"></tbody><tfoot id="grid-table-footer"></tfoot></table></form>'},grid_header:function(d){var c='<div class="grid-header">';if(!d.embedded){c+="<h2>"+d.title+"</h2>"}if(d.global_actions){c+='<ul class="manage-table-actions">';var e=(d.global_actions.length>=3);if(e){c+='<li><a class="action-button" id="popup-global-actions" class="menubutton">Actions</a></li><div popupmenu="popup-global-actions">'}for(i in d.global_actions){var f=d.global_actions[i];var b="";if(f.inbound){b="use-inbound"}else{b="use-outbound"}c+='<li><a class="action-button '+b+'" href="'+f.url_args+'" onclick="return false;">'+f.label+"</a></li>"}if(e){c+="</div>"}c+="</ul>"}if(d.insert){c+=d.insert}c+=this.grid_filters(d);c+="</div>";return c},header:function(c){var b="<tr>";if(c.show_item_checkboxes){b+="<th>";if(c.items.length>0){b+='<input type="checkbox" id="check_all" name=select_all_checkbox value="true"><input type="hidden" name=select_all_checkbox value="true">'}b+="</th>"}for(var d in c.columns){var e=c.columns[d];if(e.visible){b+='<th id="'+e.key+'-header">';if(e.href){b+='<a href="'+e.href+'" class="sort-link" sort_key="'+e.key+'">'+e.label+"</a>"}else{b+=e.label}b+='<span class="sort-arrow">'+e.extra+"</span></th>"}}b+="</tr>";return b},body:function(s){var l="";var t=0;var f=s.items.length;if(f==0){l+='<tr><td colspan="100"><em>No Items</em></td></tr>';t=1}for(var g in s.items){var q=s.items[g];var b=q.encode_id;var h="grid-"+g+"-popup";l+="<tr ";if(s.current_item_id==q.id){l+='class="current"'}l+=">";if(s.show_item_checkboxes){l+='<td style="width: 1.5em;"><input type="checkbox" name="id" value="'+b+'" id="'+b+'" class="grid-row-select-checkbox" /></td>'}for(j in s.columns){var e=s.columns[j];if(e.visible){var d="";if(e.nowrap){d='style="white-space:nowrap;"'}var r=q.column_config[e.label];var k=r.link;var m=r.value;var p=r.inbound;if(jQuery.type(m)==="string"){m=m.replace(/\/\//g,"/")}var c="";var o="";if(e.attach_popup){c="grid-"+g+"-popup";o="menubutton";if(k!=""){o+=" split"}o+=" popup"}l+="<td "+d+">";if(k){if(s.operations.length!=0){l+='<div id="'+c+'" class="'+o+'" style="float: left;">'}var n="";if(p){n="use-inbound"}else{n="use-outbound"}l+='<a class="label '+n+'" href="'+k+'" onclick="return false;">'+m+"</a>";if(s.operations.length!=0){l+="</div>"}}else{l+='<div id="'+c+'" class="'+o+'"><label id="'+e.label_id_prefix+b+'" for="'+b+'">'+(m||"")+"</label></div>"}l+="</td>"}}l+="</tr>";t++}return l},footer:function(p){var l="";if(p.use_paging&&p.num_pages>1){var n=p.num_page_links;var b=p.cur_page_num;var o=p.num_pages;var k=n/2;var h=b-k;var f=0;if(h<=0){h=1;f=k-(b-h)}var e=k+f;var d=b+e;if(d<=o){max_offset=0}else{d=o;max_offset=e-(d+1-b)}if(max_offset!=0){h-=max_offset;if(h<1){h=1}}l+='<tr id="page-links-row">';if(p.show_item_checkboxes){l+="<td></td>"}l+='<td colspan="100"><span id="page-link-container">Page:';if(h>1){l+='<span class="page-link" id="page-link-1"><a href="javascript:void(0);" page_num="1" onclick="return false;">1</a></span> ...'}for(var m=h;m<d+1;m++){if(m==p.cur_page_num){l+='<span class="page-link inactive-link" id="page-link-'+m+'">'+m+"</span>"}else{l+='<span class="page-link" id="page-link-'+m+'"><a href="javascript:void(0);" onclick="return false;" page_num="'+m+'">'+m+"</a></span>"}}if(d<o){l+='...<span class="page-link" id="page-link-'+o+'"><a href="javascript:void(0);" onclick="return false;" page_num="'+o+'">'+o+"</a></span>"}l+="</span>";l+='<span class="page-link" id="show-all-link-span"> | <a href="javascript:void(0);" onclick="return false;" page_num="all">Show All</a></span></td></tr>'}if(p.show_item_checkboxes){l+='<tr><input type="hidden" id="operation" name="operation" value=""><td></td><td colspan="100">For <span class="grid-selected-count"></span> selected '+p.get_class_plural+": ";for(i in p.operations){var c=p.operations[i];if(c.allow_multiple){l+='<input type="button" value="'+c.label+'" class="operation-button action-button"> '}}l+="</td></tr>"}var g=false;for(i in p.operations){if(p.operations[i].global_operation){g=true;break}}if(g){l+='<tr><td colspan="100">';for(i in p.operations){var c=p.operations[i];if(c.global_operation){l+='<a class="action-button" href="'+c.global_operation+'">'+c.label+"</a>"}}l+="</td></tr>"}if(p.legend){l+='<tr><td colspan="100">'+p.legend+"</td></tr>"}return l},message:function(b){return'<p><div class="'+b.status+'message transient-message">'+b.message+'</div><div style="clear: both"></div></p>'},grid_filters:function(o){var b=o.default_filter_dict;var c=o.filters;var f="none";if(o.advanced_search){f="block"}var n=false;for(var h in o.columns){var e=o.columns[h];if(e.filterable=="advanced"){var m=e.key;var d=c[m];var g=b[m];if(d&&g&&d!=g){f="block"}n=true}}var l="block";if(f=="block"){l="none"}var k='<div id="standard-search" style="display: '+l+';"><table><tr><td style="padding: 0;"><table>';for(var h in o.columns){var e=o.columns[h];if(e.filterable=="standard"){k+=this.grid_column_filter(o,e)}}k+="</table></td></tr><tr><td>";if(n){k+='<a href="" class="advanced-search-toggle">Advanced Search</a>'}k+="</td></tr></table></div>";k+='<div id="advanced-search" style="display: '+f+'; margin-top: 5px; border: 1px solid #ccc;"><table><tr><td style="text-align: left" colspan="100"><a href="" class="advanced-search-toggle">Close Advanced Search</a></td></tr>';for(var h in o.columns){var e=o.columns[h];if(e.filterable=="advanced"){k+=this.grid_column_filter(o,e)}}k+="</table></div>";return k},grid_column_filter:function(f,d){var u=f.default_filter_dict;var m=f.filters;var b=d.label;var c=d.key;if(d.filterable=="advanced"){b=b.toLowerCase()}var l="<tr>";if(d.filterable=="advanced"){l+='<td align="left" style="padding-left: 10px">'+b+":</td>"}l+='<td style="padding-bottom: 1px;">';if(d.is_text){l+='<form class="text-filter-form" column_key="'+c+'" action="'+f.url+'" method="get" >';for(v in f.columns){var h=f.columns[v];var q=m[h.key];if(q){if(q!="All"){if(h.is_text){q=JSON.stringify(q)}l+='<input type="hidden" id="'+h.key+'" name="f-'+h.key+'" value="'+q+'"/>'}}}l+='<span id="'+c+'-filtering-criteria">';var k=m[c];if(k){var g=jQuery.type(k);if(g=="string"){if(k!="All"){l+=this.filter_element(c,k)}}if(g=="array"){for(var v in k){var o=k[v];var w=k;w=w.slice(v);l+=this.filter_element(c,o)}}}l+="</span>";var s="";if(d.filterable=="standard"){s=d.label.toLowerCase();var r=s.length;if(r<20){r=20}r=r+4}l+='<span class="search-box"><input class="search-box-input" id="input-'+c+'-filter" name="f-'+c+'" type="text" placeholder="'+s+'" size="'+r+'"/><button type="submit" style="background: transparent; border: none; padding: 4px; margin: 0px;"><i class="fa fa-search"></i></button></span></form>'}else{l+='<span id="'+c+'-filtering-criteria">';var t=false;for(cf_label in f.categorical_filters[c]){var p=f.categorical_filters[c][cf_label];var e="";var n="";for(key in p){e=key;n=p[key]}if(t){l+=" | "}t=true;var o=m[c];if(o&&p[c]&&o==n){l+='<span class="categorical-filter '+c+'-filter current-filter">'+cf_label+"</span>"}else{l+='<span class="categorical-filter '+c+'-filter"><a href="javascript:void(0);" filter_key="'+e+'" filter_val="'+n+'">'+cf_label+"</a></span>"}}l+="</span>"}l+="</td></tr>";return l},filter_element:function(c,b){b=a.sanitize(b);return'<span class="text-filter-val">'+b+'<a href="javascript:void(0);" filter_key="'+c+'" filter_val="'+b+'"><i class="fa fa-times" style="padding-left: 5px; padding-bottom: 6px;"/></a></span>'}}});
\ No newline at end of file
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a static/scripts/packed/utils/utils.js
--- a/static/scripts/packed/utils/utils.js
+++ b/static/scripts/packed/utils/utils.js
@@ -1,1 +1,1 @@
-define(["libs/underscore"],function(j){function d(l,m,k){g("GET",l,{},m,k)}function g(p,l,m,o,k){if(p=="GET"||p=="DELETE"){if(l.indexOf("?")==-1){l+="?"}else{l+="&"}l+=$.param(m)}var n=new XMLHttpRequest();n.open(p,l,true);n.setRequestHeader("Accept","application/json");n.setRequestHeader("Cache-Control","no-cache");n.setRequestHeader("X-Requested-With","XMLHttpRequest");n.setRequestHeader("Content-Type","application/json");n.onloadend=function(){var q=n.status;try{response=jQuery.parseJSON(n.responseText)}catch(r){response=n.responseText}if(q==200){o&&o(response)}else{k&&k(response)}};if(p=="GET"||p=="DELETE"){n.send()}else{n.send(JSON.stringify(m))}}function h(n,k){var l=$('<div class="'+n+'"></div>');l.appendTo(":eq(0)");var m=l.css(k);l.remove();return m}function f(k){if(!$('link[href^="'+k+'"]').length){$('<link href="'+galaxy_config.root+k+'" rel="stylesheet">').appendTo("head")}}function i(k,l){if(k){return j.defaults(k,l)}else{return l}}function b(l,n){var m="";if(l>=100000000000){l=l/100000000000;m="TB"}else{if(l>=100000000){l=l/100000000;m="GB"}else{if(l>=100000){l=l/100000;m="MB"}else{if(l>=100){l=l/100;m="KB"}else{if(l>0){l=l*10;m="b"}else{return"<strong>-</strong>"}}}}}var k=(Math.round(l)/10);if(n){return k+" "+m}else{return"<strong>"+k+"</strong> "+m}}function a(){return"x"+Math.random().toString(36).substring(2,9)}function c(k){var l=$("<p></p>");l.append(k);return l}function e(){var m=new Date();var k=(m.getHours()<10?"0":"")+m.getHours();var l=(m.getMinutes()<10?"0":"")+m.getMinutes();var n=m.getDate()+"/"+(m.getMonth()+1)+"/"+m.getFullYear()+", "+k+":"+l;return n}return{cssLoadFile:f,cssGetAttribute:h,get:d,merge:i,bytesToString:b,uuid:a,time:e,wrap:c,request:g}});
\ No newline at end of file
+define(["libs/underscore"],function(k){function d(l){return $("<div/>").text(l).html()}function e(m,n,l){h("GET",m,{},n,l)}function h(q,m,n,p,l){if(q=="GET"||q=="DELETE"){if(m.indexOf("?")==-1){m+="?"}else{m+="&"}m+=$.param(n)}var o=new XMLHttpRequest();o.open(q,m,true);o.setRequestHeader("Accept","application/json");o.setRequestHeader("Cache-Control","no-cache");o.setRequestHeader("X-Requested-With","XMLHttpRequest");o.setRequestHeader("Content-Type","application/json");o.onloadend=function(){var r=o.status;try{response=jQuery.parseJSON(o.responseText)}catch(s){response=o.responseText}if(r==200){p&&p(response)}else{l&&l(response)}};if(q=="GET"||q=="DELETE"){o.send()}else{o.send(JSON.stringify(n))}}function i(o,l){var m=$('<div class="'+o+'"></div>');m.appendTo(":eq(0)");var n=m.css(l);m.remove();return n}function g(l){if(!$('link[href^="'+l+'"]').length){$('<link href="'+galaxy_config.root+l+'" rel="stylesheet">').appendTo("head")}}function j(l,m){if(l){return k.defaults(l,m)}else{return m}}function b(m,o){var n="";if(m>=100000000000){m=m/100000000000;n="TB"}else{if(m>=100000000){m=m/100000000;n="GB"}else{if(m>=100000){m=m/100000;n="MB"}else{if(m>=100){m=m/100;n="KB"}else{if(m>0){m=m*10;n="b"}else{return"<strong>-</strong>"}}}}}var l=(Math.round(m)/10);if(o){return l+" "+n}else{return"<strong>"+l+"</strong> "+n}}function a(){return"x"+Math.random().toString(36).substring(2,9)}function c(l){var m=$("<p></p>");m.append(l);return m}function f(){var n=new Date();var l=(n.getHours()<10?"0":"")+n.getHours();var m=(n.getMinutes()<10?"0":"")+n.getMinutes();var o=n.getDate()+"/"+(n.getMonth()+1)+"/"+n.getFullYear()+", "+l+":"+m;return o}return{cssLoadFile:g,cssGetAttribute:i,get:e,merge:j,bytesToString:b,uuid:a,time:f,wrap:c,request:h,sanitize:d}});
\ No newline at end of file
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a static/scripts/utils/utils.js
--- a/static/scripts/utils/utils.js
+++ b/static/scripts/utils/utils.js
@@ -6,6 +6,14 @@
// dependencies
define(["libs/underscore"], function(_) {
+/**
+ * Sanitize/escape a string
+ * @param{String} content - Content to be sanitized
+ */
+function sanitize(content) {
+ return $('<div/>').text(content).html();
+};
+
// generic function to recieve json from url
function get (url, success, error) {
request('GET', url, {}, success, error);
@@ -151,7 +159,8 @@
uuid: uuid,
time: time,
wrap: wrap,
- request: request
+ request: request,
+ sanitize: sanitize
};
});
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a templates/webapps/galaxy/tracks/history_datasets_select_grid.mako
--- a/templates/webapps/galaxy/tracks/history_datasets_select_grid.mako
+++ b/templates/webapps/galaxy/tracks/history_datasets_select_grid.mako
@@ -1,5 +1,5 @@
<%inherit file="/tracks/history_select_grid.mako"/><%def name="title()">
- <h2>History '${grid.get_current_item( trans, **kwargs ).name}'</h2>
+ <h2>History '${grid.get_current_item( trans, **kwargs ).name | h}'</h2></%def>
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a templates/webapps/galaxy/tracks/index.mako
--- a/templates/webapps/galaxy/tracks/index.mako
+++ /dev/null
@@ -1,38 +0,0 @@
-<form id="form" method="POST">
- <div class="form-row">
- <label for="dbkey">Browser name:</label>
- <div class="form-row-input">
- <input type="text" name="title" id="title" value="Unnamed Browser"></input>
- </div>
- <div style="clear: both;"></div>
- </div>
- <div class="form-row">
- <label for="dbkey">Reference genome build (dbkey): </label>
- <div class="form-row-input">
- <select name="dbkey" id="dbkey" refresh_on_change="true">
- %for tmp_dbkey in dbkey_set:
- <option value="${tmp_dbkey}"
- %if tmp_dbkey == dbkey:
- selected="selected"
- %endif
- >${tmp_dbkey}</option>
- %endfor
- </select>
- </div>
- <div style="clear: both;"></div>
- </div>
- <div class="form-row">
- <label for="dataset_ids">Datasets to visualize: (${", ".join(available_tracks)} files are supported)</label>
- %for dataset_id, (dataset_ext, dataset_name) in datasets.iteritems():
- <div>
- <input type="checkbox" id="${dataset_id}" name="dataset_ids" value="${dataset_id}" />
- <label style="display:inline; font-weight: normal" for="${dataset_id}">[${dataset_ext}] ${dataset_name}</label>
- </div>
- %endfor
-
- <div style="clear: both;"></div>
- </div>
- <div class="form-row">
- <input type="submit" name="browse" value="Browse"/>
- </div>
-</form>
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a templates/webapps/galaxy/tracks/library_datasets_select_grid.mako
--- a/templates/webapps/galaxy/tracks/library_datasets_select_grid.mako
+++ b/templates/webapps/galaxy/tracks/library_datasets_select_grid.mako
@@ -2,7 +2,7 @@
<%namespace file='/library/common/browse_library.mako' import="render_content, grid_javascripts" /><%def name="title()">
- <h2>History '${grid.get_current_item( trans, **kwargs ).name}'</h2>
+ <h2>History '${grid.get_current_item( trans, **kwargs ).name | h}'</h2></%def>
${select_header()}
diff -r 0c8ac7330cdd467e9c7ef6f02778dca07a577b79 -r 704efd2f5e06b9f6e63246d2324874cf7f28d83a templates/webapps/galaxy/visualization/phyloviz.mako
--- a/templates/webapps/galaxy/visualization/phyloviz.mako
+++ b/templates/webapps/galaxy/visualization/phyloviz.mako
@@ -180,7 +180,6 @@
<%def name="center_panel()">
-
<div class="unified-panel-header" unselectable="on"><div class="unified-panel-header-inner"><div style="float:left;" id="title"></div>
@@ -196,7 +195,7 @@
<p>Select a tree to view:
<select id="phylovizNexSelector">
% for tree, index in data["trees"]:
- <option value="${index}">${tree}</option>
+ <option value="${index | h}">${tree | h}</option>
% endfor
</select></p>
Repository URL: https://bitbucket.org/galaxy/galaxy-central/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
1
0