galaxy-commits
Threads by month
- ----- 2026 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 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
- 15302 discussions
2 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/bce30adc75e9/
Changeset: bce30adc75e9
Branch: next-stable
User: carlfeberhard
Date: 2014-12-09 17:18:31+00:00
Summary: Apply stable 782cf1a to next-stable
Affected #: 1 file
diff -r 59d5d556b6647c245fc26f331eb01e9d597c698a -r bce30adc75e94c99fc8fd53f3dee75a26b6c8e86 templates/webapps/galaxy/galaxy.masthead.mako
--- a/templates/webapps/galaxy/galaxy.masthead.mako
+++ b/templates/webapps/galaxy/galaxy.masthead.mako
@@ -1,4 +1,4 @@
-<%namespace file="/galaxy_client_app.mako" import="get_user_json" />
+<%namespace file="/galaxy_client_app.mako" import="get_user_dict" />
## masthead head generator
<%def name="load(active_view = None)">
@@ -34,7 +34,7 @@
'requests' : bool(trans.user and (trans.user.requests or trans.app.security_agent.get_accessible_request_types(trans, trans.user))),
'email' : trans.user.email if (trans.user) else "",
'valid' : bool(trans.user != None),
- 'json' : get_user_json()
+ 'json' : get_user_dict()
}
}
%>
https://bitbucket.org/galaxy/galaxy-central/commits/be3f185f9e75/
Changeset: be3f185f9e75
User: carlfeberhard
Date: 2014-12-09 17:18:44+00:00
Summary: merge
Affected #: 1 file
diff -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 -r be3f185f9e75c4a7fc8ecf689ad0d26b98275026 templates/webapps/galaxy/galaxy.masthead.mako
--- a/templates/webapps/galaxy/galaxy.masthead.mako
+++ b/templates/webapps/galaxy/galaxy.masthead.mako
@@ -1,4 +1,4 @@
-<%namespace file="/galaxy_client_app.mako" import="get_user_json" />
+<%namespace file="/galaxy_client_app.mako" import="get_user_dict" />
## masthead head generator
<%def name="load(active_view = None)">
@@ -34,7 +34,7 @@
'requests' : bool(trans.user and (trans.user.requests or trans.app.security_agent.get_accessible_request_types(trans, trans.user))),
'email' : trans.user.email if (trans.user) else "",
'valid' : bool(trans.user != None),
- 'json' : get_user_json()
+ 'json' : get_user_dict()
}
}
%>
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 search input plugin from ui.js
by commits-noreply@bitbucket.org 09 Dec '14
by commits-noreply@bitbucket.org 09 Dec '14
09 Dec '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/33f1bf5be91b/
Changeset: 33f1bf5be91b
User: carlfeberhard
Date: 2014-12-09 17:16:01+00:00
Summary: Client build: modularize search input plugin from ui.js
Affected #: 19 files
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 client/galaxy/scripts/jq-plugins/ui/search-input.js
--- /dev/null
+++ b/client/galaxy/scripts/jq-plugins/ui/search-input.js
@@ -0,0 +1,159 @@
+// 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) {
+ //TODO: So...this turns out to be an all or nothing thing. If I load jQuery in the define below, it will
+ // (of course) wipe the old jquery *and all the plugins loaded into it*. So the define below *is still
+ // relying on jquery being loaded globally* in order to preserve plugins.
+ define([], factory);
+ } else {
+ // Browser globals
+ factory(jQuery);
+ }
+
+}(function () {
+ var _l = window._l || function( s ){ return s; };
+
+ /** searchInput: (jQuery plugin)
+ * Creates a search input, a clear button, and loading indicator
+ * within the selected node.
+ *
+ * When the user either presses return or enters some minimal number
+ * of characters, a callback is called. Pressing ESC when the input
+ * is focused will clear the input and call a separate callback.
+ */
+ function searchInput( parentNode, options ){
+//TODO: consolidate with tool menu functionality, use there
+ var KEYCODE_ESC = 27,
+ KEYCODE_RETURN = 13,
+ $parentNode = $( parentNode ),
+ firstSearch = true,
+ defaults = {
+ initialVal : '',
+ name : 'search',
+ placeholder : 'search',
+ classes : '',
+ onclear : function(){},
+ onfirstsearch : null,
+ onsearch : function( inputVal ){},
+ minSearchLen : 0,
+ escWillClear : true,
+ oninit : function(){}
+ };
+
+ // .................................................................... input rendering and events
+ // visually clear the search, trigger an event, and call the callback
+ function clearSearchInput( event ){
+ var $input = $( this ).parent().children( 'input' );
+ //console.debug( this, 'clear', $input );
+ $input.focus().val( '' ).trigger( 'clear:searchInput' );
+ options.onclear();
+ }
+
+ // search for searchTerms, trigger an event, call the appropo callback (based on whether this is the first)
+ function search( event, searchTerms ){
+ //console.debug( this, 'searching', searchTerms );
+ //TODO: I don't think this is classic jq custom event form? search.searchInput?
+ $( this ).trigger( 'search:searchInput', searchTerms );
+ if( typeof options.onfirstsearch === 'function' && firstSearch ){
+ firstSearch = false;
+ options.onfirstsearch( searchTerms );
+ } else {
+ options.onsearch( searchTerms );
+ }
+ }
+
+ // .................................................................... input rendering and events
+ function inputTemplate(){
+ // class search-query is bootstrap 2.3 style that now lives in base.less
+ return [ '<input type="text" name="', options.name, '" placeholder="', options.placeholder, '" ',
+ 'class="search-query ', options.classes, '" ', '/>' ].join( '' );
+ }
+
+ // the search input that responds to keyboard events and displays the search value
+ function $input(){
+ return $( inputTemplate() )
+ // select all text on a focus
+ .focus( function( event ){
+ $( this ).select();
+ })
+ // attach behaviors to esc, return if desired, search on some min len string
+ .keyup( function( event ){
+ event.preventDefault();
+ event.stopPropagation();
+//TODO: doesn't work
+ if( !$( this ).val() ){ $( this ).blur(); }
+
+ // esc key will clear if desired
+ if( event.which === KEYCODE_ESC && options.escWillClear ){
+ clearSearchInput.call( this, event );
+
+ } else {
+ var searchTerms = $( this ).val();
+ // return key or the search string len > minSearchLen (if not 0) triggers search
+ if( ( event.which === KEYCODE_RETURN )
+ || ( options.minSearchLen && searchTerms.length >= options.minSearchLen ) ){
+ search.call( this, event, searchTerms );
+ } else if( !searchTerms.length ){
+ clearSearchInput.call( this, event );
+ }
+ }
+ })
+ .on( 'change', function( event ){
+ search.call( this, event, $( this ).val() );
+ })
+ .val( options.initialVal );
+ }
+
+ // .................................................................... clear button rendering and events
+ // a button for clearing the search bar, placed on the right hand side
+ function $clearBtn(){
+ return $([ '<span class="search-clear fa fa-times-circle" ',
+ 'title="', _l( 'clear search (esc)' ), '"></span>' ].join('') )
+ .tooltip({ placement: 'bottom' })
+ .click( function( event ){
+ clearSearchInput.call( this, event );
+ });
+ }
+
+ // .................................................................... loadingIndicator rendering
+ // a button for clearing the search bar, placed on the right hand side
+ function $loadingIndicator(){
+ return $([ '<span class="search-loading fa fa-spinner fa-spin" ',
+ 'title="', _l( 'loading...' ), '"></span>' ].join('') )
+ .hide().tooltip({ placement: 'bottom' });
+ }
+
+ // .................................................................... commands
+ // visually swap the load, clear buttons
+ function toggleLoadingIndicator(){
+ $parentNode.find( '.search-loading' ).toggle();
+ $parentNode.find( '.search-clear' ).toggle();
+ }
+
+ // .................................................................... init
+ // string command (not constructor)
+ if( jQuery.type( options ) === 'string' ){
+ if( options === 'toggle-loading' ){
+ toggleLoadingIndicator();
+ }
+ return $parentNode;
+ }
+
+ // initial render
+ if( jQuery.type( options ) === 'object' ){
+ options = jQuery.extend( true, {}, defaults, options );
+ }
+ //NOTE: prepended
+ return $parentNode.addClass( 'search-input' ).prepend([ $input(), $clearBtn(), $loadingIndicator() ]);
+ }
+
+ // as jq plugin
+ jQuery.fn.extend({
+ searchInput : function $searchInput( options ){
+ return this.each( function(){
+ return searchInput( this, options );
+ });
+ }
+ });
+}));
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 client/galaxy/scripts/mvc/history/history-panel.js
--- a/client/galaxy/scripts/mvc/history/history-panel.js
+++ b/client/galaxy/scripts/mvc/history/history-panel.js
@@ -7,7 +7,8 @@
"mvc/collection/collection-panel",
"mvc/user/user-model",
"mvc/base-mvc",
- "utils/localization"
+ "utils/localization",
+ "jq-plugins/ui/search-input"
], function(
LIST_PANEL,
HISTORY_MODEL,
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 client/galaxy/scripts/mvc/history/multi-panel.js
--- a/client/galaxy/scripts/mvc/history/multi-panel.js
+++ b/client/galaxy/scripts/mvc/history/multi-panel.js
@@ -3,7 +3,8 @@
"mvc/history/history-panel-edit",
"mvc/base-mvc",
"utils/ajax-queue",
- "jq-plugins/ui/mode-button"
+ "jq-plugins/ui/mode-button",
+ "jq-plugins/ui/search-input"
], function( HISTORY_MODEL, HPANEL_EDIT, baseMVC, ajaxQueue ){
window.HISTORY_MODEL = HISTORY_MODEL;
//==============================================================================
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 client/galaxy/scripts/mvc/list/list-panel.js
--- a/client/galaxy/scripts/mvc/list/list-panel.js
+++ b/client/galaxy/scripts/mvc/list/list-panel.js
@@ -1,7 +1,8 @@
define([
"mvc/list/list-item",
"mvc/base-mvc",
- "utils/localization"
+ "utils/localization",
+ "jq-plugins/ui/search-input"
], function( LIST_ITEM, BASE_MVC, _l ){
/* ============================================================================
TODO:
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 client/galaxy/scripts/mvc/ui.js
--- a/client/galaxy/scripts/mvc/ui.js
+++ b/client/galaxy/scripts/mvc/ui.js
@@ -601,151 +601,3 @@
};
return self;
}
-
-//==============================================================================
-(function(){
- /** searchInput: (jQuery plugin)
- * Creates a search input, a clear button, and loading indicator
- * within the selected node.
- *
- * When the user either presses return or enters some minimal number
- * of characters, a callback is called. Pressing ESC when the input
- * is focused will clear the input and call a separate callback.
- */
- var _l = window._l || function( s ){ return s; };
-
- // contructor
- function searchInput( parentNode, options ){
-//TODO: consolidate with tool menu functionality, use there
- var KEYCODE_ESC = 27,
- KEYCODE_RETURN = 13,
- $parentNode = $( parentNode ),
- firstSearch = true,
- defaults = {
- initialVal : '',
- name : 'search',
- placeholder : 'search',
- classes : '',
- onclear : function(){},
- onfirstsearch : null,
- onsearch : function( inputVal ){},
- minSearchLen : 0,
- escWillClear : true,
- oninit : function(){}
- };
-
- // .................................................................... input rendering and events
- // visually clear the search, trigger an event, and call the callback
- function clearSearchInput( event ){
- var $input = $( this ).parent().children( 'input' );
- //console.debug( this, 'clear', $input );
- $input.focus().val( '' ).trigger( 'clear:searchInput' );
- options.onclear();
- }
-
- // search for searchTerms, trigger an event, call the appropo callback (based on whether this is the first)
- function search( event, searchTerms ){
- //console.debug( this, 'searching', searchTerms );
- $( this ).trigger( 'search:searchInput', searchTerms );
- if( typeof options.onfirstsearch === 'function' && firstSearch ){
- firstSearch = false;
- options.onfirstsearch( searchTerms );
- } else {
- options.onsearch( searchTerms );
- }
- }
-
- // .................................................................... input rendering and events
- function inputTemplate(){
- // class search-query is bootstrap 2.3 style that now lives in base.less
- return [ '<input type="text" name="', options.name, '" placeholder="', options.placeholder, '" ',
- 'class="search-query ', options.classes, '" ', '/>' ].join( '' );
- }
-
- // the search input that responds to keyboard events and displays the search value
- function $input(){
- return $( inputTemplate() )
- // select all text on a focus
- .focus( function( event ){
- $( this ).select();
- })
- // attach behaviors to esc, return if desired, search on some min len string
- .keyup( function( event ){
- event.preventDefault();
- event.stopPropagation();
-//TODO: doesn't work
- if( !$( this ).val() ){ $( this ).blur(); }
-
- // esc key will clear if desired
- if( event.which === KEYCODE_ESC && options.escWillClear ){
- clearSearchInput.call( this, event );
-
- } else {
- var searchTerms = $( this ).val();
- // return key or the search string len > minSearchLen (if not 0) triggers search
- if( ( event.which === KEYCODE_RETURN )
- || ( options.minSearchLen && searchTerms.length >= options.minSearchLen ) ){
- search.call( this, event, searchTerms );
- } else if( !searchTerms.length ){
- clearSearchInput.call( this, event );
- }
- }
- })
- .on( 'change', function( event ){
- search.call( this, event, $( this ).val() );
- })
- .val( options.initialVal );
- }
-
- // .................................................................... clear button rendering and events
- // a button for clearing the search bar, placed on the right hand side
- function $clearBtn(){
- return $([ '<span class="search-clear fa fa-times-circle" ',
- 'title="', _l( 'clear search (esc)' ), '"></span>' ].join('') )
- .tooltip({ placement: 'bottom' })
- .click( function( event ){
- clearSearchInput.call( this, event );
- });
- }
-
- // .................................................................... loadingIndicator rendering
- // a button for clearing the search bar, placed on the right hand side
- function $loadingIndicator(){
- return $([ '<span class="search-loading fa fa-spinner fa-spin" ',
- 'title="', _l( 'loading...' ), '"></span>' ].join('') )
- .hide().tooltip({ placement: 'bottom' });
- }
-
- // .................................................................... commands
- // visually swap the load, clear buttons
- function toggleLoadingIndicator(){
- $parentNode.find( '.search-loading' ).toggle();
- $parentNode.find( '.search-clear' ).toggle();
- }
-
- // .................................................................... init
- // string command (not constructor)
- if( jQuery.type( options ) === 'string' ){
- if( options === 'toggle-loading' ){
- toggleLoadingIndicator();
- }
- return $parentNode;
- }
-
- // initial render
- if( jQuery.type( options ) === 'object' ){
- options = jQuery.extend( true, {}, defaults, options );
- }
- //NOTE: prepended
- return $parentNode.addClass( 'search-input' ).prepend([ $input(), $clearBtn(), $loadingIndicator() ]);
- }
-
- // as jq plugin
- jQuery.fn.extend({
- searchInput : function $searchInput( options ){
- return this.each( function(){
- return searchInput( this, options );
- });
- }
- });
-}());
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 static/scripts/jq-plugins/ui/search-input.js
--- /dev/null
+++ b/static/scripts/jq-plugins/ui/search-input.js
@@ -0,0 +1,159 @@
+// 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) {
+ //TODO: So...this turns out to be an all or nothing thing. If I load jQuery in the define below, it will
+ // (of course) wipe the old jquery *and all the plugins loaded into it*. So the define below *is still
+ // relying on jquery being loaded globally* in order to preserve plugins.
+ define([], factory);
+ } else {
+ // Browser globals
+ factory(jQuery);
+ }
+
+}(function () {
+ var _l = window._l || function( s ){ return s; };
+
+ /** searchInput: (jQuery plugin)
+ * Creates a search input, a clear button, and loading indicator
+ * within the selected node.
+ *
+ * When the user either presses return or enters some minimal number
+ * of characters, a callback is called. Pressing ESC when the input
+ * is focused will clear the input and call a separate callback.
+ */
+ function searchInput( parentNode, options ){
+//TODO: consolidate with tool menu functionality, use there
+ var KEYCODE_ESC = 27,
+ KEYCODE_RETURN = 13,
+ $parentNode = $( parentNode ),
+ firstSearch = true,
+ defaults = {
+ initialVal : '',
+ name : 'search',
+ placeholder : 'search',
+ classes : '',
+ onclear : function(){},
+ onfirstsearch : null,
+ onsearch : function( inputVal ){},
+ minSearchLen : 0,
+ escWillClear : true,
+ oninit : function(){}
+ };
+
+ // .................................................................... input rendering and events
+ // visually clear the search, trigger an event, and call the callback
+ function clearSearchInput( event ){
+ var $input = $( this ).parent().children( 'input' );
+ //console.debug( this, 'clear', $input );
+ $input.focus().val( '' ).trigger( 'clear:searchInput' );
+ options.onclear();
+ }
+
+ // search for searchTerms, trigger an event, call the appropo callback (based on whether this is the first)
+ function search( event, searchTerms ){
+ //console.debug( this, 'searching', searchTerms );
+ //TODO: I don't think this is classic jq custom event form? search.searchInput?
+ $( this ).trigger( 'search:searchInput', searchTerms );
+ if( typeof options.onfirstsearch === 'function' && firstSearch ){
+ firstSearch = false;
+ options.onfirstsearch( searchTerms );
+ } else {
+ options.onsearch( searchTerms );
+ }
+ }
+
+ // .................................................................... input rendering and events
+ function inputTemplate(){
+ // class search-query is bootstrap 2.3 style that now lives in base.less
+ return [ '<input type="text" name="', options.name, '" placeholder="', options.placeholder, '" ',
+ 'class="search-query ', options.classes, '" ', '/>' ].join( '' );
+ }
+
+ // the search input that responds to keyboard events and displays the search value
+ function $input(){
+ return $( inputTemplate() )
+ // select all text on a focus
+ .focus( function( event ){
+ $( this ).select();
+ })
+ // attach behaviors to esc, return if desired, search on some min len string
+ .keyup( function( event ){
+ event.preventDefault();
+ event.stopPropagation();
+//TODO: doesn't work
+ if( !$( this ).val() ){ $( this ).blur(); }
+
+ // esc key will clear if desired
+ if( event.which === KEYCODE_ESC && options.escWillClear ){
+ clearSearchInput.call( this, event );
+
+ } else {
+ var searchTerms = $( this ).val();
+ // return key or the search string len > minSearchLen (if not 0) triggers search
+ if( ( event.which === KEYCODE_RETURN )
+ || ( options.minSearchLen && searchTerms.length >= options.minSearchLen ) ){
+ search.call( this, event, searchTerms );
+ } else if( !searchTerms.length ){
+ clearSearchInput.call( this, event );
+ }
+ }
+ })
+ .on( 'change', function( event ){
+ search.call( this, event, $( this ).val() );
+ })
+ .val( options.initialVal );
+ }
+
+ // .................................................................... clear button rendering and events
+ // a button for clearing the search bar, placed on the right hand side
+ function $clearBtn(){
+ return $([ '<span class="search-clear fa fa-times-circle" ',
+ 'title="', _l( 'clear search (esc)' ), '"></span>' ].join('') )
+ .tooltip({ placement: 'bottom' })
+ .click( function( event ){
+ clearSearchInput.call( this, event );
+ });
+ }
+
+ // .................................................................... loadingIndicator rendering
+ // a button for clearing the search bar, placed on the right hand side
+ function $loadingIndicator(){
+ return $([ '<span class="search-loading fa fa-spinner fa-spin" ',
+ 'title="', _l( 'loading...' ), '"></span>' ].join('') )
+ .hide().tooltip({ placement: 'bottom' });
+ }
+
+ // .................................................................... commands
+ // visually swap the load, clear buttons
+ function toggleLoadingIndicator(){
+ $parentNode.find( '.search-loading' ).toggle();
+ $parentNode.find( '.search-clear' ).toggle();
+ }
+
+ // .................................................................... init
+ // string command (not constructor)
+ if( jQuery.type( options ) === 'string' ){
+ if( options === 'toggle-loading' ){
+ toggleLoadingIndicator();
+ }
+ return $parentNode;
+ }
+
+ // initial render
+ if( jQuery.type( options ) === 'object' ){
+ options = jQuery.extend( true, {}, defaults, options );
+ }
+ //NOTE: prepended
+ return $parentNode.addClass( 'search-input' ).prepend([ $input(), $clearBtn(), $loadingIndicator() ]);
+ }
+
+ // as jq plugin
+ jQuery.fn.extend({
+ searchInput : function $searchInput( options ){
+ return this.each( function(){
+ return searchInput( this, options );
+ });
+ }
+ });
+}));
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 static/scripts/mvc/history/history-panel.js
--- a/static/scripts/mvc/history/history-panel.js
+++ b/static/scripts/mvc/history/history-panel.js
@@ -7,7 +7,8 @@
"mvc/collection/collection-panel",
"mvc/user/user-model",
"mvc/base-mvc",
- "utils/localization"
+ "utils/localization",
+ "jq-plugins/ui/search-input"
], function(
LIST_PANEL,
HISTORY_MODEL,
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 static/scripts/mvc/history/multi-panel.js
--- a/static/scripts/mvc/history/multi-panel.js
+++ b/static/scripts/mvc/history/multi-panel.js
@@ -3,7 +3,8 @@
"mvc/history/history-panel-edit",
"mvc/base-mvc",
"utils/ajax-queue",
- "jq-plugins/ui/mode-button"
+ "jq-plugins/ui/mode-button",
+ "jq-plugins/ui/search-input"
], function( HISTORY_MODEL, HPANEL_EDIT, baseMVC, ajaxQueue ){
window.HISTORY_MODEL = HISTORY_MODEL;
//==============================================================================
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 static/scripts/mvc/list/list-panel.js
--- a/static/scripts/mvc/list/list-panel.js
+++ b/static/scripts/mvc/list/list-panel.js
@@ -1,7 +1,8 @@
define([
"mvc/list/list-item",
"mvc/base-mvc",
- "utils/localization"
+ "utils/localization",
+ "jq-plugins/ui/search-input"
], function( LIST_ITEM, BASE_MVC, _l ){
/* ============================================================================
TODO:
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 static/scripts/mvc/ui.js
--- a/static/scripts/mvc/ui.js
+++ b/static/scripts/mvc/ui.js
@@ -601,151 +601,3 @@
};
return self;
}
-
-//==============================================================================
-(function(){
- /** searchInput: (jQuery plugin)
- * Creates a search input, a clear button, and loading indicator
- * within the selected node.
- *
- * When the user either presses return or enters some minimal number
- * of characters, a callback is called. Pressing ESC when the input
- * is focused will clear the input and call a separate callback.
- */
- var _l = window._l || function( s ){ return s; };
-
- // contructor
- function searchInput( parentNode, options ){
-//TODO: consolidate with tool menu functionality, use there
- var KEYCODE_ESC = 27,
- KEYCODE_RETURN = 13,
- $parentNode = $( parentNode ),
- firstSearch = true,
- defaults = {
- initialVal : '',
- name : 'search',
- placeholder : 'search',
- classes : '',
- onclear : function(){},
- onfirstsearch : null,
- onsearch : function( inputVal ){},
- minSearchLen : 0,
- escWillClear : true,
- oninit : function(){}
- };
-
- // .................................................................... input rendering and events
- // visually clear the search, trigger an event, and call the callback
- function clearSearchInput( event ){
- var $input = $( this ).parent().children( 'input' );
- //console.debug( this, 'clear', $input );
- $input.focus().val( '' ).trigger( 'clear:searchInput' );
- options.onclear();
- }
-
- // search for searchTerms, trigger an event, call the appropo callback (based on whether this is the first)
- function search( event, searchTerms ){
- //console.debug( this, 'searching', searchTerms );
- $( this ).trigger( 'search:searchInput', searchTerms );
- if( typeof options.onfirstsearch === 'function' && firstSearch ){
- firstSearch = false;
- options.onfirstsearch( searchTerms );
- } else {
- options.onsearch( searchTerms );
- }
- }
-
- // .................................................................... input rendering and events
- function inputTemplate(){
- // class search-query is bootstrap 2.3 style that now lives in base.less
- return [ '<input type="text" name="', options.name, '" placeholder="', options.placeholder, '" ',
- 'class="search-query ', options.classes, '" ', '/>' ].join( '' );
- }
-
- // the search input that responds to keyboard events and displays the search value
- function $input(){
- return $( inputTemplate() )
- // select all text on a focus
- .focus( function( event ){
- $( this ).select();
- })
- // attach behaviors to esc, return if desired, search on some min len string
- .keyup( function( event ){
- event.preventDefault();
- event.stopPropagation();
-//TODO: doesn't work
- if( !$( this ).val() ){ $( this ).blur(); }
-
- // esc key will clear if desired
- if( event.which === KEYCODE_ESC && options.escWillClear ){
- clearSearchInput.call( this, event );
-
- } else {
- var searchTerms = $( this ).val();
- // return key or the search string len > minSearchLen (if not 0) triggers search
- if( ( event.which === KEYCODE_RETURN )
- || ( options.minSearchLen && searchTerms.length >= options.minSearchLen ) ){
- search.call( this, event, searchTerms );
- } else if( !searchTerms.length ){
- clearSearchInput.call( this, event );
- }
- }
- })
- .on( 'change', function( event ){
- search.call( this, event, $( this ).val() );
- })
- .val( options.initialVal );
- }
-
- // .................................................................... clear button rendering and events
- // a button for clearing the search bar, placed on the right hand side
- function $clearBtn(){
- return $([ '<span class="search-clear fa fa-times-circle" ',
- 'title="', _l( 'clear search (esc)' ), '"></span>' ].join('') )
- .tooltip({ placement: 'bottom' })
- .click( function( event ){
- clearSearchInput.call( this, event );
- });
- }
-
- // .................................................................... loadingIndicator rendering
- // a button for clearing the search bar, placed on the right hand side
- function $loadingIndicator(){
- return $([ '<span class="search-loading fa fa-spinner fa-spin" ',
- 'title="', _l( 'loading...' ), '"></span>' ].join('') )
- .hide().tooltip({ placement: 'bottom' });
- }
-
- // .................................................................... commands
- // visually swap the load, clear buttons
- function toggleLoadingIndicator(){
- $parentNode.find( '.search-loading' ).toggle();
- $parentNode.find( '.search-clear' ).toggle();
- }
-
- // .................................................................... init
- // string command (not constructor)
- if( jQuery.type( options ) === 'string' ){
- if( options === 'toggle-loading' ){
- toggleLoadingIndicator();
- }
- return $parentNode;
- }
-
- // initial render
- if( jQuery.type( options ) === 'object' ){
- options = jQuery.extend( true, {}, defaults, options );
- }
- //NOTE: prepended
- return $parentNode.addClass( 'search-input' ).prepend([ $input(), $clearBtn(), $loadingIndicator() ]);
- }
-
- // as jq plugin
- jQuery.fn.extend({
- searchInput : function $searchInput( options ){
- return this.each( function(){
- return searchInput( this, options );
- });
- }
- });
-}());
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 static/scripts/packed/jq-plugins/ui/search-input.js
--- /dev/null
+++ b/static/scripts/packed/jq-plugins/ui/search-input.js
@@ -0,0 +1,1 @@
+(function(a){if(typeof define==="function"&&define.amd){define([],a)}else{a(jQuery)}}(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)})}})}));
\ No newline at end of file
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 static/scripts/packed/mvc/history/history-panel.js
--- a/static/scripts/packed/mvc/history/history-panel.js
+++ b/static/scripts/packed/mvc/history/history-panel.js
@@ -1,1 +1,1 @@
-define(["mvc/list/list-panel","mvc/history/history-model","mvc/history/history-contents","mvc/history/hda-li","mvc/history/hdca-li","mvc/collection/collection-panel","mvc/user/user-model","mvc/base-mvc","utils/localization"],function(d,f,l,b,a,m,g,o,e){var j=o.SessionStorageModel.extend({defaults:{expandedIds:{},show_deleted:false,show_hidden:false},addExpanded:function(p){var q="expandedIds";this.save(q,_.extend(this.get(q),_.object([p.id],[p.get("id")])))},removeExpanded:function(p){var q="expandedIds";this.save(q,_.omit(this.get(q),p.id))},toString:function(){return"HistoryPrefs("+this.id+")"}});j.storageKeyPrefix="history:";j.historyStorageKey=function h(p){if(!p){throw new Error("HistoryPrefs.historyStorageKey needs valid id: "+p)}return(j.storageKeyPrefix+p)};j.get=function c(p){return new j({id:j.historyStorageKey(p)})};j.clearAll=function i(q){for(var p in sessionStorage){if(p.indexOf(j.storageKeyPrefix)===0){sessionStorage.removeItem(p)}}};var n=d.ModelListPanel;var k=n.extend({HDAViewClass:b.HDAListItemView,HDCAViewClass:a.HDCAListItemView,collectionClass:l.HistoryContents,modelCollectionKey:"contents",tagName:"div",className:n.prototype.className+" history-panel",emptyMsg:e("This history is empty"),noneFoundMsg:e("No matching datasets found"),searchPlaceholder:e("search datasets"),initialize:function(p){n.prototype.initialize.call(this,p);this.linkTarget=p.linkTarget||"_blank"},freeModel:function(){n.prototype.freeModel.call(this);if(this.model){this.model.clearUpdateTimeout()}return this},_setUpListeners:function(){n.prototype._setUpListeners.call(this);this.on("error",function(q,t,p,s,r){this.errorHandler(q,t,p,s,r)});this.on("loading-done",function(){if(!this.views.length){this.trigger("empty-history",this)}})},loadHistoryWithDetails:function(s,r,q,t){this.info("loadHistoryWithDetails:",s,r,q,t);var p=function(u){return _.values(j.get(u.id).get("expandedIds"))};return this.loadHistory(s,r,q,t,p)},loadHistory:function(t,s,r,u,p){this.info("loadHistory:",t,s,r,u,p);var q=this;s=s||{};q.trigger("loading",q);var v=f.History.getHistoryData(t,{historyFn:r,contentsFn:u,detailIdsFn:s.initiallyExpanded||p});return q._loadHistoryFromXHR(v,s).fail(function(y,w,x){q.trigger("error",q,y,s,e("An error was encountered while "+w),{historyId:t,history:x||{}})}).always(function(){q.trigger("loading-done",q)})},_loadHistoryFromXHR:function(r,q){var p=this;r.then(function(s,t){p.JSONToModel(s,t,q);p.render()});r.fail(function(t,s){p.render()});return r},refreshContents:function(q,p){if(this.model){return this.model.refresh(q,p)}return $.when()},JSONToModel:function(s,p,q){this.log("JSONToModel:",s,p,q);q=q||{};var r=new f.History(s,p,q);this.setModel(r);return r},setModel:function(q,p){p=p||{};n.prototype.setModel.call(this,q,p);if(this.model){this._setUpWebStorage(p.initiallyExpanded,p.show_deleted,p.show_hidden)}},_setUpWebStorage:function(q,p,r){if(this.storage){this.stopListening(this.storage)}this.storage=new j({id:j.historyStorageKey(this.model.get("id"))});if(_.isObject(q)){this.storage.set("expandedIds",q)}if(_.isBoolean(p)){this.storage.set("show_deleted",p)}if(_.isBoolean(r)){this.storage.set("show_hidden",r)}this.trigger("new-storage",this.storage,this);this.log(this+" (init'd) storage:",this.storage.get());this.listenTo(this.storage,{"change:show_deleted":function(s,t){this.showDeleted=t},"change:show_hidden":function(s,t){this.showHidden=t}},this);this.showDeleted=(p!==undefined)?p:this.storage.get("show_deleted");this.showHidden=(r!==undefined)?r:this.storage.get("show_hidden");return this},_buildNewRender:function(){var p=n.prototype._buildNewRender.call(this);if(this.multiselectActions.length){p.find(".controls .actions").prepend(this._renderSelectButton())}return p},_renderSelectButton:function(p){return faIconButton({title:e("Operations on multiple datasets"),classes:"show-selectors-btn",faIcon:"fa-check-square-o"})},_getItemViewClass:function(p){var q=p.get("history_content_type");switch(q){case"dataset":return this.HDAViewClass;case"dataset_collection":return this.HDCAViewClass}throw new TypeError("Unknown history_content_type: "+q)},_filterItem:function(q){var p=this;return(n.prototype._filterItem.call(p,q)&&(!q.hidden()||p.showHidden)&&(!q.isDeletedOrPurged()||p.showDeleted))},_getItemViewOptions:function(q){var p=n.prototype._getItemViewOptions.call(this,q);return _.extend(p,{linkTarget:this.linkTarget,expanded:!!this.storage.get("expandedIds")[q.id],hasUser:this.model.ownedByCurrUser()})},_setUpItemViewListeners:function(q){var p=this;n.prototype._setUpItemViewListeners.call(p,q);q.on("expanded",function(r){p.storage.addExpanded(r.model)});q.on("collapsed",function(r){p.storage.removeExpanded(r.model)});return this},getSelectedModels:function(){var p=n.prototype.getSelectedModels.call(this);p.historyId=this.collection.historyId;return p},events:_.extend(_.clone(n.prototype.events),{"click .show-selectors-btn":"toggleSelectors"}),toggleShowDeleted:function(p,q){p=(p!==undefined)?(p):(!this.showDeleted);q=(q!==undefined)?(q):(true);this.showDeleted=p;if(q){this.storage.set("show_deleted",p)}this.trigger("show-hidden",p);this.renderItems();return this.showDeleted},toggleShowHidden:function(p,q){p=(p!==undefined)?(p):(!this.showHidden);q=(q!==undefined)?(q):(true);this.showHidden=p;if(q){this.storage.set("show_hidden",p)}this.trigger("show-hidden",p);this.renderItems();return this.showHidden},_firstSearch:function(p){var q=this,r=".history-search-input";this.log("onFirstSearch",p);if(q.model.contents.haveDetails()){q.searchItems(p);return}q.$el.find(r).searchInput("toggle-loading");q.model.contents.fetchAllDetails({silent:true}).always(function(){q.$el.find(r).searchInput("toggle-loading")}).done(function(){q.searchItems(p)})},errorHandler:function(r,u,q,t,s){this.error(r,u,q,t,s);if(u&&u.status===0&&u.readyState===0){}else{if(u&&u.status===502){}else{var p=this._parseErrorMessage(r,u,q,t,s);if(!this.$messages().is(":visible")){this.once("rendered",function(){this.displayMessage("error",p.message,p.details)})}else{this.displayMessage("error",p.message,p.details)}}}},_parseErrorMessage:function(t,w,x,r,p,u){var s=Galaxy.currUser,v={message:this._bePolite(r),details:{message:r,raven:(window.Raven&&_.isFunction(Raven.lastEventId))?(Raven.lastEventId()):(undefined),agent:navigator.userAgent,url:(window.Galaxy)?(Galaxy.lastAjax.url):(undefined),data:(window.Galaxy)?(Galaxy.lastAjax.data):(undefined),options:(w)?(_.omit(x,"xhr")):(x),xhr:w,source:(_.isFunction(t.toJSON))?(t.toJSON()):(t+""),user:(s instanceof g.User)?(s.toJSON()):(s+"")}};_.extend(v.details,p||{});if(w&&_.isFunction(w.getAllResponseHeaders)){var q=w.getAllResponseHeaders();q=_.compact(q.split("\n"));q=_.map(q,function(y){return y.split(": ")});v.details.xhr.responseHeaders=_.object(q)}return v},_bePolite:function(p){p=p||e("An error occurred while getting updates from the server");return p+". "+e("Please contact a Galaxy administrator if the problem persists")+"."},displayMessage:function(u,v,t){var r=this;this.scrollToTop();var s=this.$messages(),p=$("<div/>").addClass(u+"message").html(v);if(!_.isEmpty(t)){var q=$('<a href="javascript:void(0)">Details</a>').click(function(){Galaxy.modal.show(r._messageToModalOptions(u,v,t));return false});p.append(" ",q)}return s.html(p)},_messageToModalOptions:function(s,v,r){var p=this,q={title:"Details"};if(_.isObject(r)){r=_.omit(r,_.functions(r));var u=JSON.stringify(r,null," "),t=$("<pre/>").text(u);q.body=$("<div/>").append(t)}else{q.body=$("<div/>").html(r)}q.buttons={Ok:function(){Galaxy.modal.hide();p.clearMessages()}};return q},clearMessages:function(p){$(p.currentTarget).fadeOut(this.fxSpeed,function(){$(this).remove()});return this},scrollToHid:function(p){return this.scrollToItem(_.first(this.viewsWhereModel({hid:p})))},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});k.prototype.templates=(function(){var p=o.wrapTemplate(['<div class="controls">','<div class="title">','<div class="name"><%= history.name %></div>',"</div>",'<div class="subtitle"></div>','<div class="history-size"><%= history.nice_size %></div>','<div class="actions"></div>','<div class="messages">',"<% if( history.deleted ){ %>",'<div class="deleted-msg warningmessagesmall">',e("This history has been deleted"),"</div>","<% } %>","<% if( history.message ){ %>",'<div class="<%= history.message.level || "info" %>messagesmall">',"<%= history.message.text %>","</div>","<% } %>","</div>",'<div class="tags-display"></div>','<div class="annotation-display"></div>','<div class="search">','<div class="search-input"></div>',"</div>",'<div class="list-actions">','<div class="btn-group">','<button class="select-all btn btn-default"','data-mode="select">',e("All"),"</button>",'<button class="deselect-all btn btn-default"','data-mode="select">',e("None"),"</button>","</div>",'<button class="list-action-popup-btn btn btn-default">',e("For all selected"),"...</button>","</div>","</div>"],"history");return _.extend(_.clone(n.prototype.templates),{controls:p})}());return{HistoryPanel:k}});
\ No newline at end of file
+define(["mvc/list/list-panel","mvc/history/history-model","mvc/history/history-contents","mvc/history/hda-li","mvc/history/hdca-li","mvc/collection/collection-panel","mvc/user/user-model","mvc/base-mvc","utils/localization","jq-plugins/ui/search-input"],function(d,f,l,b,a,m,g,o,e){var j=o.SessionStorageModel.extend({defaults:{expandedIds:{},show_deleted:false,show_hidden:false},addExpanded:function(p){var q="expandedIds";this.save(q,_.extend(this.get(q),_.object([p.id],[p.get("id")])))},removeExpanded:function(p){var q="expandedIds";this.save(q,_.omit(this.get(q),p.id))},toString:function(){return"HistoryPrefs("+this.id+")"}});j.storageKeyPrefix="history:";j.historyStorageKey=function h(p){if(!p){throw new Error("HistoryPrefs.historyStorageKey needs valid id: "+p)}return(j.storageKeyPrefix+p)};j.get=function c(p){return new j({id:j.historyStorageKey(p)})};j.clearAll=function i(q){for(var p in sessionStorage){if(p.indexOf(j.storageKeyPrefix)===0){sessionStorage.removeItem(p)}}};var n=d.ModelListPanel;var k=n.extend({HDAViewClass:b.HDAListItemView,HDCAViewClass:a.HDCAListItemView,collectionClass:l.HistoryContents,modelCollectionKey:"contents",tagName:"div",className:n.prototype.className+" history-panel",emptyMsg:e("This history is empty"),noneFoundMsg:e("No matching datasets found"),searchPlaceholder:e("search datasets"),initialize:function(p){n.prototype.initialize.call(this,p);this.linkTarget=p.linkTarget||"_blank"},freeModel:function(){n.prototype.freeModel.call(this);if(this.model){this.model.clearUpdateTimeout()}return this},_setUpListeners:function(){n.prototype._setUpListeners.call(this);this.on("error",function(q,t,p,s,r){this.errorHandler(q,t,p,s,r)});this.on("loading-done",function(){if(!this.views.length){this.trigger("empty-history",this)}})},loadHistoryWithDetails:function(s,r,q,t){this.info("loadHistoryWithDetails:",s,r,q,t);var p=function(u){return _.values(j.get(u.id).get("expandedIds"))};return this.loadHistory(s,r,q,t,p)},loadHistory:function(t,s,r,u,p){this.info("loadHistory:",t,s,r,u,p);var q=this;s=s||{};q.trigger("loading",q);var v=f.History.getHistoryData(t,{historyFn:r,contentsFn:u,detailIdsFn:s.initiallyExpanded||p});return q._loadHistoryFromXHR(v,s).fail(function(y,w,x){q.trigger("error",q,y,s,e("An error was encountered while "+w),{historyId:t,history:x||{}})}).always(function(){q.trigger("loading-done",q)})},_loadHistoryFromXHR:function(r,q){var p=this;r.then(function(s,t){p.JSONToModel(s,t,q);p.render()});r.fail(function(t,s){p.render()});return r},refreshContents:function(q,p){if(this.model){return this.model.refresh(q,p)}return $.when()},JSONToModel:function(s,p,q){this.log("JSONToModel:",s,p,q);q=q||{};var r=new f.History(s,p,q);this.setModel(r);return r},setModel:function(q,p){p=p||{};n.prototype.setModel.call(this,q,p);if(this.model){this._setUpWebStorage(p.initiallyExpanded,p.show_deleted,p.show_hidden)}},_setUpWebStorage:function(q,p,r){if(this.storage){this.stopListening(this.storage)}this.storage=new j({id:j.historyStorageKey(this.model.get("id"))});if(_.isObject(q)){this.storage.set("expandedIds",q)}if(_.isBoolean(p)){this.storage.set("show_deleted",p)}if(_.isBoolean(r)){this.storage.set("show_hidden",r)}this.trigger("new-storage",this.storage,this);this.log(this+" (init'd) storage:",this.storage.get());this.listenTo(this.storage,{"change:show_deleted":function(s,t){this.showDeleted=t},"change:show_hidden":function(s,t){this.showHidden=t}},this);this.showDeleted=(p!==undefined)?p:this.storage.get("show_deleted");this.showHidden=(r!==undefined)?r:this.storage.get("show_hidden");return this},_buildNewRender:function(){var p=n.prototype._buildNewRender.call(this);if(this.multiselectActions.length){p.find(".controls .actions").prepend(this._renderSelectButton())}return p},_renderSelectButton:function(p){return faIconButton({title:e("Operations on multiple datasets"),classes:"show-selectors-btn",faIcon:"fa-check-square-o"})},_getItemViewClass:function(p){var q=p.get("history_content_type");switch(q){case"dataset":return this.HDAViewClass;case"dataset_collection":return this.HDCAViewClass}throw new TypeError("Unknown history_content_type: "+q)},_filterItem:function(q){var p=this;return(n.prototype._filterItem.call(p,q)&&(!q.hidden()||p.showHidden)&&(!q.isDeletedOrPurged()||p.showDeleted))},_getItemViewOptions:function(q){var p=n.prototype._getItemViewOptions.call(this,q);return _.extend(p,{linkTarget:this.linkTarget,expanded:!!this.storage.get("expandedIds")[q.id],hasUser:this.model.ownedByCurrUser()})},_setUpItemViewListeners:function(q){var p=this;n.prototype._setUpItemViewListeners.call(p,q);q.on("expanded",function(r){p.storage.addExpanded(r.model)});q.on("collapsed",function(r){p.storage.removeExpanded(r.model)});return this},getSelectedModels:function(){var p=n.prototype.getSelectedModels.call(this);p.historyId=this.collection.historyId;return p},events:_.extend(_.clone(n.prototype.events),{"click .show-selectors-btn":"toggleSelectors"}),toggleShowDeleted:function(p,q){p=(p!==undefined)?(p):(!this.showDeleted);q=(q!==undefined)?(q):(true);this.showDeleted=p;if(q){this.storage.set("show_deleted",p)}this.trigger("show-hidden",p);this.renderItems();return this.showDeleted},toggleShowHidden:function(p,q){p=(p!==undefined)?(p):(!this.showHidden);q=(q!==undefined)?(q):(true);this.showHidden=p;if(q){this.storage.set("show_hidden",p)}this.trigger("show-hidden",p);this.renderItems();return this.showHidden},_firstSearch:function(p){var q=this,r=".history-search-input";this.log("onFirstSearch",p);if(q.model.contents.haveDetails()){q.searchItems(p);return}q.$el.find(r).searchInput("toggle-loading");q.model.contents.fetchAllDetails({silent:true}).always(function(){q.$el.find(r).searchInput("toggle-loading")}).done(function(){q.searchItems(p)})},errorHandler:function(r,u,q,t,s){this.error(r,u,q,t,s);if(u&&u.status===0&&u.readyState===0){}else{if(u&&u.status===502){}else{var p=this._parseErrorMessage(r,u,q,t,s);if(!this.$messages().is(":visible")){this.once("rendered",function(){this.displayMessage("error",p.message,p.details)})}else{this.displayMessage("error",p.message,p.details)}}}},_parseErrorMessage:function(t,w,x,r,p,u){var s=Galaxy.currUser,v={message:this._bePolite(r),details:{message:r,raven:(window.Raven&&_.isFunction(Raven.lastEventId))?(Raven.lastEventId()):(undefined),agent:navigator.userAgent,url:(window.Galaxy)?(Galaxy.lastAjax.url):(undefined),data:(window.Galaxy)?(Galaxy.lastAjax.data):(undefined),options:(w)?(_.omit(x,"xhr")):(x),xhr:w,source:(_.isFunction(t.toJSON))?(t.toJSON()):(t+""),user:(s instanceof g.User)?(s.toJSON()):(s+"")}};_.extend(v.details,p||{});if(w&&_.isFunction(w.getAllResponseHeaders)){var q=w.getAllResponseHeaders();q=_.compact(q.split("\n"));q=_.map(q,function(y){return y.split(": ")});v.details.xhr.responseHeaders=_.object(q)}return v},_bePolite:function(p){p=p||e("An error occurred while getting updates from the server");return p+". "+e("Please contact a Galaxy administrator if the problem persists")+"."},displayMessage:function(u,v,t){var r=this;this.scrollToTop();var s=this.$messages(),p=$("<div/>").addClass(u+"message").html(v);if(!_.isEmpty(t)){var q=$('<a href="javascript:void(0)">Details</a>').click(function(){Galaxy.modal.show(r._messageToModalOptions(u,v,t));return false});p.append(" ",q)}return s.html(p)},_messageToModalOptions:function(s,v,r){var p=this,q={title:"Details"};if(_.isObject(r)){r=_.omit(r,_.functions(r));var u=JSON.stringify(r,null," "),t=$("<pre/>").text(u);q.body=$("<div/>").append(t)}else{q.body=$("<div/>").html(r)}q.buttons={Ok:function(){Galaxy.modal.hide();p.clearMessages()}};return q},clearMessages:function(p){$(p.currentTarget).fadeOut(this.fxSpeed,function(){$(this).remove()});return this},scrollToHid:function(p){return this.scrollToItem(_.first(this.viewsWhereModel({hid:p})))},toString:function(){return"HistoryPanel("+((this.model)?(this.model.get("name")):(""))+")"}});k.prototype.templates=(function(){var p=o.wrapTemplate(['<div class="controls">','<div class="title">','<div class="name"><%= history.name %></div>',"</div>",'<div class="subtitle"></div>','<div class="history-size"><%= history.nice_size %></div>','<div class="actions"></div>','<div class="messages">',"<% if( history.deleted ){ %>",'<div class="deleted-msg warningmessagesmall">',e("This history has been deleted"),"</div>","<% } %>","<% if( history.message ){ %>",'<div class="<%= history.message.level || "info" %>messagesmall">',"<%= history.message.text %>","</div>","<% } %>","</div>",'<div class="tags-display"></div>','<div class="annotation-display"></div>','<div class="search">','<div class="search-input"></div>',"</div>",'<div class="list-actions">','<div class="btn-group">','<button class="select-all btn btn-default"','data-mode="select">',e("All"),"</button>",'<button class="deselect-all btn btn-default"','data-mode="select">',e("None"),"</button>","</div>",'<button class="list-action-popup-btn btn btn-default">',e("For all selected"),"...</button>","</div>","</div>"],"history");return _.extend(_.clone(n.prototype.templates),{controls:p})}());return{HistoryPanel:k}});
\ No newline at end of file
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 static/scripts/packed/mvc/history/multi-panel.js
--- a/static/scripts/packed/mvc/history/multi-panel.js
+++ b/static/scripts/packed/mvc/history/multi-panel.js
@@ -1,1 +1,1 @@
-define(["mvc/history/history-model","mvc/history/history-panel-edit","mvc/base-mvc","utils/ajax-queue","jq-plugins/ui/mode-button"],function(d,l,z,a){window.HISTORY_MODEL=d;function g(H,E){E=E||{};if(!(Galaxy&&Galaxy.modal)){return H.copy()}var F=H.get("name"),C="Copy of '"+F+"'";function D(J){if(!J){if(!Galaxy.modal.$("#invalid-title").size()){var I=$("<p/>").attr("id","invalid-title").css({color:"red","margin-top":"8px"}).addClass("bg-danger").text(_l("Please enter a valid history title"));Galaxy.modal.$(".modal-body").append(I)}return false}return J}function G(I){var J=$('<p><span class="fa fa-spinner fa-spin"></span> Copying history...</p>').css("margin-top","8px");Galaxy.modal.$(".modal-body").append(J);H.copy(true,I).fail(function(){alert(_l("History could not be copied. Please contact a Galaxy administrator"))}).always(function(){Galaxy.modal.hide()})}Galaxy.modal.show(_.extend({title:_l("Copying history")+' "'+F+'"',body:$(['<label for="copy-modal-title">',_l("Enter a title for the copied history"),":","</label><br />",'<input id="copy-modal-title" class="form-control" style="width: 100%" value="',C,'" />'].join("")),buttons:{Cancel:function(){Galaxy.modal.hide()},Copy:function(){var I=Galaxy.modal.$("#copy-modal-title").val();if(!D(I)){return}G(I)}}},E));$("#copy-modal-title").focus().select()}var B=Backbone.View.extend(z.LoggableMixin).extend({tagName:"div",className:"history-column flex-column flex-row-container",id:function q(){if(!this.model){return""}return"history-column-"+this.model.get("id")},initialize:function c(C){C=C||{};this.panel=C.panel||this.createPanel(C);this.setUpListeners()},createPanel:function u(D){D=_.extend({model:this.model,dragItems:true},D);var C=new l.HistoryPanelEdit(D);C._renderEmptyMessage=this.__patch_renderEmptyMessage;return C},__patch_renderEmptyMessage:function(E){var D=this,F=_.chain(this.model.get("state_ids")).values().flatten().value().length,C=D.$emptyMessage(E);if(!_.isEmpty(D.hdaViews)){C.hide()}else{if(F&&!this.model.contents.length){C.empty().append($('<span class="fa fa-spinner fa-spin"></span><i>loading datasets...</i>')).show()}else{if(D.searchFor){C.text(D.noneFoundMsg).show()}else{C.text(D.emptyMsg).show()}}}return C},setUpListeners:function f(){var C=this;this.once("rendered",function(){C.trigger("rendered:initial",C)});this.setUpPanelListeners()},setUpPanelListeners:function k(){var C=this;this.listenTo(this.panel,{rendered:function(){C.trigger("rendered",C)}},this)},inView:function(C,D){var F=this.$el.offset().left,E=F+this.$el.width();if(E<C){return false}if(F>D){return false}return true},$panel:function e(){return this.$(".history-panel")},render:function A(D){D=(D!==undefined)?(D):("fast");var C=this.model?this.model.toJSON():{};this.$el.html(this.template(C));this.renderPanel(D);this.setUpBehaviors();return this},setUpBehaviors:function v(){},template:function w(D){D=D||{};var C=['<div class="panel-controls clear flex-row">',this.controlsLeftTemplate(),'<div class="pull-right">','<button class="delete-history btn btn-default">',D.deleted?_l("Undelete"):_l("Delete"),"</button>",'<button class="copy-history btn btn-default">',_l("Copy"),"</button>","</div>","</div>",'<div class="inner flex-row flex-column-container">','<div id="history-',D.id,'" class="history-column history-panel flex-column"></div>',"</div>"].join("");return $(C)},controlsLeftTemplate:function(){return(this.currentHistory)?['<div class="pull-left">','<button class="create-new btn btn-default">',_l("Create new"),"</button> ","</div>"].join(""):['<div class="pull-left">','<button class="switch-to btn btn-default">',_l("Switch to"),"</button>","</div>"].join("")},renderPanel:function h(C){C=(C!==undefined)?(C):("fast");this.panel.setElement(this.$panel()).render(C);return this},events:{"click .switch-to.btn":function(){this.model.setAsCurrent()},"click .delete-history.btn":function(){var C=this,D;if(this.model.get("deleted")){D=this.model.undelete()}else{D=this.model._delete()}D.fail(function(G,E,F){alert(_l("Could not delete the history")+":\n"+F)}).done(function(E){C.render()})},"click .copy-history.btn":"copy"},copy:function s(){g(this.model)},toString:function(){return"HistoryPanelColumn("+(this.panel?this.panel:"")+")"}});var m=Backbone.View.extend(z.LoggableMixin).extend({initialize:function c(C){C=C||{};this.log(this+".init",C);if(!C.currentHistoryId){throw new Error(this+" requires a currentHistoryId in the options")}this.currentHistoryId=C.currentHistoryId;this.options={columnWidth:312,borderWidth:1,columnGap:8,headerHeight:29,footerHeight:0,controlsHeight:20};this.order=C.order||"update";this.hdaQueue=new a.NamedAjaxQueue([],false);this.collection=null;this.setCollection(C.histories||[]);this.columnMap={};this.createColumns(C.columnOptions);this.setUpListeners()},setUpListeners:function f(){},setCollection:function y(D){var C=this;C.stopListening(C.collection);C.collection=D;C.sortCollection(C.order,{silent:true});C.setUpCollectionListeners();C.trigger("new-collection",C);return C},setUpCollectionListeners:function(){var C=this,D=C.collection;C.listenTo(D,{add:C.addAsCurrentColumn,"set-as-current":C.setCurrentHistory,"change:deleted":C.handleDeletedHistory,sort:function(){C.renderColumns(0)}})},setCurrentHistory:function p(D){var C=this.columnMap[this.currentHistoryId];if(C){C.currentHistory=false;C.$el.height("")}this.currentHistoryId=D.id;var E=this.columnMap[this.currentHistoryId];E.currentHistory=true;this.sortCollection();multipanel._recalcFirstColumnHeight();return E},handleDeletedHistory:function b(D){if(D.get("deleted")){this.log("handleDeletedHistory",this.collection.includeDeleted,D);var C=this;column=C.columnMap[D.id];if(!column){return}if(column.model.id===this.currentHistoryId){}else{if(!C.collection.includeDeleted){C.removeColumn(column)}}}},sortCollection:function(C,D){C=C||this.order;var E=this.currentHistoryId;switch(C){case"name":this.collection.comparator=function(F){return[F.id!==E,F.get("name").toLowerCase()]};break;case"size":this.collection.comparator=function(F){return[F.id!==E,F.get("size")]};break;default:this.collection.comparator=function(F){return[F.id!==E,Date(F.get("update_time"))]}}this.collection.sort(D);return this.collection},setOrder:function(C){if(["update","name","size"].indexOf(C)===-1){C="update"}this.order=C;this.sortCollection();return this},create:function(C){return this.collection.create({current:true})},createColumns:function r(D){D=D||{};var C=this;this.columnMap={};C.collection.each(function(E,F){var G=C.createColumn(E,D);C.columnMap[E.id]=G})},createColumn:function t(E,C){C=_.extend({},C,{model:E});var D=new B(C);if(E.id===this.currentHistoryId){D.currentHistory=true}this.setUpColumnListeners(D);return D},sortedFilteredColumns:function(C){C=C||this.filters;if(!C||!C.length){return this.sortedColumns()}var D=this;return D.sortedColumns().filter(function(G,F){var E=G.currentHistory||_.every(C.map(function(H){return H.call(G)}));return E})},sortedColumns:function(){var D=this;var C=this.collection.map(function(F,E){return D.columnMap[F.id]});return C},addColumn:function o(E,C){C=C!==undefined?C:true;var D=this.createColumn(E);this.columnMap[E.id]=D;if(C){this.renderColumns()}return D},addAsCurrentColumn:function o(E){var D=this,C=this.addColumn(E,false);this.setCurrentHistory(E);C.once("rendered",function(){D.queueHdaFetch(C)});return C},removeColumn:function x(E,D){D=D!==undefined?D:true;this.log("removeColumn",E);if(!E){return}var F=this,C=this.options.columnWidth+this.options.columnGap;E.$el.fadeOut("fast",function(){if(D){$(this).remove();F.$(".middle").width(F.$(".middle").width()-C);F.checkColumnsInView();F._recalcFirstColumnHeight()}F.stopListening(E.panel);F.stopListening(E);delete F.columnMap[E.model.id];E.remove()})},setUpColumnListeners:function n(C){var D=this;D.listenTo(C,{"in-view":D.queueHdaFetch});D.listenTo(C.panel,{"view:draggable:dragstart":function(H,F,E,G){D._dropData=JSON.parse(H.dataTransfer.getData("text"));D.currentColumnDropTargetOn()},"view:draggable:dragend":function(H,F,E,G){D._dropData=null;D.currentColumnDropTargetOff()},"droptarget:drop":function(G,H,F){var I=D._dropData.filter(function(J){return(_.isObject(J)&&J.id&&J.model_class==="HistoryDatasetAssociation")});D._dropData=null;var E=new a.NamedAjaxQueue();I.forEach(function(J){E.add({name:"copy-"+J.id,fn:function(){return F.model.contents.copy(J.id)}})});E.start();E.done(function(J){F.model.fetch()})}})},columnMapLength:function(){return Object.keys(this.columnMap).length},render:function A(D){D=D!==undefined?D:this.fxSpeed;var C=this;C.log(C+".render");C.$el.html(C.template(C.options));C.renderColumns(D);C.setUpBehaviors();C.trigger("rendered",C);return C},template:function w(C){C=C||{};var D=[];if(this.options.headerHeight){D=D.concat(['<div class="loading-overlay flex-row"><div class="loading-overlay-message">loading...</div></div>','<div class="header flex-column-container">','<div class="header-control header-control-left flex-column">','<button class="done btn btn-default">',_l("Done"),"</button>",'<button class="include-deleted btn btn-default"></button>','<div class="order btn-group">','<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">',_l("Order histories by")+'... <span class="caret"></span>',"</button>",'<ul class="dropdown-menu" role="menu">','<li><a href="javascript:void(0);" class="order-update">',_l("Time of last update"),"</a></li>",'<li><a href="javascript:void(0);" class="order-name">',_l("Name"),"</a></li>",'<li><a href="javascript:void(0);" class="order-size">',_l("Size"),"</a></li>","</ul>","</div>",'<div id="search-histories" class="header-search"></div>',"</div>",'<div class="header-control header-control-center flex-column">','<div class="header-info">',"</div>","</div>",'<div class="header-control header-control-right flex-column">','<div id="search-datasets" class="header-search"></div>','<button id="toggle-deleted" class="btn btn-default">',_l("Include deleted datasets"),"</button>",'<button id="toggle-hidden" class="btn btn-default">',_l("Include hidden datasets"),"</button>","</div>","</div>"])}D=D.concat(['<div class="outer-middle flex-row flex-row-container">','<div class="middle flex-column-container flex-row"></div>',"</div>",'<div class="footer flex-column-container">',"</div>"]);return $(D.join(""))},renderColumns:function j(F){F=F!==undefined?F:this.fxSpeed;var E=this,C=E.sortedFilteredColumns();E.$(".middle").width(C.length*(this.options.columnWidth+this.options.columnGap)+this.options.columnGap+16);var D=E.$(".middle");D.empty();C.forEach(function(H,G){H.$el.appendTo(D);H.delegateEvents();E.renderColumn(H,F)});if(this.searchFor&&C.length<=1){}else{E.checkColumnsInView();this._recalcFirstColumnHeight()}return C},renderColumn:function(C,D){D=D!==undefined?D:this.fxSpeed;return C.render(D)},queueHdaFetch:function i(E){if(E.model.contents.length===0&&!E.model.get("empty")){var C={},D=_.values(E.panel.storage.get("expandedIds")).join();if(D){C.dataset_details=D}this.hdaQueue.add({name:E.model.id,fn:function(){var F=E.model.contents.fetch({data:C,silent:true});return F.done(function(G){E.panel.renderItems()})}});if(!this.hdaQueue.running){this.hdaQueue.start()}}},queueHdaFetchDetails:function(C){if((C.model.contents.length===0&&!C.model.get("empty"))||(!C.model.contents.haveDetails())){this.hdaQueue.add({name:C.model.id,fn:function(){var D=C.model.contents.fetch({data:{details:"all"},silent:true});return D.done(function(E){C.panel.renderItems()})}});if(!this.hdaQueue.running){this.hdaQueue.start()}}},renderInfo:function(C){this.$(".header .header-info").text(C)},events:{"click .done.btn":function(){window.location="/"},"click .create-new.btn":"create","click .order .order-update":function(C){this.setOrder("update")},"click .order .order-name":function(C){this.setOrder("name")},"click .order .order-size":function(C){this.setOrder("size")}},includeDeletedHistories:function(){window.location+=(/\?/.test(window.location.toString()))?("&"):("?")+"include_deleted_histories=True"},excludeDeletedHistories:function(){window.location=window.location.toString().replace(/[&\?]include_deleted_histories=True/g,"")},setUpBehaviors:function(){var D=this;D.$(".include-deleted").modeButton({initialMode:this.collection.includeDeleted?"exclude":"include",switchModesOnClick:false,modes:[{mode:"include",html:_l("Include deleted histories"),onclick:_.bind(D.includeDeletedHistories,D)},{mode:"exclude",html:_l("Exclude deleted histories"),onclick:_.bind(D.excludeDeletedHistories,D)}]});D.$("#search-histories").searchInput({name:"search-histories",placeholder:_l("search histories"),onsearch:function(E){D.searchFor=E;D.filters=[function(){return this.model.matchesAll(D.searchFor)}];D.renderColumns(0)},onclear:function(E){D.searchFor=null;D.filters=[];D.renderColumns(0)}});D.$("#search-datasets").searchInput({name:"search-datasets",placeholder:_l("search all datasets"),onfirstsearch:function(E){D.hdaQueue.clear();D.$("#search-datasets").searchInput("toggle-loading");D.searchFor=E;D.sortedFilteredColumns().forEach(function(F){F.panel.searchItems(E);D.queueHdaFetchDetails(F)});D.hdaQueue.progress(function(F){D.renderInfo([_l("searching"),(F.curr+1),_l("of"),F.total].join(" "))});D.hdaQueue.deferred.done(function(){D.renderInfo("");D.$("#search-datasets").searchInput("toggle-loading")})},onsearch:function(E){D.searchFor=E;D.sortedFilteredColumns().forEach(function(F){F.panel.searchItems(E)})},onclear:function(E){D.searchFor=null;D.sortedFilteredColumns().forEach(function(F){F.panel.clearSearch()})}});D.$("#toggle-deleted").modeButton({initialMode:"include",modes:[{mode:"exclude",html:_l("Exclude deleted datasets")},{mode:"include",html:_l("Include deleted datasets")}]}).click(function(){var E=$(this).modeButton("getMode").mode==="exclude";D.sortedFilteredColumns().forEach(function(G,F){_.delay(function(){G.panel.toggleShowDeleted(E,false)},F*200)})});D.$("#toggle-hidden").modeButton({initialMode:"include",modes:[{mode:"exclude",html:_l("Exclude hidden datasets")},{mode:"include",html:_l("Include hidden datasets")}]}).click(function(){var E=$(this).modeButton("getMode").mode==="exclude";D.sortedFilteredColumns().forEach(function(G,F){_.delay(function(){G.panel.toggleShowHidden(E,false)},F*200)})});$(window).resize(function(){D._recalcFirstColumnHeight()});var C=_.debounce(_.bind(this.checkColumnsInView,this),100);this.$(".middle").parent().scroll(C)},_recalcFirstColumnHeight:function(){var C=this.$(".history-column").first(),E=this.$(".middle").height(),D=C.find(".panel-controls").height();C.height(E).find(".inner").height(E-D)},_viewport:function(){var C=this.$(".middle").parent().offset().left;return{left:C,right:C+this.$(".middle").parent().width()}},columnsInView:function(){var C=this._viewport();return this.sortedFilteredColumns().filter(function(D){return D.currentHistory||D.inView(C.left,C.right)})},checkColumnsInView:function(){this.columnsInView().forEach(function(C){C.trigger("in-view",C)})},currentColumnDropTargetOn:function(){var C=this.columnMap[this.currentHistoryId];if(!C){return}C.panel.dataDropped=function(D){};C.panel.dropTargetOn()},currentColumnDropTargetOff:function(){var C=this.columnMap[this.currentHistoryId];if(!C){return}C.panel.dataDropped=l.HistoryPanelEdit.prototype.dataDrop;C.panel.dropTargetOff()},toString:function(){return"MultiPanelColumns("+(this.columns?this.columns.length:0)+")"}});return{MultiPanelColumns:m}});
\ No newline at end of file
+define(["mvc/history/history-model","mvc/history/history-panel-edit","mvc/base-mvc","utils/ajax-queue","jq-plugins/ui/mode-button","jq-plugins/ui/search-input"],function(d,l,z,a){window.HISTORY_MODEL=d;function g(H,E){E=E||{};if(!(Galaxy&&Galaxy.modal)){return H.copy()}var F=H.get("name"),C="Copy of '"+F+"'";function D(J){if(!J){if(!Galaxy.modal.$("#invalid-title").size()){var I=$("<p/>").attr("id","invalid-title").css({color:"red","margin-top":"8px"}).addClass("bg-danger").text(_l("Please enter a valid history title"));Galaxy.modal.$(".modal-body").append(I)}return false}return J}function G(I){var J=$('<p><span class="fa fa-spinner fa-spin"></span> Copying history...</p>').css("margin-top","8px");Galaxy.modal.$(".modal-body").append(J);H.copy(true,I).fail(function(){alert(_l("History could not be copied. Please contact a Galaxy administrator"))}).always(function(){Galaxy.modal.hide()})}Galaxy.modal.show(_.extend({title:_l("Copying history")+' "'+F+'"',body:$(['<label for="copy-modal-title">',_l("Enter a title for the copied history"),":","</label><br />",'<input id="copy-modal-title" class="form-control" style="width: 100%" value="',C,'" />'].join("")),buttons:{Cancel:function(){Galaxy.modal.hide()},Copy:function(){var I=Galaxy.modal.$("#copy-modal-title").val();if(!D(I)){return}G(I)}}},E));$("#copy-modal-title").focus().select()}var B=Backbone.View.extend(z.LoggableMixin).extend({tagName:"div",className:"history-column flex-column flex-row-container",id:function q(){if(!this.model){return""}return"history-column-"+this.model.get("id")},initialize:function c(C){C=C||{};this.panel=C.panel||this.createPanel(C);this.setUpListeners()},createPanel:function u(D){D=_.extend({model:this.model,dragItems:true},D);var C=new l.HistoryPanelEdit(D);C._renderEmptyMessage=this.__patch_renderEmptyMessage;return C},__patch_renderEmptyMessage:function(E){var D=this,F=_.chain(this.model.get("state_ids")).values().flatten().value().length,C=D.$emptyMessage(E);if(!_.isEmpty(D.hdaViews)){C.hide()}else{if(F&&!this.model.contents.length){C.empty().append($('<span class="fa fa-spinner fa-spin"></span><i>loading datasets...</i>')).show()}else{if(D.searchFor){C.text(D.noneFoundMsg).show()}else{C.text(D.emptyMsg).show()}}}return C},setUpListeners:function f(){var C=this;this.once("rendered",function(){C.trigger("rendered:initial",C)});this.setUpPanelListeners()},setUpPanelListeners:function k(){var C=this;this.listenTo(this.panel,{rendered:function(){C.trigger("rendered",C)}},this)},inView:function(C,D){var F=this.$el.offset().left,E=F+this.$el.width();if(E<C){return false}if(F>D){return false}return true},$panel:function e(){return this.$(".history-panel")},render:function A(D){D=(D!==undefined)?(D):("fast");var C=this.model?this.model.toJSON():{};this.$el.html(this.template(C));this.renderPanel(D);this.setUpBehaviors();return this},setUpBehaviors:function v(){},template:function w(D){D=D||{};var C=['<div class="panel-controls clear flex-row">',this.controlsLeftTemplate(),'<div class="pull-right">','<button class="delete-history btn btn-default">',D.deleted?_l("Undelete"):_l("Delete"),"</button>",'<button class="copy-history btn btn-default">',_l("Copy"),"</button>","</div>","</div>",'<div class="inner flex-row flex-column-container">','<div id="history-',D.id,'" class="history-column history-panel flex-column"></div>',"</div>"].join("");return $(C)},controlsLeftTemplate:function(){return(this.currentHistory)?['<div class="pull-left">','<button class="create-new btn btn-default">',_l("Create new"),"</button> ","</div>"].join(""):['<div class="pull-left">','<button class="switch-to btn btn-default">',_l("Switch to"),"</button>","</div>"].join("")},renderPanel:function h(C){C=(C!==undefined)?(C):("fast");this.panel.setElement(this.$panel()).render(C);return this},events:{"click .switch-to.btn":function(){this.model.setAsCurrent()},"click .delete-history.btn":function(){var C=this,D;if(this.model.get("deleted")){D=this.model.undelete()}else{D=this.model._delete()}D.fail(function(G,E,F){alert(_l("Could not delete the history")+":\n"+F)}).done(function(E){C.render()})},"click .copy-history.btn":"copy"},copy:function s(){g(this.model)},toString:function(){return"HistoryPanelColumn("+(this.panel?this.panel:"")+")"}});var m=Backbone.View.extend(z.LoggableMixin).extend({initialize:function c(C){C=C||{};this.log(this+".init",C);if(!C.currentHistoryId){throw new Error(this+" requires a currentHistoryId in the options")}this.currentHistoryId=C.currentHistoryId;this.options={columnWidth:312,borderWidth:1,columnGap:8,headerHeight:29,footerHeight:0,controlsHeight:20};this.order=C.order||"update";this.hdaQueue=new a.NamedAjaxQueue([],false);this.collection=null;this.setCollection(C.histories||[]);this.columnMap={};this.createColumns(C.columnOptions);this.setUpListeners()},setUpListeners:function f(){},setCollection:function y(D){var C=this;C.stopListening(C.collection);C.collection=D;C.sortCollection(C.order,{silent:true});C.setUpCollectionListeners();C.trigger("new-collection",C);return C},setUpCollectionListeners:function(){var C=this,D=C.collection;C.listenTo(D,{add:C.addAsCurrentColumn,"set-as-current":C.setCurrentHistory,"change:deleted":C.handleDeletedHistory,sort:function(){C.renderColumns(0)}})},setCurrentHistory:function p(D){var C=this.columnMap[this.currentHistoryId];if(C){C.currentHistory=false;C.$el.height("")}this.currentHistoryId=D.id;var E=this.columnMap[this.currentHistoryId];E.currentHistory=true;this.sortCollection();multipanel._recalcFirstColumnHeight();return E},handleDeletedHistory:function b(D){if(D.get("deleted")){this.log("handleDeletedHistory",this.collection.includeDeleted,D);var C=this;column=C.columnMap[D.id];if(!column){return}if(column.model.id===this.currentHistoryId){}else{if(!C.collection.includeDeleted){C.removeColumn(column)}}}},sortCollection:function(C,D){C=C||this.order;var E=this.currentHistoryId;switch(C){case"name":this.collection.comparator=function(F){return[F.id!==E,F.get("name").toLowerCase()]};break;case"size":this.collection.comparator=function(F){return[F.id!==E,F.get("size")]};break;default:this.collection.comparator=function(F){return[F.id!==E,Date(F.get("update_time"))]}}this.collection.sort(D);return this.collection},setOrder:function(C){if(["update","name","size"].indexOf(C)===-1){C="update"}this.order=C;this.sortCollection();return this},create:function(C){return this.collection.create({current:true})},createColumns:function r(D){D=D||{};var C=this;this.columnMap={};C.collection.each(function(E,F){var G=C.createColumn(E,D);C.columnMap[E.id]=G})},createColumn:function t(E,C){C=_.extend({},C,{model:E});var D=new B(C);if(E.id===this.currentHistoryId){D.currentHistory=true}this.setUpColumnListeners(D);return D},sortedFilteredColumns:function(C){C=C||this.filters;if(!C||!C.length){return this.sortedColumns()}var D=this;return D.sortedColumns().filter(function(G,F){var E=G.currentHistory||_.every(C.map(function(H){return H.call(G)}));return E})},sortedColumns:function(){var D=this;var C=this.collection.map(function(F,E){return D.columnMap[F.id]});return C},addColumn:function o(E,C){C=C!==undefined?C:true;var D=this.createColumn(E);this.columnMap[E.id]=D;if(C){this.renderColumns()}return D},addAsCurrentColumn:function o(E){var D=this,C=this.addColumn(E,false);this.setCurrentHistory(E);C.once("rendered",function(){D.queueHdaFetch(C)});return C},removeColumn:function x(E,D){D=D!==undefined?D:true;this.log("removeColumn",E);if(!E){return}var F=this,C=this.options.columnWidth+this.options.columnGap;E.$el.fadeOut("fast",function(){if(D){$(this).remove();F.$(".middle").width(F.$(".middle").width()-C);F.checkColumnsInView();F._recalcFirstColumnHeight()}F.stopListening(E.panel);F.stopListening(E);delete F.columnMap[E.model.id];E.remove()})},setUpColumnListeners:function n(C){var D=this;D.listenTo(C,{"in-view":D.queueHdaFetch});D.listenTo(C.panel,{"view:draggable:dragstart":function(H,F,E,G){D._dropData=JSON.parse(H.dataTransfer.getData("text"));D.currentColumnDropTargetOn()},"view:draggable:dragend":function(H,F,E,G){D._dropData=null;D.currentColumnDropTargetOff()},"droptarget:drop":function(G,H,F){var I=D._dropData.filter(function(J){return(_.isObject(J)&&J.id&&J.model_class==="HistoryDatasetAssociation")});D._dropData=null;var E=new a.NamedAjaxQueue();I.forEach(function(J){E.add({name:"copy-"+J.id,fn:function(){return F.model.contents.copy(J.id)}})});E.start();E.done(function(J){F.model.fetch()})}})},columnMapLength:function(){return Object.keys(this.columnMap).length},render:function A(D){D=D!==undefined?D:this.fxSpeed;var C=this;C.log(C+".render");C.$el.html(C.template(C.options));C.renderColumns(D);C.setUpBehaviors();C.trigger("rendered",C);return C},template:function w(C){C=C||{};var D=[];if(this.options.headerHeight){D=D.concat(['<div class="loading-overlay flex-row"><div class="loading-overlay-message">loading...</div></div>','<div class="header flex-column-container">','<div class="header-control header-control-left flex-column">','<button class="done btn btn-default">',_l("Done"),"</button>",'<button class="include-deleted btn btn-default"></button>','<div class="order btn-group">','<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">',_l("Order histories by")+'... <span class="caret"></span>',"</button>",'<ul class="dropdown-menu" role="menu">','<li><a href="javascript:void(0);" class="order-update">',_l("Time of last update"),"</a></li>",'<li><a href="javascript:void(0);" class="order-name">',_l("Name"),"</a></li>",'<li><a href="javascript:void(0);" class="order-size">',_l("Size"),"</a></li>","</ul>","</div>",'<div id="search-histories" class="header-search"></div>',"</div>",'<div class="header-control header-control-center flex-column">','<div class="header-info">',"</div>","</div>",'<div class="header-control header-control-right flex-column">','<div id="search-datasets" class="header-search"></div>','<button id="toggle-deleted" class="btn btn-default">',_l("Include deleted datasets"),"</button>",'<button id="toggle-hidden" class="btn btn-default">',_l("Include hidden datasets"),"</button>","</div>","</div>"])}D=D.concat(['<div class="outer-middle flex-row flex-row-container">','<div class="middle flex-column-container flex-row"></div>',"</div>",'<div class="footer flex-column-container">',"</div>"]);return $(D.join(""))},renderColumns:function j(F){F=F!==undefined?F:this.fxSpeed;var E=this,C=E.sortedFilteredColumns();E.$(".middle").width(C.length*(this.options.columnWidth+this.options.columnGap)+this.options.columnGap+16);var D=E.$(".middle");D.empty();C.forEach(function(H,G){H.$el.appendTo(D);H.delegateEvents();E.renderColumn(H,F)});if(this.searchFor&&C.length<=1){}else{E.checkColumnsInView();this._recalcFirstColumnHeight()}return C},renderColumn:function(C,D){D=D!==undefined?D:this.fxSpeed;return C.render(D)},queueHdaFetch:function i(E){if(E.model.contents.length===0&&!E.model.get("empty")){var C={},D=_.values(E.panel.storage.get("expandedIds")).join();if(D){C.dataset_details=D}this.hdaQueue.add({name:E.model.id,fn:function(){var F=E.model.contents.fetch({data:C,silent:true});return F.done(function(G){E.panel.renderItems()})}});if(!this.hdaQueue.running){this.hdaQueue.start()}}},queueHdaFetchDetails:function(C){if((C.model.contents.length===0&&!C.model.get("empty"))||(!C.model.contents.haveDetails())){this.hdaQueue.add({name:C.model.id,fn:function(){var D=C.model.contents.fetch({data:{details:"all"},silent:true});return D.done(function(E){C.panel.renderItems()})}});if(!this.hdaQueue.running){this.hdaQueue.start()}}},renderInfo:function(C){this.$(".header .header-info").text(C)},events:{"click .done.btn":function(){window.location="/"},"click .create-new.btn":"create","click .order .order-update":function(C){this.setOrder("update")},"click .order .order-name":function(C){this.setOrder("name")},"click .order .order-size":function(C){this.setOrder("size")}},includeDeletedHistories:function(){window.location+=(/\?/.test(window.location.toString()))?("&"):("?")+"include_deleted_histories=True"},excludeDeletedHistories:function(){window.location=window.location.toString().replace(/[&\?]include_deleted_histories=True/g,"")},setUpBehaviors:function(){var D=this;D.$(".include-deleted").modeButton({initialMode:this.collection.includeDeleted?"exclude":"include",switchModesOnClick:false,modes:[{mode:"include",html:_l("Include deleted histories"),onclick:_.bind(D.includeDeletedHistories,D)},{mode:"exclude",html:_l("Exclude deleted histories"),onclick:_.bind(D.excludeDeletedHistories,D)}]});D.$("#search-histories").searchInput({name:"search-histories",placeholder:_l("search histories"),onsearch:function(E){D.searchFor=E;D.filters=[function(){return this.model.matchesAll(D.searchFor)}];D.renderColumns(0)},onclear:function(E){D.searchFor=null;D.filters=[];D.renderColumns(0)}});D.$("#search-datasets").searchInput({name:"search-datasets",placeholder:_l("search all datasets"),onfirstsearch:function(E){D.hdaQueue.clear();D.$("#search-datasets").searchInput("toggle-loading");D.searchFor=E;D.sortedFilteredColumns().forEach(function(F){F.panel.searchItems(E);D.queueHdaFetchDetails(F)});D.hdaQueue.progress(function(F){D.renderInfo([_l("searching"),(F.curr+1),_l("of"),F.total].join(" "))});D.hdaQueue.deferred.done(function(){D.renderInfo("");D.$("#search-datasets").searchInput("toggle-loading")})},onsearch:function(E){D.searchFor=E;D.sortedFilteredColumns().forEach(function(F){F.panel.searchItems(E)})},onclear:function(E){D.searchFor=null;D.sortedFilteredColumns().forEach(function(F){F.panel.clearSearch()})}});D.$("#toggle-deleted").modeButton({initialMode:"include",modes:[{mode:"exclude",html:_l("Exclude deleted datasets")},{mode:"include",html:_l("Include deleted datasets")}]}).click(function(){var E=$(this).modeButton("getMode").mode==="exclude";D.sortedFilteredColumns().forEach(function(G,F){_.delay(function(){G.panel.toggleShowDeleted(E,false)},F*200)})});D.$("#toggle-hidden").modeButton({initialMode:"include",modes:[{mode:"exclude",html:_l("Exclude hidden datasets")},{mode:"include",html:_l("Include hidden datasets")}]}).click(function(){var E=$(this).modeButton("getMode").mode==="exclude";D.sortedFilteredColumns().forEach(function(G,F){_.delay(function(){G.panel.toggleShowHidden(E,false)},F*200)})});$(window).resize(function(){D._recalcFirstColumnHeight()});var C=_.debounce(_.bind(this.checkColumnsInView,this),100);this.$(".middle").parent().scroll(C)},_recalcFirstColumnHeight:function(){var C=this.$(".history-column").first(),E=this.$(".middle").height(),D=C.find(".panel-controls").height();C.height(E).find(".inner").height(E-D)},_viewport:function(){var C=this.$(".middle").parent().offset().left;return{left:C,right:C+this.$(".middle").parent().width()}},columnsInView:function(){var C=this._viewport();return this.sortedFilteredColumns().filter(function(D){return D.currentHistory||D.inView(C.left,C.right)})},checkColumnsInView:function(){this.columnsInView().forEach(function(C){C.trigger("in-view",C)})},currentColumnDropTargetOn:function(){var C=this.columnMap[this.currentHistoryId];if(!C){return}C.panel.dataDropped=function(D){};C.panel.dropTargetOn()},currentColumnDropTargetOff:function(){var C=this.columnMap[this.currentHistoryId];if(!C){return}C.panel.dataDropped=l.HistoryPanelEdit.prototype.dataDrop;C.panel.dropTargetOff()},toString:function(){return"MultiPanelColumns("+(this.columns?this.columns.length:0)+")"}});return{MultiPanelColumns:m}});
\ No newline at end of file
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 static/scripts/packed/mvc/list/list-panel.js
--- a/static/scripts/packed/mvc/list/list-panel.js
+++ b/static/scripts/packed/mvc/list/list-panel.js
@@ -1,1 +1,1 @@
-define(["mvc/list/list-item","mvc/base-mvc","utils/localization"],function(d,b,c){var e=Backbone.View.extend(b.LoggableMixin).extend({viewClass:d.ListItemView,collectionClass:Backbone.Collection,tagName:"div",className:"list-panel",fxSpeed:"fast",emptyMsg:c("This list is empty"),noneFoundMsg:c("No matching items found"),searchPlaceholder:c("search"),multiselectActions:[],initialize:function(f,g){f=f||{};if(f.logger){this.logger=f.logger}this.log(this+".initialize:",f);this.fxSpeed=_.has(f,"fxSpeed")?(f.fxSpeed):(this.fxSpeed);this.filters=[];this.searchFor=f.searchFor||"";this.indicator=new LoadingIndicator(this.$el);this.selecting=(f.selecting!==undefined)?f.selecting:true;this.selected=f.selected||[];this.lastSelected=null;this.dragItems=f.dragItems||false;this.viewClass=f.viewClass||this.viewClass;this.views=[];this.collection=f.collection||(new this.collectionClass([]));this.filters=f.filters||[];this.$scrollContainer=f.$scrollContainer||this.$scrollContainer;this.title=f.title||"";this.subtitle=f.subtitle||"";this.multiselectActions=f.multiselectActions||this.multiselectActions;this.actionsPopup=null;this._setUpListeners()},freeViews:function(){_.each(this.views,function(f){f.off()});this.views=[];return this},_setUpListeners:function(){this.off();this.on("error",function(g,j,f,i,h){console.error(g,j,f,i,h)},this);this.on("loading",function(){this._showLoadingIndicator("loading...",40)},this);this.on("loading-done",function(){this._hideLoadingIndicator(40)},this);this.once("rendered",function(){this.trigger("rendered:initial",this)},this);if(this.logger){this.on("all",function(f){this.log(this+"",arguments)},this)}this._setUpCollectionListeners();this._setUpViewListeners();return this},_setUpCollectionListeners:function(){this.log(this+"._setUpCollectionListeners",this.collection);this.collection.off();this.collection.on("error",function(g,j,f,i,h){this.trigger("error",g,j,f,i,h)},this);this.collection.on("reset",function(){this.renderItems()},this);this.collection.on("add",this.addItemView,this);this.collection.on("remove",this.removeItemView,this);if(this.logger){this.collection.on("all",function(f){this.info(this+"(collection)",arguments)},this)}return this},_setUpViewListeners:function(){this.log(this+"._setUpViewListeners");this.on("view:selected",function(f,g){if(g&&g.shiftKey&&this.lastSelected){var h=this.viewFromModelId(this.lastSelected);if(h){this.selectRange(f,h)}}else{if(g&&g.altKey&&!this.selecting){this.showSelectors()}}this.selected.push(f.model.id);this.lastSelected=f.model.id},this);this.on("view:de-selected",function(f,g){this.selected=_.without(this.selected,f.model.id)},this)},render:function(g){this.log(this+".render",g);var f=this._buildNewRender();this._setUpBehaviors(f);this._queueNewRender(f,g);return this},_buildNewRender:function(){this.debug(this+"(ListPanel)._buildNewRender");var f=$(this.templates.el({},this));this._renderControls(f);this._renderTitle(f);this._renderSubtitle(f);this._renderSearch(f);this.renderItems(f);return f},_renderControls:function(g){this.debug(this+"(ListPanel)._renderControls");var f=$(this.templates.controls({},this));g.find(".controls").replaceWith(f);return f},_renderTitle:function(f){},_renderSubtitle:function(f){},_queueNewRender:function(g,h){h=(h===undefined)?(this.fxSpeed):(h);var f=this;f.log("_queueNewRender:",g,h);$(f).queue("fx",[function(i){this.$el.fadeOut(h,i)},function(i){f._swapNewRender(g);i()},function(i){this.$el.fadeIn(h,i)},function(i){f.trigger("rendered",f);i()}])},_swapNewRender:function(f){this.$el.empty().attr("class",this.className).append(f.children());if(this.selecting){this.showSelectors(0)}return this},_setUpBehaviors:function(f){f=f||this.$el;f.find(".controls [title]").tooltip({placement:"bottom"});return this},$scrollContainer:function(){return this.$el.parent().parent()},$list:function(f){return(f||this.$el).find("> .list-items")},$messages:function(f){return(f||this.$el).find("> .controls .messages")},$emptyMessage:function(f){return(f||this.$el).find("> .empty-message")},renderItems:function(h){h=h||this.$el;var f=this;f.log(this+".renderItems",h);var g=f.$list(h);f.views=f._filterCollection().map(function(i){return f._createItemView(i).render(0)});g.empty();if(f.views.length){f._attachItems(h);f.$emptyMessage(h).hide()}else{f._renderEmptyMessage(h).show()}return f.views},_filterCollection:function(){var f=this;return f.collection.filter(_.bind(f._filterItem,f))},_filterItem:function(g){var f=this;return(_.every(f.filters.map(function(h){return h.call(g)})))&&(!f.searchFor||g.matchesAll(f.searchFor))},_createItemView:function(h){var i=this._getItemViewClass(h),g=_.extend(this._getItemViewOptions(h),{model:h}),f=new i(g);this._setUpItemViewListeners(f);return f},_getItemViewClass:function(f){return this.viewClass},_getItemViewOptions:function(f){return{fxSpeed:this.fxSpeed,expanded:false,selectable:this.selecting,selected:_.contains(this.selected,f.id),draggable:this.dragItems}},_setUpItemViewListeners:function(g){var f=this;g.on("all",function(){var h=Array.prototype.slice.call(arguments,0);h[0]="view:"+h[0];f.trigger.apply(f,h)});g.on("draggable:dragstart",function(k,h){var i={},j=this.getSelectedModels();if(j.length){i=j.toJSON()}else{i=[h.model.toJSON()]}k.dataTransfer.setData("text",JSON.stringify(i))},this);return f},_attachItems:function(f){this.$list(f).append(this.views.map(function(g){return g.$el}));return this},_renderEmptyMessage:function(f){this.debug("_renderEmptyMessage",f,this.searchFor);var g=this.searchFor?this.noneFoundMsg:this.emptyMsg;return this.$emptyMessage(f).text(g)},expandAll:function(){_.each(this.views,function(f){f.expand()})},collapseAll:function(){_.each(this.views,function(f){f.collapse()})},addItemView:function(i,j,h){this.log(this+".addItemView:",i);var g=this;if(!g._filterItem(i)){return undefined}var f=g._createItemView(i);$(f).queue("fx",[function(k){g.$emptyMessage().fadeOut(g.fxSpeed,k)},function(k){g._attachView(f);k()}]);return f},_attachView:function(g){var f=this;f.views.push(g);f.$list().append(g.render(0).$el.hide());g.$el.slideDown(f.fxSpeed)},removeItemView:function(i,j,h){this.log(this+".removeItemView:",i);var g=this,f=g.viewFromModel(i);if(!f){return undefined}g.views=_.without(g.views,f);$({}).queue("fx",[function(k){f.$el.fadeOut(g.fxSpeed,k)},function(k){f.remove();if(!g.views.length){g._renderEmptyMessage().fadeIn(g.fxSpeed,k)}else{k()}}]);return f},viewFromModelId:function(g){for(var f=0;f<this.views.length;f++){if(this.views[f].model.id===g){return this.views[f]}}return undefined},viewFromModel:function(f){if(!f){return undefined}return this.viewFromModelId(f.id)},viewsWhereModel:function(f){return this.views.filter(function(g){var i=g.model.toJSON();for(var h in f){if(f.hasOwnProperty(h)){if(i[h]!==g.model.get(h)){return false}}}return true})},viewRange:function(i,h){if(i===h){return(i)?([i]):([])}var g=this.views.indexOf(i),f=this.views.indexOf(h);if(g===-1||f===-1){if(g===f){return[]}return(g===-1)?([h]):([i])}return(g<f)?this.views.slice(g,f+1):this.views.slice(f,g+1)},_renderSearch:function(f){f.find(".controls .search-input").searchInput({placeholder:this.searchPlaceholder,initialVal:this.searchFor,onfirstsearch:_.bind(this._firstSearch,this),onsearch:_.bind(this.searchItems,this),onclear:_.bind(this.clearSearch,this)});return f},_firstSearch:function(f){this.log("onFirstSearch",f);return this.searchItems(f)},searchItems:function(f){this.searchFor=f;this.trigger("search:searching",f,this);this.renderItems();this.$("> .controls .search-query").val(f);return this},clearSearch:function(f){this.searchFor="";this.trigger("search:clear",this);this.renderItems();this.$("> .controls .search-query").val("");return this},showSelectors:function(f){f=(f!==undefined)?(f):(this.fxSpeed);this.selecting=true;this.$(".list-actions").slideDown(f);_.each(this.views,function(g){g.showSelector(f)})},hideSelectors:function(f){f=(f!==undefined)?(f):(this.fxSpeed);this.selecting=false;this.$(".list-actions").slideUp(f);_.each(this.views,function(g){g.hideSelector(f)});this.selected=[];this.lastSelected=null},toggleSelectors:function(){if(!this.selecting){this.showSelectors()}else{this.hideSelectors()}},selectAll:function(f){_.each(this.views,function(g){g.select(f)})},deselectAll:function(f){this.lastSelected=null;_.each(this.views,function(g){g.deselect(f)})},selectRange:function(h,g){var f=this.viewRange(h,g);_.each(f,function(i){i.select()});return f},getSelectedViews:function(){return _.filter(this.views,function(f){return f.selected})},getSelectedModels:function(){return new this.collection.constructor(_.map(this.getSelectedViews(),function(f){return f.model}))},_showLoadingIndicator:function(g,f,h){this.debug("_showLoadingIndicator",this.indicator,g,f,h);f=(f!==undefined)?(f):(this.fxSpeed);if(!this.indicator){this.indicator=new LoadingIndicator(this.$el,this.$el.parent());this.debug("\t created",this.indicator)}if(!this.$el.is(":visible")){this.indicator.show(0,h)}else{this.$el.fadeOut(f);this.indicator.show(g,f,h)}},_hideLoadingIndicator:function(f,g){this.debug("_hideLoadingIndicator",this.indicator,f,g);f=(f!==undefined)?(f):(this.fxSpeed);if(this.indicator){this.indicator.hide(f,g)}},scrollPosition:function(){return this.$scrollContainer().scrollTop()},scrollTo:function(g,f){f=f||0;this.$scrollContainer().animate({scrollTop:g},f);return this},scrollToTop:function(f){return this.scrollTo(0,f)},scrollToItem:function(f,h){if(!f){return this}var g=f.$el.position().top;return this.scrollTo(g,h)},scrollToId:function(g,f){return this.scrollToItem(this.viewFromModelId(g),f)},events:{"click .select-all":"selectAll","click .deselect-all":"deselectAll"},toString:function(){return"ListPanel("+this.collection+")"}});e.prototype.templates=(function(){var g=b.wrapTemplate(["<div>",'<div class="controls"></div>','<div class="list-items"></div>','<div class="empty-message infomessagesmall"></div>',"</div>"]);var f=b.wrapTemplate(['<div class="controls">','<div class="title">','<div class="name"><%= view.title %></div>',"</div>",'<div class="subtitle"><%= view.subtitle %></div>','<div class="actions"></div>','<div class="messages"></div>','<div class="search">','<div class="search-input"></div>',"</div>",'<div class="list-actions">','<div class="btn-group">','<button class="select-all btn btn-default"','data-mode="select">',c("All"),"</button>",'<button class="deselect-all btn btn-default"','data-mode="select">',c("None"),"</button>","</div>","</div>","</div>"]);return{el:g,controls:f}}());var a=e.extend({modelCollectionKey:"contents",initialize:function(f){e.prototype.initialize.call(this,f);this.selecting=(f.selecting!==undefined)?f.selecting:false;this.setModel(this.model,f)},setModel:function(g,f){f=f||{};this.debug(this+".setModel:",g,f);this.freeModel();this.freeViews();if(g){var h=this.model?this.model.get("id"):null;this.model=g;if(this.logger){this.model.logger=this.logger}this._setUpModelListeners();this.collection.off();this.collection=(this.model[this.modelCollectionKey])?this.model[this.modelCollectionKey]:(f.collection||(new this.collectionClass([])));this._setUpCollectionListeners();if(h&&g.get("id")!==h){this.trigger("new-model",this)}}return this},freeModel:function(){if(this.model){this.stopListening(this.model)}return this},_setUpModelListeners:function(){this.log(this+"._setUpModelListeners",this.model);this.model.on("error",function(){this.trigger.apply(panel,arguments)},this);return this},_renderControls:function(g){this.debug(this+"(ListPanel)._renderControls");var h=this.model?this.model.toJSON():{},f=$(this.templates.controls(h,this));g.find(".controls").replaceWith(f);return f},toString:function(){return"ModelListPanel("+this.model+")"}});a.prototype.templates=(function(){var f=b.wrapTemplate(['<div class="controls">','<div class="title">','<div class="name"><%= model.name %></div>',"</div>",'<div class="subtitle"><%= view.subtitle %></div>','<div class="actions"></div>','<div class="messages"></div>','<div class="search">','<div class="search-input"></div>',"</div>",'<div class="list-actions">','<div class="btn-group">','<button class="select-all btn btn-default"','data-mode="select">',c("All"),"</button>",'<button class="deselect-all btn btn-default"','data-mode="select">',c("None"),"</button>","</div>","</div>","</div>"]);return _.extend(_.clone(e.prototype.templates),{controls:f})}());return{ListPanel:e,ModelListPanel:a}});
\ No newline at end of file
+define(["mvc/list/list-item","mvc/base-mvc","utils/localization","jq-plugins/ui/search-input"],function(d,b,c){var e=Backbone.View.extend(b.LoggableMixin).extend({viewClass:d.ListItemView,collectionClass:Backbone.Collection,tagName:"div",className:"list-panel",fxSpeed:"fast",emptyMsg:c("This list is empty"),noneFoundMsg:c("No matching items found"),searchPlaceholder:c("search"),multiselectActions:[],initialize:function(f,g){f=f||{};if(f.logger){this.logger=f.logger}this.log(this+".initialize:",f);this.fxSpeed=_.has(f,"fxSpeed")?(f.fxSpeed):(this.fxSpeed);this.filters=[];this.searchFor=f.searchFor||"";this.indicator=new LoadingIndicator(this.$el);this.selecting=(f.selecting!==undefined)?f.selecting:true;this.selected=f.selected||[];this.lastSelected=null;this.dragItems=f.dragItems||false;this.viewClass=f.viewClass||this.viewClass;this.views=[];this.collection=f.collection||(new this.collectionClass([]));this.filters=f.filters||[];this.$scrollContainer=f.$scrollContainer||this.$scrollContainer;this.title=f.title||"";this.subtitle=f.subtitle||"";this.multiselectActions=f.multiselectActions||this.multiselectActions;this.actionsPopup=null;this._setUpListeners()},freeViews:function(){_.each(this.views,function(f){f.off()});this.views=[];return this},_setUpListeners:function(){this.off();this.on("error",function(g,j,f,i,h){console.error(g,j,f,i,h)},this);this.on("loading",function(){this._showLoadingIndicator("loading...",40)},this);this.on("loading-done",function(){this._hideLoadingIndicator(40)},this);this.once("rendered",function(){this.trigger("rendered:initial",this)},this);if(this.logger){this.on("all",function(f){this.log(this+"",arguments)},this)}this._setUpCollectionListeners();this._setUpViewListeners();return this},_setUpCollectionListeners:function(){this.log(this+"._setUpCollectionListeners",this.collection);this.collection.off();this.collection.on("error",function(g,j,f,i,h){this.trigger("error",g,j,f,i,h)},this);this.collection.on("reset",function(){this.renderItems()},this);this.collection.on("add",this.addItemView,this);this.collection.on("remove",this.removeItemView,this);if(this.logger){this.collection.on("all",function(f){this.info(this+"(collection)",arguments)},this)}return this},_setUpViewListeners:function(){this.log(this+"._setUpViewListeners");this.on("view:selected",function(f,g){if(g&&g.shiftKey&&this.lastSelected){var h=this.viewFromModelId(this.lastSelected);if(h){this.selectRange(f,h)}}else{if(g&&g.altKey&&!this.selecting){this.showSelectors()}}this.selected.push(f.model.id);this.lastSelected=f.model.id},this);this.on("view:de-selected",function(f,g){this.selected=_.without(this.selected,f.model.id)},this)},render:function(g){this.log(this+".render",g);var f=this._buildNewRender();this._setUpBehaviors(f);this._queueNewRender(f,g);return this},_buildNewRender:function(){this.debug(this+"(ListPanel)._buildNewRender");var f=$(this.templates.el({},this));this._renderControls(f);this._renderTitle(f);this._renderSubtitle(f);this._renderSearch(f);this.renderItems(f);return f},_renderControls:function(g){this.debug(this+"(ListPanel)._renderControls");var f=$(this.templates.controls({},this));g.find(".controls").replaceWith(f);return f},_renderTitle:function(f){},_renderSubtitle:function(f){},_queueNewRender:function(g,h){h=(h===undefined)?(this.fxSpeed):(h);var f=this;f.log("_queueNewRender:",g,h);$(f).queue("fx",[function(i){this.$el.fadeOut(h,i)},function(i){f._swapNewRender(g);i()},function(i){this.$el.fadeIn(h,i)},function(i){f.trigger("rendered",f);i()}])},_swapNewRender:function(f){this.$el.empty().attr("class",this.className).append(f.children());if(this.selecting){this.showSelectors(0)}return this},_setUpBehaviors:function(f){f=f||this.$el;f.find(".controls [title]").tooltip({placement:"bottom"});return this},$scrollContainer:function(){return this.$el.parent().parent()},$list:function(f){return(f||this.$el).find("> .list-items")},$messages:function(f){return(f||this.$el).find("> .controls .messages")},$emptyMessage:function(f){return(f||this.$el).find("> .empty-message")},renderItems:function(h){h=h||this.$el;var f=this;f.log(this+".renderItems",h);var g=f.$list(h);f.views=f._filterCollection().map(function(i){return f._createItemView(i).render(0)});g.empty();if(f.views.length){f._attachItems(h);f.$emptyMessage(h).hide()}else{f._renderEmptyMessage(h).show()}return f.views},_filterCollection:function(){var f=this;return f.collection.filter(_.bind(f._filterItem,f))},_filterItem:function(g){var f=this;return(_.every(f.filters.map(function(h){return h.call(g)})))&&(!f.searchFor||g.matchesAll(f.searchFor))},_createItemView:function(h){var i=this._getItemViewClass(h),g=_.extend(this._getItemViewOptions(h),{model:h}),f=new i(g);this._setUpItemViewListeners(f);return f},_getItemViewClass:function(f){return this.viewClass},_getItemViewOptions:function(f){return{fxSpeed:this.fxSpeed,expanded:false,selectable:this.selecting,selected:_.contains(this.selected,f.id),draggable:this.dragItems}},_setUpItemViewListeners:function(g){var f=this;g.on("all",function(){var h=Array.prototype.slice.call(arguments,0);h[0]="view:"+h[0];f.trigger.apply(f,h)});g.on("draggable:dragstart",function(k,h){var i={},j=this.getSelectedModels();if(j.length){i=j.toJSON()}else{i=[h.model.toJSON()]}k.dataTransfer.setData("text",JSON.stringify(i))},this);return f},_attachItems:function(f){this.$list(f).append(this.views.map(function(g){return g.$el}));return this},_renderEmptyMessage:function(f){this.debug("_renderEmptyMessage",f,this.searchFor);var g=this.searchFor?this.noneFoundMsg:this.emptyMsg;return this.$emptyMessage(f).text(g)},expandAll:function(){_.each(this.views,function(f){f.expand()})},collapseAll:function(){_.each(this.views,function(f){f.collapse()})},addItemView:function(i,j,h){this.log(this+".addItemView:",i);var g=this;if(!g._filterItem(i)){return undefined}var f=g._createItemView(i);$(f).queue("fx",[function(k){g.$emptyMessage().fadeOut(g.fxSpeed,k)},function(k){g._attachView(f);k()}]);return f},_attachView:function(g){var f=this;f.views.push(g);f.$list().append(g.render(0).$el.hide());g.$el.slideDown(f.fxSpeed)},removeItemView:function(i,j,h){this.log(this+".removeItemView:",i);var g=this,f=g.viewFromModel(i);if(!f){return undefined}g.views=_.without(g.views,f);$({}).queue("fx",[function(k){f.$el.fadeOut(g.fxSpeed,k)},function(k){f.remove();if(!g.views.length){g._renderEmptyMessage().fadeIn(g.fxSpeed,k)}else{k()}}]);return f},viewFromModelId:function(g){for(var f=0;f<this.views.length;f++){if(this.views[f].model.id===g){return this.views[f]}}return undefined},viewFromModel:function(f){if(!f){return undefined}return this.viewFromModelId(f.id)},viewsWhereModel:function(f){return this.views.filter(function(g){var i=g.model.toJSON();for(var h in f){if(f.hasOwnProperty(h)){if(i[h]!==g.model.get(h)){return false}}}return true})},viewRange:function(i,h){if(i===h){return(i)?([i]):([])}var g=this.views.indexOf(i),f=this.views.indexOf(h);if(g===-1||f===-1){if(g===f){return[]}return(g===-1)?([h]):([i])}return(g<f)?this.views.slice(g,f+1):this.views.slice(f,g+1)},_renderSearch:function(f){f.find(".controls .search-input").searchInput({placeholder:this.searchPlaceholder,initialVal:this.searchFor,onfirstsearch:_.bind(this._firstSearch,this),onsearch:_.bind(this.searchItems,this),onclear:_.bind(this.clearSearch,this)});return f},_firstSearch:function(f){this.log("onFirstSearch",f);return this.searchItems(f)},searchItems:function(f){this.searchFor=f;this.trigger("search:searching",f,this);this.renderItems();this.$("> .controls .search-query").val(f);return this},clearSearch:function(f){this.searchFor="";this.trigger("search:clear",this);this.renderItems();this.$("> .controls .search-query").val("");return this},showSelectors:function(f){f=(f!==undefined)?(f):(this.fxSpeed);this.selecting=true;this.$(".list-actions").slideDown(f);_.each(this.views,function(g){g.showSelector(f)})},hideSelectors:function(f){f=(f!==undefined)?(f):(this.fxSpeed);this.selecting=false;this.$(".list-actions").slideUp(f);_.each(this.views,function(g){g.hideSelector(f)});this.selected=[];this.lastSelected=null},toggleSelectors:function(){if(!this.selecting){this.showSelectors()}else{this.hideSelectors()}},selectAll:function(f){_.each(this.views,function(g){g.select(f)})},deselectAll:function(f){this.lastSelected=null;_.each(this.views,function(g){g.deselect(f)})},selectRange:function(h,g){var f=this.viewRange(h,g);_.each(f,function(i){i.select()});return f},getSelectedViews:function(){return _.filter(this.views,function(f){return f.selected})},getSelectedModels:function(){return new this.collection.constructor(_.map(this.getSelectedViews(),function(f){return f.model}))},_showLoadingIndicator:function(g,f,h){this.debug("_showLoadingIndicator",this.indicator,g,f,h);f=(f!==undefined)?(f):(this.fxSpeed);if(!this.indicator){this.indicator=new LoadingIndicator(this.$el,this.$el.parent());this.debug("\t created",this.indicator)}if(!this.$el.is(":visible")){this.indicator.show(0,h)}else{this.$el.fadeOut(f);this.indicator.show(g,f,h)}},_hideLoadingIndicator:function(f,g){this.debug("_hideLoadingIndicator",this.indicator,f,g);f=(f!==undefined)?(f):(this.fxSpeed);if(this.indicator){this.indicator.hide(f,g)}},scrollPosition:function(){return this.$scrollContainer().scrollTop()},scrollTo:function(g,f){f=f||0;this.$scrollContainer().animate({scrollTop:g},f);return this},scrollToTop:function(f){return this.scrollTo(0,f)},scrollToItem:function(f,h){if(!f){return this}var g=f.$el.position().top;return this.scrollTo(g,h)},scrollToId:function(g,f){return this.scrollToItem(this.viewFromModelId(g),f)},events:{"click .select-all":"selectAll","click .deselect-all":"deselectAll"},toString:function(){return"ListPanel("+this.collection+")"}});e.prototype.templates=(function(){var g=b.wrapTemplate(["<div>",'<div class="controls"></div>','<div class="list-items"></div>','<div class="empty-message infomessagesmall"></div>',"</div>"]);var f=b.wrapTemplate(['<div class="controls">','<div class="title">','<div class="name"><%= view.title %></div>',"</div>",'<div class="subtitle"><%= view.subtitle %></div>','<div class="actions"></div>','<div class="messages"></div>','<div class="search">','<div class="search-input"></div>',"</div>",'<div class="list-actions">','<div class="btn-group">','<button class="select-all btn btn-default"','data-mode="select">',c("All"),"</button>",'<button class="deselect-all btn btn-default"','data-mode="select">',c("None"),"</button>","</div>","</div>","</div>"]);return{el:g,controls:f}}());var a=e.extend({modelCollectionKey:"contents",initialize:function(f){e.prototype.initialize.call(this,f);this.selecting=(f.selecting!==undefined)?f.selecting:false;this.setModel(this.model,f)},setModel:function(g,f){f=f||{};this.debug(this+".setModel:",g,f);this.freeModel();this.freeViews();if(g){var h=this.model?this.model.get("id"):null;this.model=g;if(this.logger){this.model.logger=this.logger}this._setUpModelListeners();this.collection.off();this.collection=(this.model[this.modelCollectionKey])?this.model[this.modelCollectionKey]:(f.collection||(new this.collectionClass([])));this._setUpCollectionListeners();if(h&&g.get("id")!==h){this.trigger("new-model",this)}}return this},freeModel:function(){if(this.model){this.stopListening(this.model)}return this},_setUpModelListeners:function(){this.log(this+"._setUpModelListeners",this.model);this.model.on("error",function(){this.trigger.apply(panel,arguments)},this);return this},_renderControls:function(g){this.debug(this+"(ListPanel)._renderControls");var h=this.model?this.model.toJSON():{},f=$(this.templates.controls(h,this));g.find(".controls").replaceWith(f);return f},toString:function(){return"ModelListPanel("+this.model+")"}});a.prototype.templates=(function(){var f=b.wrapTemplate(['<div class="controls">','<div class="title">','<div class="name"><%= model.name %></div>',"</div>",'<div class="subtitle"><%= view.subtitle %></div>','<div class="actions"></div>','<div class="messages"></div>','<div class="search">','<div class="search-input"></div>',"</div>",'<div class="list-actions">','<div class="btn-group">','<button class="select-all btn btn-default"','data-mode="select">',c("All"),"</button>",'<button class="deselect-all btn btn-default"','data-mode="select">',c("None"),"</button>","</div>","</div>","</div>"]);return _.extend(_.clone(e.prototype.templates),{controls:f})}());return{ListPanel:e,ModelListPanel:a}});
\ No newline at end of file
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 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)})}})}());
\ 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};
\ No newline at end of file
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 static/style/blue/base.css
--- a/static/style/blue/base.css
+++ b/static/style/blue/base.css
@@ -1584,7 +1584,6 @@
.jstree-default-large.jstree-rtl .jstree-node{background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAACAQMAAAAD0EyKAAAABlBMVEUAAAAdHRvEkCwcAAAAAXRSTlMAQObYZgAAAAxJREFUCNdjgIIGBgABCgCBvVLXcAAAAABJRU5ErkJggg==")}
.jstree-default-large.jstree-rtl .jstree-last{background:transparent}
@media (max-width:768px){#jstree-dnd.jstree-dnd-responsive{line-height:40px;font-weight:bold;font-size:1.1em;text-shadow:1px 1px white} #jstree-dnd.jstree-dnd-responsive>i{background:transparent;width:40px;height:40px} #jstree-dnd.jstree-dnd-responsive>.jstree-ok{background-image:url("../images/jstree/40px.png");background-position:0 -200px;background-size:120px 240px} #jstree-dnd.jstree-dnd-responsive>.jstree-er{background-image:url("../images/jstree/40px.png");background-position:-40px -200px;background-size:120px 240px} #jstree-marker.jstree-dnd-responsive{border-left-width:10px;border-top-width:10px;border-bottom-width:10px;margin-top:-10px}}@media (max-width:768px){.jstree-default-responsive{} .jstree-default-responsive .jstree-icon{background-image:url("../images/jstree/40px.png")} .jstree-default-responsive .jstree-node,.jstree-default-responsive .jstree-leaf>.jstree-ocl{background:transparent} .jstree-default-responsive .jstree-node{min-height:40px;line-height:40px;margin-left:40px;min-width:40px;white-space:nowrap} .jstree-default-responsive .jstree-anchor{line-height:40px;height:40px} .jstree-default-responsive .jstree-icon,.jstree-default-responsive .jstree-icon:empty{width:40px;height:40px;line-height:40px} .jstree-default-responsive>.jstree-container-ul>.jstree-node{margin-left:0} .jstree-default-responsive.jstree-rtl .jstree-node{margin-left:0;margin-right:40px} .jstree-default-responsive.jstree-rtl .jstree-container-ul>.jstree-node{margin-right:0} .jstree-default-responsive .jstree-ocl,.jstree-default-responsive .jstree-themeicon,.jstree-default-responsive .jstree-checkbox{background-size:120px 240px} .jstree-default-responsive .jstree-leaf>.jstree-ocl{background:transparent} .jstree-default-responsive .jstree-open>.jstree-ocl{background-position:0 0px !important} .jstree-default-responsive .jstree-closed>.jstree-ocl{background-position:0 -40px !important} .jstree-default-responsive.jstree-rtl .jstree-closed>.jstree-ocl{background-position:-40px 0 !important} .jstree-default-responsive .jstree-themeicon{background-position:-40px -40px} .jstree-default-responsive .jstree-checkbox,.jstree-default-responsive .jstree-checkbox:hover{background-position:-40px -80px} .jstree-default-responsive.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox,.jstree-default-responsive.jstree-checkbox-selection .jstree-clicked>.jstree-checkbox:hover,.jstree-default-responsive .jstree-checked>.jstree-checkbox,.jstree-default-responsive .jstree-checked>.jstree-checkbox:hover{background-position:0 -80px} .jstree-default-responsive .jstree-anchor>.jstree-undetermined,.jstree-default-responsive .jstree-anchor>.jstree-undetermined:hover{background-position:0 -120px} .jstree-default-responsive .jstree-anchor{font-weight:bold;font-size:1.1em;text-shadow:1px 1px white} .jstree-default-responsive>.jstree-striped{background:transparent} .jstree-default-responsive .jstree-wholerow{border-top:1px solid rgba(255,255,255,0.7);border-bottom:1px solid rgba(64,64,64,0.2);background:#ebebeb;height:40px} .jstree-default-responsive .jstree-wholerow-hovered{background:#e7f4f9} .jstree-default-responsive .jstree-wholerow-clicked{background:#beebff} .jstree-default-responsive .jstree-children .jstree-last>.jstree-wholerow{box-shadow:inset 0 -6px 3px -5px #666} .jstree-default-responsive .jstree-children .jstree-open>.jstree-wholerow{box-shadow:inset 0 6px 3px -5px #666;border-top:0} .jstree-default-responsive .jstree-children .jstree-open+.jstree-open{box-shadow:none} .jstree-default-responsive .jstree-node,.jstree-default-responsive .jstree-icon,.jstree-default-responsive .jstree-node>.jstree-ocl,.jstree-default-responsive .jstree-themeicon,.jstree-default-responsive .jstree-checkbox{background-image:url("../images/jstree/40px.png");background-size:120px 240px} .jstree-default-responsive .jstree-node{background-position:-80px 0;background-repeat:repeat-y} .jstree-default-responsive .jstree-last{background:transparent} .jstree-default-responsive .jstree-leaf>.jstree-ocl{background-position:-40px -120px} .jstree-default-responsive .jstree-last>.jstree-ocl{background-position:-40px -160px} .jstree-default-responsive .jstree-themeicon-custom{background-color:transparent;background-image:none;background-position:0 0} .jstree-default-responsive .jstree-file{background:url("../images/jstree/40px.png") 0 -160px no-repeat;background-size:120px 240px} .jstree-default-responsive .jstree-folder{background:url("../images/jstree/40px.png") -40px -40px no-repeat;background-size:120px 240px}}.jstree-default>.jstree-container-ul>.jstree-node{margin-left:0;margin-right:0}
-.select2-minwidth{min-width:256px}
.unselectable{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;-o-user-select:none;user-select:none}
.parent-width{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;width:100%;*width:90%}
.clear:before,.clear:after{content:" ";display:table;}
@@ -1878,9 +1877,6 @@
.list-panel .empty-message{display:none;margin:0px}
.list-item .details .list-panel{margin-top:8px;border-radius:3px;background:white;padding:4px}.list-item .details .list-panel .list-items{border:1px solid #bfbfbf;border-radius:3px}.list-item .details .list-panel .list-items .list-item:first-child{border-top-width:0px;border-radius:3px 3px 0px 0px}
.list-item .details .list-panel .list-items .list-item:last-child{border-bottom-width:0px;border-radius:0px 0px 3px 3px}
-.search-input .search-query{width:100%;padding-right:24px}
-.search-input .search-clear,.search-input .search-loading{position:relative;display:inline-block;float:right;margin-top:-24px;margin-right:4px;font-size:1.4em;line-height:23px;color:grey}
-.search-input .search-clear:hover{color:#303030}
.history-panel-header .panel-header-button{display:inline-block;height:22px;width:22px;text-align:center;line-height:19px;font-size:1.2em;padding:0px}
.state-icon{font-family:FontAwesome;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;display:inline-block;margin-right:4px;vertical-align:middle;width:16px;height:16px;line-height:16px;text-align:center;font-size:16px}
.state-icon-running{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)} 100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)} 100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)} 100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0%{-ms-transform:rotate(0deg)} 100%{-ms-transform:rotate(359deg)}}@keyframes spin{0%{transform:rotate(0deg)} 100%{transform:rotate(359deg)}}.state-icon-running:before{content:"\f110"}
@@ -2081,6 +2077,9 @@
.collection-creator .footer .attributes .collection-name-prompt.validation-warning:before{content:'(required)';margin-right:4px;color:red}
.collection-creator .footer .attributes .collection-name{width:50%}.collection-creator .footer .attributes .collection-name.validation-warning{border-color:red}
.collection-creator .footer .actions .other-options>*{display:none;margin-left:4px}
+.search-input .search-query{width:100%;padding-right:24px}
+.search-input .search-clear,.search-input .search-loading{position:relative;display:inline-block;float:right;margin-top:-24px;margin-right:4px;font-size:1.4em;line-height:23px;color:grey}
+.search-input .search-clear:hover{color:#303030}
.dataset-choice{border:1px solid lightgrey;border-radius:3px;overflow:hidden;padding:10px 8px 8px 8px}.dataset-choice:hover{border-color:black;cursor:pointer}.dataset-choice:hover>*{cursor:pointer}
.dataset-choice .prompt{margin-right:8px}.dataset-choice .prompt:after{content:':'}
.dataset-choice .prompt:empty{display:none}
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 static/style/src/less/base.less
--- a/static/style/src/less/base.less
+++ b/static/style/src/less/base.less
@@ -15,11 +15,11 @@
}
@import "select2.less";
-
/* fix for zero width select2 - remove when fixed there */
.select2-minwidth {
min-width: 256px;
}
+
// galaxy sub-components
@import "frame.less";
@import "upload.less";
@@ -30,12 +30,6 @@
// Mixins
-
-/* fix for zero width select2 - remove when fixed there */
-.select2-minwidth {
- min-width: 256px;
-}
-
.unselectable {
.user-select(none);
}
@@ -1457,6 +1451,7 @@
@import "ui/paired-collection-creator.less";
+@import "ui/search-input.less";
@import "ui/dataset-choice.less";
@import "ui/peek-column-selector.less";
@import "ui/pagination.less";
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 static/style/src/less/history.less
--- a/static/style/src/less/history.less
+++ b/static/style/src/less/history.less
@@ -1,29 +1,3 @@
-// ---------------------------------------------------------------------------- search bar
-.search-input {
- .search-query {
- width : 100%;
- padding-right : 24px;
- }
- .search-clear,
- .search-loading {
- // it places the icons on the right of the bar (and puts the lotion on its skin)
- position : relative;
- display : inline-block;
-
- float : right;
- margin-top : -24px;
- margin-right : 4px;
-
- font-size : 1.4em;
- line-height : 23px;
- color : grey;
- }
-
- .search-clear:hover {
- color : @link-color;
- }
-}
-
// ---------------------------------------------------------------------------- panel header buttons
.history-panel-header {
.panel-header-button {
diff -r b51b59a29c9025290c572ecd6b842518a90a2b50 -r 33f1bf5be91b6aee2ff0a42d4315b43da0a5da33 static/style/src/less/ui/search-input.less
--- /dev/null
+++ b/static/style/src/less/ui/search-input.less
@@ -0,0 +1,26 @@
+// search bar, see: scripts/jq-plugins/ui/search-input.js
+.search-input {
+ .search-query {
+ width : 100%;
+ padding-right : 24px;
+ }
+ .search-clear,
+ .search-loading {
+ // it places the icons on the right of the bar (and puts the lotion on its skin)
+ position : relative;
+ display : inline-block;
+
+ float : right;
+ margin-top : -24px;
+ margin-right : 4px;
+
+ font-size : 1.4em;
+ line-height : 23px;
+ color : grey;
+ }
+
+ .search-clear:hover {
+ color : @link-color;
+ }
+}
+
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/59d5d556b664/
Changeset: 59d5d556b664
Branch: next-stable
User: jmchilton
Date: 2014-12-09 14:33:35+00:00
Summary: Merge stable.
Affected #: 11 files
diff -r ac30ccd5cd1e285390706592a0b005f79456cbac -r 59d5d556b6647c245fc26f331eb01e9d597c698a templates/refresh_frames.mako
--- a/templates/refresh_frames.mako
+++ b/templates/refresh_frames.mako
@@ -21,7 +21,7 @@
## Refresh masthead == user changes (backward compatibility)
if ( parent.user_changed ) {
%if trans.user:
- parent.user_changed( "${trans.user.email}", ${int( app.config.is_admin_user( trans.user ) )} );
+ parent.user_changed( "${trans.user.email | h }", ${int( app.config.is_admin_user( trans.user ) )} );
%else:
parent.user_changed( null, false );
%endif
https://bitbucket.org/galaxy/galaxy-central/commits/b51b59a29c90/
Changeset: b51b59a29c90
User: jmchilton
Date: 2014-12-09 14:33:49+00:00
Summary: Merge next-stable.
Affected #: 11 files
diff -r 511fbb67b820735128ee02e526fb6873beaa961e -r b51b59a29c9025290c572ecd6b842518a90a2b50 templates/refresh_frames.mako
--- a/templates/refresh_frames.mako
+++ b/templates/refresh_frames.mako
@@ -21,7 +21,7 @@
## Refresh masthead == user changes (backward compatibility)
if ( parent.user_changed ) {
%if trans.user:
- parent.user_changed( "${trans.user.email}", ${int( app.config.is_admin_user( trans.user ) )} );
+ parent.user_changed( "${trans.user.email | h }", ${int( app.config.is_admin_user( trans.user ) )} );
%else:
parent.user_changed( null, false );
%endif
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: jmchilton: Merged in dannon/galaxy-central/stable (pull request #596)
by commits-noreply@bitbucket.org 09 Dec '14
by commits-noreply@bitbucket.org 09 Dec '14
09 Dec '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/3e7adbbe91a0/
Changeset: 3e7adbbe91a0
Branch: stable
User: jmchilton
Date: 2014-12-09 14:31:53+00:00
Summary: Merged in dannon/galaxy-central/stable (pull request #596)
[STABLE] Grafts of next-stable commits for security release.
Affected #: 12 files
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 lib/galaxy/web/framework/helpers/__init__.py
--- a/lib/galaxy/web/framework/helpers/__init__.py
+++ b/lib/galaxy/web/framework/helpers/__init__.py
@@ -3,13 +3,13 @@
"""
import time
-from cgi import escape
from datetime import datetime, timedelta
from galaxy import eggs
from galaxy.util import hash_util
from galaxy.util.json import dumps
eggs.require( "MarkupSafe" ) #required by WebHelpers
eggs.require( "WebHelpers" )
+from markupsafe import escape
from webhelpers import date
from webhelpers.html.tags import stylesheet_link, javascript_link
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 lib/galaxy/webapps/galaxy/controllers/dataset.py
--- a/lib/galaxy/webapps/galaxy/controllers/dataset.py
+++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py
@@ -11,8 +11,7 @@
from galaxy.util.sanitize_html import sanitize_html
from galaxy.util.json import loads
from galaxy.web.base.controller import BaseUIController, ERROR, SUCCESS, url_for, UsesHistoryDatasetAssociationMixin, UsesHistoryMixin, UsesExtendedMetadataMixin
-from galaxy.web.framework.helpers import grids, iff, time_ago
-from galaxy.web.framework.helpers import to_unicode
+from galaxy.web.framework.helpers import grids, iff, time_ago, to_unicode, escape
from galaxy.tools.errors import EmailErrorReporter
eggs.require( "Paste" )
@@ -510,7 +509,7 @@
# Set referer message.
referer = trans.request.referer
if referer is not "":
- referer_message = "<a href='%s'>return to the previous page</a>" % referer
+ referer_message = "<a href='%s'>return to the previous page</a>" % escape(referer)
else:
referer_message = "<a href='%s'>go to Galaxy's start page</a>" % url_for( '/' )
# Error checking.
@@ -939,9 +938,12 @@
"""
Show the parameters used for an HDA
"""
- hda = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
+ try:
+ hda = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
+ except ValueError:
+ hda = None
if not hda:
- raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid reference dataset id: %s." % str( dataset_id ) )
+ raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid reference dataset id: %s." % escape( str( dataset_id ) ) )
if not self._can_access_dataset( trans, hda ):
return trans.show_error_message( "You are not allowed to access this dataset" )
@@ -973,7 +975,10 @@
params_objects = job.get_param_values( trans.app, ignore_errors=False )
except:
params_objects = job.get_param_values( trans.app, ignore_errors=True )
- upgrade_messages = tool.check_and_update_param_values( job.get_param_values( trans.app, ignore_errors=True ), trans, update_values=False ) #use different param_objects here, since we want to display original values as much as possible
+ # use different param_objects in the following line, since we want to display original values as much as possible
+ upgrade_messages = tool.check_and_update_param_values( job.get_param_values( trans.app, ignore_errors=True ),
+ trans,
+ update_values=False )
has_parameter_errors = True
except:
pass
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 lib/galaxy/webapps/galaxy/controllers/history.py
--- a/lib/galaxy/webapps/galaxy/controllers/history.py
+++ b/lib/galaxy/webapps/galaxy/controllers/history.py
@@ -17,7 +17,7 @@
from galaxy.web.base.controller import ExportsHistoryMixin
from galaxy.web.base.controller import ImportsHistoryMixin
from galaxy.web.base.controller import ERROR, INFO, SUCCESS, WARNING
-from galaxy.web.framework.helpers import grids, iff, time_ago
+from galaxy.web.framework.helpers import grids, iff, time_ago, escape
log = logging.getLogger( __name__ )
@@ -1234,7 +1234,7 @@
else:
referer = trans.request.referer
if referer is not "":
- referer_message = "<a href='%s'>return to the previous page</a>" % referer
+ referer_message = "<a href='%s'>return to the previous page</a>" % escape(referer)
else:
referer_message = "<a href='%s'>go to Galaxy's start page</a>" % url_for( '/' )
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 lib/galaxy/webapps/galaxy/controllers/search.py
--- a/lib/galaxy/webapps/galaxy/controllers/search.py
+++ b/lib/galaxy/webapps/galaxy/controllers/search.py
@@ -1,4 +1,3 @@
-
"""
Contains a basic search interface for Galaxy
"""
@@ -8,7 +7,13 @@
log = logging.getLogger( __name__ )
+
class SearchController( BaseUIController ):
+
@web.expose
def index(self, trans):
- return trans.fill_template( "search/index.mako")
+ """
+ Not ready for human consumption, yet. Power users can still use the
+ search API.
+ """
+ return trans.show_message("Sorry, the search interface isn't quite ready for use, yet.")
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 lib/galaxy/webapps/galaxy/controllers/tool_runner.py
--- a/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
+++ b/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
@@ -15,8 +15,8 @@
from galaxy.tools.parameters.basic import UnvalidatedValue
from galaxy.util.bunch import Bunch
from galaxy.util.hash_util import is_hashable
-from galaxy.web import error
-from galaxy.web import url_for
+from galaxy.web import error, url_for
+from galaxy.web.framework.helpers import escape
from galaxy.web.base.controller import BaseUIController
import tool_shed.util.shed_util_common as suc
@@ -57,7 +57,7 @@
return trans.response.send_redirect( url_for( controller="root", action="welcome" ) )
# When the tool form is initially loaded, the received kwd will not include a 'refresh'
# entry (which only is included when another option is selected in the tool_version_select_field),
- # so the default selected option should be the most recent version of the tool. The following
+ # so the default selected option should be the most recent version of the tool. The following
# check will mae sure this occurs.
refreshed_on_change = kwd.get( 'refresh', False )
tool_version_select_field, tools, tool = self.__get_tool_components( tool_id,
@@ -69,7 +69,7 @@
log.error( "index called with tool id '%s' but no such tool exists", tool_id )
trans.log_event( "Tool id '%s' does not exist" % tool_id )
trans.response.status = 404
- return "Tool '%s' does not exist, kwd=%s " % ( tool_id, kwd )
+ return trans.show_error_message("Tool '%s' does not exist." % ( escape(tool_id) ))
if tool.require_login and not trans.user:
message = "You must be logged in to use this tool."
status = "info"
@@ -290,7 +290,7 @@
log.error( "data_source_redirect called with tool id '%s' but no such tool exists", tool_id )
trans.log_event( "Tool id '%s' does not exist" % tool_id )
trans.response.status = 404
- return "Tool '%s' does not exist, kwd=%s " % ( tool_id, kwd )
+ return trans.show_error_message("Tool '%s' does not exist." % ( escape(tool_id) ))
if isinstance( tool, DataSourceTool ):
link = url_for( tool.action, **tool.get_static_param_values( trans ) )
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -605,8 +605,8 @@
else:
refresh_frames = [ 'masthead' ]
trans.handle_user_logout( logout_all=logout_all )
- message = 'You have been logged out.<br>You can log in again, <a target="_top" href="%s">go back to the page you were visiting</a> or <a target="_top" href="%s">go to the home page</a>.' % \
- ( escape( trans.request.referer ), url_for( '/' ) )
+ message = 'You have been logged out.<br>To log in again <a target="_top" href="%s">go to the home page</a>.' % \
+ ( url_for( '/' ) )
if biostar.biostar_logged_in( trans ):
biostar_url = biostar.biostar_logout( trans )
if biostar_url:
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 lib/galaxy/webapps/galaxy/controllers/visualization.py
--- a/lib/galaxy/webapps/galaxy/controllers/visualization.py
+++ b/lib/galaxy/webapps/galaxy/controllers/visualization.py
@@ -9,7 +9,7 @@
from galaxy import model, web
from galaxy.model.item_attrs import UsesAnnotations, UsesItemRatings
from galaxy.web.base.controller import BaseUIController, SharableMixin, UsesVisualizationMixin
-from galaxy.web.framework.helpers import time_ago, grids
+from galaxy.web.framework.helpers import time_ago, grids, escape
from galaxy import util
from galaxy.datatypes.interval import Bed
from galaxy.util.json import loads
@@ -123,7 +123,7 @@
# or_( "metadata like '%%\"dbkey\": [\"?\"]%%'", "metadata like '%%\"dbkey\": \"?\"%%'" ) \
# )
# )
-
+
class HistoryColumn( grids.GridColumn ):
""" Column for filtering by history id. """
@@ -360,7 +360,7 @@
@web.expose
@web.require_login( "use Galaxy visualizations", use_panels=True )
def list( self, trans, *args, **kwargs ):
-
+
# Handle operation
if 'operation' in kwargs and 'id' in kwargs:
session = trans.sa_session
@@ -388,7 +388,7 @@
kwargs[ 'embedded' ] = True
grid = self._user_list_grid( trans, *args, **kwargs )
return trans.fill_template( "visualization/list.mako", embedded_grid=grid, shared_by_others=shared_by_others )
-
+
#
# -- Functions for operating on visualizations. --
#
@@ -459,7 +459,7 @@
# Set referer message.
referer = trans.request.referer
if referer is not "":
- referer_message = "<a href='%s'>return to the previous page</a>" % referer
+ referer_message = "<a href='%s'>return to the previous page</a>" % escape(referer)
else:
referer_message = "<a href='%s'>go to Galaxy's start page</a>" % web.url_for( '/' )
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 lib/galaxy/webapps/galaxy/controllers/workflow.py
--- a/lib/galaxy/webapps/galaxy/controllers/workflow.py
+++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py
@@ -22,8 +22,7 @@
from galaxy.web import error, url_for
from galaxy.web.base.controller import BaseUIController, SharableMixin, UsesStoredWorkflowMixin
from galaxy.web.framework.formbuilder import form
-from galaxy.web.framework.helpers import grids, time_ago
-from galaxy.web.framework.helpers import to_unicode
+from galaxy.web.framework.helpers import grids, time_ago, to_unicode, escape
from galaxy.workflow.modules import WorkflowModuleInjector
from galaxy.workflow.modules import MissingToolException
from galaxy.workflow.modules import module_factory, is_tool_module_type
@@ -389,7 +388,7 @@
# Set referer message.
referer = trans.request.referer
if referer is not "":
- referer_message = "<a href='%s'>return to the previous page</a>" % referer
+ referer_message = "<a href='%s'>return to the previous page</a>" % escape(referer)
else:
referer_message = "<a href='%s'>go to Galaxy's start page</a>" % url_for( '/' )
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 templates/base/base_panels.mako
--- a/templates/base/base_panels.mako
+++ b/templates/base/base_panels.mako
@@ -45,7 +45,7 @@
<script>
Raven.config('${app.config.sentry_dsn_public}').install();
%if trans.user:
- Raven.setUser( { email: "${trans.user.email}" } );
+ Raven.setUser( { email: "${trans.user.email | h}" } );
%endif
</script>
%endif
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 templates/refresh_frames.mako
--- a/templates/refresh_frames.mako
+++ b/templates/refresh_frames.mako
@@ -21,7 +21,7 @@
## Refresh masthead == user changes (backward compatibility)
if ( parent.user_changed ) {
%if trans.user:
- parent.user_changed( "${trans.user.email}", ${int( app.config.is_admin_user( trans.user ) )} );
+ parent.user_changed( "${trans.user.email | h }", ${int( app.config.is_admin_user( trans.user ) )} );
%else:
parent.user_changed( null, false );
%endif
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 templates/search/index.mako
--- a/templates/search/index.mako
+++ b/templates/search/index.mako
@@ -1,52 +1,112 @@
-
<%inherit file="/webapps/galaxy/base_panels.mako"/>
-<%namespace file="/search/search.mako" import="search_init" />
-<%namespace file="/search/search.mako" import="search_dialog" /><%def name="init()">
-<%
- self.has_left_panel=False
- self.has_right_panel=False
- self.message_box_visible=False
- self.active_view="shared"
- self.overlay_visible=False
-%>
+ <%
+ self.has_left_panel=False
+ self.has_right_panel=False
+ self.message_box_visible=False
+ self.active_view="shared"
+ self.overlay_visible=False
+ %></%def><%def name="stylesheets()">
-${parent.stylesheets()}
-<style>
-.searchResult {
- border-style:dashed;
- border-width:1px;
- margin: 5px;
-}
-</style>
+ ${parent.stylesheets()}
+ <style>
+ .searchResult {
+ border-style:dashed;
+ border-width:1px;
+ margin: 5px;
+ }
+ </style>
+</%def>
+
+
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ ${h.js(
+ "libs/jquery/jquery",
+ )}
+ <script type="text/javascript">
+
+ function search_format_output(doc) {
+ var div_class = "historyItem";
+ var a = $("<div class='" + div_class + "'>")
+ a.append($("<div>").append(doc['model_class']));
+ b = a.append( $("<div class='historyItemTitle'><a href='/file/" + doc['id'] + "'>" + doc['name'] + "</a></div>") );
+ if ('misc_blurb' in doc) {
+ b.append( $("<div>").append(doc["misc_blurb"]) );
+ }
+ if ('peek' in doc) {
+ b.append( $("<pre class='peek'>").append( doc["peek"]) );
+ }
+ return a;
+ }
+
+ function doSearch(query) {
+ if (query.length > 1) {
+ var url = "/api/search";
+ $.ajax({
+ type : 'POST',
+ url: url,
+ data: JSON.stringify({"query" : query }),
+ contentType : 'application/json',
+ dataType : 'json',
+ success : function(data) {
+ var p = $("#output");
+ p.empty();
+ _.each(data.results, function(doc){
+ var div_class = "historyItem";
+ var a = $("<div class='" + div_class + "'>")
+ a.append($("<div>").append(doc['model_class']));
+ b = a.append( $("<div class='historyItemTitle'><a href='/file/" + doc['id'] + "'>" + doc['name'] + "</a></div>") );
+ if ('misc_blurb' in doc) {
+ b.append( $("<div>").append(doc["misc_blurb"]) );
+ }
+ if ('peek' in doc) {
+ b.append( $("<pre class='peek'>").append( doc["peek"]) );
+ }
+ p.append(b);
+ });
+ }
+ });
+ }
+ };
+
+
+ var queryURL = function (query) {
+ var url = "/api/search" + encodeURIComponent(query);
+ url = url + "&field=" + $("#searchFields").val();
+ if ($("#fileType").val() != "All") {
+ url = url + "&type=" + $("#fileType").val()
+ }
+ return url;
+ }
+
+ $(document).ready( function() {
+ $("#search_button").click(function() {
+ doSearch($("#search_text").val());
+ });
+ $('#search_text').keyup(function(e){
+ if(e.keyCode == 13) {
+ doSearch($("#search_text").val());
+ }
+ });
+ doSearch($("#search_text").val());
+ });
+ </script></%def>
+
<%def name="center_panel()">
-${search_init()}
-<script type="text/javascript">
-var search_format_output = function(doc) {
- var div_class = "historyItem";
- var a = $("<div class='" + div_class + "'>")
- a.append($("<div>").append(doc['model_class']));
- b = a.append( $("<div class='historyItemTitle'><a href='/file/" + doc['id'] + "'>" + doc['name'] + "</a></div>") );
- if ('misc_blurb' in doc) {
- b.append( $("<div>").append(doc["misc_blurb"]) );
- }
- if ('peek' in doc) {
- b.append( $("<pre class='peek'>").append( doc["peek"]) );
- }
- return a;
-}
+ <div id="search_box" style="margin: 20px;">
+ <input type="text" id="search_text" size="90"/>
+ </div>
+ <div style="margin: 20px;">
+ <input type="button" id="search_button" value="Search"/>
+ </div>
+ <div id="output"></div>
-</script>
-<div style="overflow: auto; height: 100%">
-${search_dialog("search_format_output")}
-</div></%def>
-
-
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 templates/webapps/galaxy/cloud/run.mako
--- a/templates/webapps/galaxy/cloud/run.mako
+++ /dev/null
@@ -1,41 +0,0 @@
-<%inherit file="/webapps/galaxy/base_panels.mako"/>
-
-<%def name="init()">
-<%
- self.has_left_panel=False
- self.has_right_panel=False
- self.active_view="shared"
- self.message_box_visible=False
-%>
-</%def>
-
-
-<%def name="center_panel()">
- <div style="overflow: auto; height: 100%;">
- <div class="page-container" style="padding: 10px;">
- <h2>Launching a Galaxy Cloud Instance</h2>
-%if error:
- <p>${error}</p>
-%elif instance:
- %if kp_material:
- <h3>Very Important Key Pair Information</h3>
- <p>A new key pair named '${kp_name}' has been created in your AWS
- account and will be used to access this instance via ssh. It is
- <strong>very important</strong> that you save the following private key
- as it is not saved on this Galaxy instance and will be permanently lost
- once you leave this page. To do this, save the following key block as
- a plain text file named '${kp_name}'.</p>
- <pre>${kp_material}</pre>
- %endif
- <p>The instance '${instance.id} has been successfully launched using the
- '${instance.image_id}' AMI.<br/> Access it at <a
- href="http://${instance.public_dns_name}">http://${instance.public_dns_name}</a></p>
- <p>SSH access is available using your private key '${kp_name}'.</p>
-%else:
- <p> Unknown failure, no instance. Please refer to your AWS console at <a
- href="https://console.aws.amazon.com">https://console.aws.amazon.com</a></p>
-%endif
- </div>
- </div>
-</%def>
-
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
11 new commits in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/c759bf3e94af/
Changeset: c759bf3e94af
Branch: stable
User: dannon
Date: 2014-11-25 19:26:52+00:00
Summary: Prevent XSS in unknown tool display, update to actually use show_error_message instead of replying with plain text.
Affected #: 2 files
diff -r 8e45b1cefba16727d4d3a7d0dceaaaf1ef400a0c -r c759bf3e94af92d7889ce847fe319bd0592f70e5 lib/galaxy/web/framework/helpers/__init__.py
--- a/lib/galaxy/web/framework/helpers/__init__.py
+++ b/lib/galaxy/web/framework/helpers/__init__.py
@@ -3,13 +3,13 @@
"""
import time
-from cgi import escape
from datetime import datetime, timedelta
from galaxy import eggs
from galaxy.util import hash_util
from galaxy.util.json import dumps
eggs.require( "MarkupSafe" ) #required by WebHelpers
eggs.require( "WebHelpers" )
+from markupsafe import escape
from webhelpers import date
from webhelpers.html.tags import stylesheet_link, javascript_link
diff -r 8e45b1cefba16727d4d3a7d0dceaaaf1ef400a0c -r c759bf3e94af92d7889ce847fe319bd0592f70e5 lib/galaxy/webapps/galaxy/controllers/tool_runner.py
--- a/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
+++ b/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
@@ -15,8 +15,8 @@
from galaxy.tools.parameters.basic import UnvalidatedValue
from galaxy.util.bunch import Bunch
from galaxy.util.hash_util import is_hashable
-from galaxy.web import error
-from galaxy.web import url_for
+from galaxy.web import error, url_for
+from galaxy.web.framework.helpers import escape
from galaxy.web.base.controller import BaseUIController
import tool_shed.util.shed_util_common as suc
@@ -57,7 +57,7 @@
return trans.response.send_redirect( url_for( controller="root", action="welcome" ) )
# When the tool form is initially loaded, the received kwd will not include a 'refresh'
# entry (which only is included when another option is selected in the tool_version_select_field),
- # so the default selected option should be the most recent version of the tool. The following
+ # so the default selected option should be the most recent version of the tool. The following
# check will mae sure this occurs.
refreshed_on_change = kwd.get( 'refresh', False )
tool_version_select_field, tools, tool = self.__get_tool_components( tool_id,
@@ -69,7 +69,7 @@
log.error( "index called with tool id '%s' but no such tool exists", tool_id )
trans.log_event( "Tool id '%s' does not exist" % tool_id )
trans.response.status = 404
- return "Tool '%s' does not exist, kwd=%s " % ( tool_id, kwd )
+ return trans.show_error_message("Tool '%s' does not exist." % ( escape(tool_id) ))
if tool.require_login and not trans.user:
message = "You must be logged in to use this tool."
status = "info"
https://bitbucket.org/galaxy/galaxy-central/commits/6a27c440865c/
Changeset: 6a27c440865c
Branch: stable
User: dannon
Date: 2014-11-25 19:31:55+00:00
Summary: Another instance of tool_id reflected xss (in data_source_redirect).
Affected #: 1 file
diff -r c759bf3e94af92d7889ce847fe319bd0592f70e5 -r 6a27c440865cdb38241b4e49664e594c719dcd2f lib/galaxy/webapps/galaxy/controllers/tool_runner.py
--- a/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
+++ b/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
@@ -290,7 +290,7 @@
log.error( "data_source_redirect called with tool id '%s' but no such tool exists", tool_id )
trans.log_event( "Tool id '%s' does not exist" % tool_id )
trans.response.status = 404
- return "Tool '%s' does not exist, kwd=%s " % ( tool_id, kwd )
+ return trans.show_error_message("Tool '%s' does not exist." % ( escape(tool_id) ))
if isinstance( tool, DataSourceTool ):
link = url_for( tool.action, **tool.get_static_param_values( trans ) )
https://bitbucket.org/galaxy/galaxy-central/commits/705e13dd0567/
Changeset: 705e13dd0567
Branch: stable
User: dannon
Date: 2014-11-25 20:14:25+00:00
Summary: Referer-in-okmessage reflective xss fix
Affected #: 4 files
diff -r 6a27c440865cdb38241b4e49664e594c719dcd2f -r 705e13dd05674a8f35771c3b86fc221842e371e9 lib/galaxy/webapps/galaxy/controllers/dataset.py
--- a/lib/galaxy/webapps/galaxy/controllers/dataset.py
+++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py
@@ -11,8 +11,7 @@
from galaxy.util.sanitize_html import sanitize_html
from galaxy.util.json import loads
from galaxy.web.base.controller import BaseUIController, ERROR, SUCCESS, url_for, UsesHistoryDatasetAssociationMixin, UsesHistoryMixin, UsesExtendedMetadataMixin
-from galaxy.web.framework.helpers import grids, iff, time_ago
-from galaxy.web.framework.helpers import to_unicode
+from galaxy.web.framework.helpers import grids, iff, time_ago, to_unicode, escape
from galaxy.tools.errors import EmailErrorReporter
eggs.require( "Paste" )
@@ -510,7 +509,7 @@
# Set referer message.
referer = trans.request.referer
if referer is not "":
- referer_message = "<a href='%s'>return to the previous page</a>" % referer
+ referer_message = "<a href='%s'>return to the previous page</a>" % escape(referer)
else:
referer_message = "<a href='%s'>go to Galaxy's start page</a>" % url_for( '/' )
# Error checking.
diff -r 6a27c440865cdb38241b4e49664e594c719dcd2f -r 705e13dd05674a8f35771c3b86fc221842e371e9 lib/galaxy/webapps/galaxy/controllers/history.py
--- a/lib/galaxy/webapps/galaxy/controllers/history.py
+++ b/lib/galaxy/webapps/galaxy/controllers/history.py
@@ -17,7 +17,7 @@
from galaxy.web.base.controller import ExportsHistoryMixin
from galaxy.web.base.controller import ImportsHistoryMixin
from galaxy.web.base.controller import ERROR, INFO, SUCCESS, WARNING
-from galaxy.web.framework.helpers import grids, iff, time_ago
+from galaxy.web.framework.helpers import grids, iff, time_ago, escape
log = logging.getLogger( __name__ )
@@ -1232,7 +1232,7 @@
else:
referer = trans.request.referer
if referer is not "":
- referer_message = "<a href='%s'>return to the previous page</a>" % referer
+ referer_message = "<a href='%s'>return to the previous page</a>" % escape(referer)
else:
referer_message = "<a href='%s'>go to Galaxy's start page</a>" % url_for( '/' )
diff -r 6a27c440865cdb38241b4e49664e594c719dcd2f -r 705e13dd05674a8f35771c3b86fc221842e371e9 lib/galaxy/webapps/galaxy/controllers/visualization.py
--- a/lib/galaxy/webapps/galaxy/controllers/visualization.py
+++ b/lib/galaxy/webapps/galaxy/controllers/visualization.py
@@ -9,7 +9,7 @@
from galaxy import model, web
from galaxy.model.item_attrs import UsesAnnotations, UsesItemRatings
from galaxy.web.base.controller import BaseUIController, SharableMixin, UsesVisualizationMixin
-from galaxy.web.framework.helpers import time_ago, grids
+from galaxy.web.framework.helpers import time_ago, grids, escape
from galaxy import util
from galaxy.datatypes.interval import Bed
from galaxy.util.json import loads
@@ -123,7 +123,7 @@
# or_( "metadata like '%%\"dbkey\": [\"?\"]%%'", "metadata like '%%\"dbkey\": \"?\"%%'" ) \
# )
# )
-
+
class HistoryColumn( grids.GridColumn ):
""" Column for filtering by history id. """
@@ -360,7 +360,7 @@
@web.expose
@web.require_login( "use Galaxy visualizations", use_panels=True )
def list( self, trans, *args, **kwargs ):
-
+
# Handle operation
if 'operation' in kwargs and 'id' in kwargs:
session = trans.sa_session
@@ -388,7 +388,7 @@
kwargs[ 'embedded' ] = True
grid = self._user_list_grid( trans, *args, **kwargs )
return trans.fill_template( "visualization/list.mako", embedded_grid=grid, shared_by_others=shared_by_others )
-
+
#
# -- Functions for operating on visualizations. --
#
@@ -459,7 +459,7 @@
# Set referer message.
referer = trans.request.referer
if referer is not "":
- referer_message = "<a href='%s'>return to the previous page</a>" % referer
+ referer_message = "<a href='%s'>return to the previous page</a>" % escape(referer)
else:
referer_message = "<a href='%s'>go to Galaxy's start page</a>" % web.url_for( '/' )
diff -r 6a27c440865cdb38241b4e49664e594c719dcd2f -r 705e13dd05674a8f35771c3b86fc221842e371e9 lib/galaxy/webapps/galaxy/controllers/workflow.py
--- a/lib/galaxy/webapps/galaxy/controllers/workflow.py
+++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py
@@ -22,8 +22,7 @@
from galaxy.web import error, url_for
from galaxy.web.base.controller import BaseUIController, SharableMixin, UsesStoredWorkflowMixin
from galaxy.web.framework.formbuilder import form
-from galaxy.web.framework.helpers import grids, time_ago
-from galaxy.web.framework.helpers import to_unicode
+from galaxy.web.framework.helpers import grids, time_ago, to_unicode, escape
from galaxy.workflow.modules import WorkflowModuleInjector
from galaxy.workflow.modules import MissingToolException
from galaxy.workflow.modules import module_factory, is_tool_module_type
@@ -389,7 +388,7 @@
# Set referer message.
referer = trans.request.referer
if referer is not "":
- referer_message = "<a href='%s'>return to the previous page</a>" % referer
+ referer_message = "<a href='%s'>return to the previous page</a>" % escape(referer)
else:
referer_message = "<a href='%s'>go to Galaxy's start page</a>" % url_for( '/' )
https://bitbucket.org/galaxy/galaxy-central/commits/b6ee4c07d0ab/
Changeset: b6ee4c07d0ab
Branch: stable
User: dannon
Date: 2014-11-25 20:14:58+00:00
Summary: Referer-in-logout reflective xss fix.
Affected #: 1 file
diff -r 705e13dd05674a8f35771c3b86fc221842e371e9 -r b6ee4c07d0abb03f1d017c7756a59606594a88a2 lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -605,8 +605,8 @@
else:
refresh_frames = [ 'masthead' ]
trans.handle_user_logout( logout_all=logout_all )
- message = 'You have been logged out.<br>You can log in again, <a target="_top" href="%s">go back to the page you were visiting</a> or <a target="_top" href="%s">go to the home page</a>.' % \
- ( escape( trans.request.referer ), url_for( '/' ) )
+ message = 'You have been logged out.<br>To log in again <a target="_top" href="%s">go to the home page</a>.' % \
+ ( url_for( '/' ) )
if biostar.biostar_logged_in( trans ):
biostar_url = biostar.biostar_logout( trans )
if biostar_url:
https://bitbucket.org/galaxy/galaxy-central/commits/925ce4a5eb77/
Changeset: 925ce4a5eb77
Branch: stable
User: dannon
Date: 2014-12-04 16:51:46+00:00
Summary: Remove unused cloud/run.mako; all functionality is rolled into cloud/index.
Affected #: 1 file
diff -r b6ee4c07d0abb03f1d017c7756a59606594a88a2 -r 925ce4a5eb7730dffad81391e2e0caff5d764fac templates/webapps/galaxy/cloud/run.mako
--- a/templates/webapps/galaxy/cloud/run.mako
+++ /dev/null
@@ -1,41 +0,0 @@
-<%inherit file="/webapps/galaxy/base_panels.mako"/>
-
-<%def name="init()">
-<%
- self.has_left_panel=False
- self.has_right_panel=False
- self.active_view="shared"
- self.message_box_visible=False
-%>
-</%def>
-
-
-<%def name="center_panel()">
- <div style="overflow: auto; height: 100%;">
- <div class="page-container" style="padding: 10px;">
- <h2>Launching a Galaxy Cloud Instance</h2>
-%if error:
- <p>${error}</p>
-%elif instance:
- %if kp_material:
- <h3>Very Important Key Pair Information</h3>
- <p>A new key pair named '${kp_name}' has been created in your AWS
- account and will be used to access this instance via ssh. It is
- <strong>very important</strong> that you save the following private key
- as it is not saved on this Galaxy instance and will be permanently lost
- once you leave this page. To do this, save the following key block as
- a plain text file named '${kp_name}'.</p>
- <pre>${kp_material}</pre>
- %endif
- <p>The instance '${instance.id} has been successfully launched using the
- '${instance.image_id}' AMI.<br/> Access it at <a
- href="http://${instance.public_dns_name}">http://${instance.public_dns_name}</a></p>
- <p>SSH access is available using your private key '${kp_name}'.</p>
-%else:
- <p> Unknown failure, no instance. Please refer to your AWS console at <a
- href="https://console.aws.amazon.com">https://console.aws.amazon.com</a></p>
-%endif
- </div>
- </div>
-</%def>
-
https://bitbucket.org/galaxy/galaxy-central/commits/58b1fe20c936/
Changeset: 58b1fe20c936
Branch: stable
User: dannon
Date: 2014-12-04 17:05:07+00:00
Summary: Don't trust user email rendered into page unescaped for Raven.
Affected #: 1 file
diff -r 925ce4a5eb7730dffad81391e2e0caff5d764fac -r 58b1fe20c93603b62becc2b669fe9e4e380c40c5 templates/base/base_panels.mako
--- a/templates/base/base_panels.mako
+++ b/templates/base/base_panels.mako
@@ -45,7 +45,7 @@
<script>
Raven.config('${app.config.sentry_dsn_public}').install();
%if trans.user:
- Raven.setUser( { email: "${trans.user.email}" } );
+ Raven.setUser( { email: "${trans.user.email | h}" } );
%endif
</script>
%endif
https://bitbucket.org/galaxy/galaxy-central/commits/bb90975b564a/
Changeset: bb90975b564a
Branch: stable
User: dannon
Date: 2014-12-04 18:10:37+00:00
Summary: Catch ValueError and actually log it instead of blowing up w/ invalid dataset_id. Raise httpexception.
Affected #: 1 file
diff -r 58b1fe20c93603b62becc2b669fe9e4e380c40c5 -r bb90975b564a98a98cd215ae3a29771ea9dbc31c lib/galaxy/webapps/galaxy/controllers/dataset.py
--- a/lib/galaxy/webapps/galaxy/controllers/dataset.py
+++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py
@@ -938,9 +938,12 @@
"""
Show the parameters used for an HDA
"""
- hda = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
+ try:
+ hda = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
+ except ValueError:
+ hda = None
if not hda:
- raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid reference dataset id: %s." % str( dataset_id ) )
+ raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid reference dataset id: %s." % escape( str( dataset_id ) ) )
if not self._can_access_dataset( trans, hda ):
return trans.show_error_message( "You are not allowed to access this dataset" )
@@ -972,7 +975,10 @@
params_objects = job.get_param_values( trans.app, ignore_errors=False )
except:
params_objects = job.get_param_values( trans.app, ignore_errors=True )
- upgrade_messages = tool.check_and_update_param_values( job.get_param_values( trans.app, ignore_errors=True ), trans, update_values=False ) #use different param_objects here, since we want to display original values as much as possible
+ # use different param_objects in the following line, since we want to display original values as much as possible
+ upgrade_messages = tool.check_and_update_param_values( job.get_param_values( trans.app, ignore_errors=True ),
+ trans,
+ update_values=False )
has_parameter_errors = True
except:
pass
https://bitbucket.org/galaxy/galaxy-central/commits/9612d9eedace/
Changeset: 9612d9eedace
Branch: stable
User: dannon
Date: 2014-12-04 20:23:35+00:00
Summary: Fix search to work for at least datasets, hdas, etc.
Affected #: 1 file
diff -r bb90975b564a98a98cd215ae3a29771ea9dbc31c -r 9612d9eedace3e2a9f6eb752228d5a6e651737cb templates/search/index.mako
--- a/templates/search/index.mako
+++ b/templates/search/index.mako
@@ -1,52 +1,112 @@
-
<%inherit file="/webapps/galaxy/base_panels.mako"/>
-<%namespace file="/search/search.mako" import="search_init" />
-<%namespace file="/search/search.mako" import="search_dialog" /><%def name="init()">
-<%
- self.has_left_panel=False
- self.has_right_panel=False
- self.message_box_visible=False
- self.active_view="shared"
- self.overlay_visible=False
-%>
+ <%
+ self.has_left_panel=False
+ self.has_right_panel=False
+ self.message_box_visible=False
+ self.active_view="shared"
+ self.overlay_visible=False
+ %></%def><%def name="stylesheets()">
-${parent.stylesheets()}
-<style>
-.searchResult {
- border-style:dashed;
- border-width:1px;
- margin: 5px;
-}
-</style>
+ ${parent.stylesheets()}
+ <style>
+ .searchResult {
+ border-style:dashed;
+ border-width:1px;
+ margin: 5px;
+ }
+ </style>
+</%def>
+
+
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ ${h.js(
+ "libs/jquery/jquery",
+ )}
+ <script type="text/javascript">
+
+ function search_format_output(doc) {
+ var div_class = "historyItem";
+ var a = $("<div class='" + div_class + "'>")
+ a.append($("<div>").append(doc['model_class']));
+ b = a.append( $("<div class='historyItemTitle'><a href='/file/" + doc['id'] + "'>" + doc['name'] + "</a></div>") );
+ if ('misc_blurb' in doc) {
+ b.append( $("<div>").append(doc["misc_blurb"]) );
+ }
+ if ('peek' in doc) {
+ b.append( $("<pre class='peek'>").append( doc["peek"]) );
+ }
+ return a;
+ }
+
+ function doSearch(query) {
+ if (query.length > 1) {
+ var url = "/api/search";
+ $.ajax({
+ type : 'POST',
+ url: url,
+ data: JSON.stringify({"query" : query }),
+ contentType : 'application/json',
+ dataType : 'json',
+ success : function(data) {
+ var p = $("#output");
+ p.empty();
+ _.each(data.results, function(doc){
+ var div_class = "historyItem";
+ var a = $("<div class='" + div_class + "'>")
+ a.append($("<div>").append(doc['model_class']));
+ b = a.append( $("<div class='historyItemTitle'><a href='/file/" + doc['id'] + "'>" + doc['name'] + "</a></div>") );
+ if ('misc_blurb' in doc) {
+ b.append( $("<div>").append(doc["misc_blurb"]) );
+ }
+ if ('peek' in doc) {
+ b.append( $("<pre class='peek'>").append( doc["peek"]) );
+ }
+ p.append(b);
+ });
+ }
+ });
+ }
+ };
+
+
+ var queryURL = function (query) {
+ var url = "/api/search" + encodeURIComponent(query);
+ url = url + "&field=" + $("#searchFields").val();
+ if ($("#fileType").val() != "All") {
+ url = url + "&type=" + $("#fileType").val()
+ }
+ return url;
+ }
+
+ $(document).ready( function() {
+ $("#search_button").click(function() {
+ doSearch($("#search_text").val());
+ });
+ $('#search_text').keyup(function(e){
+ if(e.keyCode == 13) {
+ doSearch($("#search_text").val());
+ }
+ });
+ doSearch($("#search_text").val());
+ });
+ </script></%def>
+
<%def name="center_panel()">
-${search_init()}
-<script type="text/javascript">
-var search_format_output = function(doc) {
- var div_class = "historyItem";
- var a = $("<div class='" + div_class + "'>")
- a.append($("<div>").append(doc['model_class']));
- b = a.append( $("<div class='historyItemTitle'><a href='/file/" + doc['id'] + "'>" + doc['name'] + "</a></div>") );
- if ('misc_blurb' in doc) {
- b.append( $("<div>").append(doc["misc_blurb"]) );
- }
- if ('peek' in doc) {
- b.append( $("<pre class='peek'>").append( doc["peek"]) );
- }
- return a;
-}
+ <div id="search_box" style="margin: 20px;">
+ <input type="text" id="search_text" size="90"/>
+ </div>
+ <div style="margin: 20px;">
+ <input type="button" id="search_button" value="Search"/>
+ </div>
+ <div id="output"></div>
-</script>
-<div style="overflow: auto; height: 100%">
-${search_dialog("search_format_output")}
-</div></%def>
-
-
https://bitbucket.org/galaxy/galaxy-central/commits/5e4d8a48320f/
Changeset: 5e4d8a48320f
Branch: stable
User: dannon
Date: 2014-12-04 20:27:19+00:00
Summary: Disable search interface for right now -- I made it not broken in the previous commit, but nobody should be using this yet.
Affected #: 1 file
diff -r 9612d9eedace3e2a9f6eb752228d5a6e651737cb -r 5e4d8a48320fb516ea8695bfdfdedc1960594f84 lib/galaxy/webapps/galaxy/controllers/search.py
--- a/lib/galaxy/webapps/galaxy/controllers/search.py
+++ b/lib/galaxy/webapps/galaxy/controllers/search.py
@@ -1,4 +1,3 @@
-
"""
Contains a basic search interface for Galaxy
"""
@@ -8,7 +7,13 @@
log = logging.getLogger( __name__ )
+
class SearchController( BaseUIController ):
+
@web.expose
def index(self, trans):
- return trans.fill_template( "search/index.mako")
+ """
+ Not ready for human consumption, yet. Power users can still use the
+ search API.
+ """
+ return trans.show_message("Sorry, the search interface isn't quite ready for use, yet.")
https://bitbucket.org/galaxy/galaxy-central/commits/fdc7863699d2/
Changeset: fdc7863699d2
Branch: stable
User: dannon
Date: 2014-12-08 22:16:21+00:00
Summary: One more place we shouldn't trust user_email.
Affected #: 1 file
diff -r 5e4d8a48320fb516ea8695bfdfdedc1960594f84 -r fdc7863699d233e2b44d6d42f47dfaaa97e0d80e templates/refresh_frames.mako
--- a/templates/refresh_frames.mako
+++ b/templates/refresh_frames.mako
@@ -21,7 +21,7 @@
## Refresh masthead == user changes (backward compatibility)
if ( parent.user_changed ) {
%if trans.user:
- parent.user_changed( "${trans.user.email}", ${int( app.config.is_admin_user( trans.user ) )} );
+ parent.user_changed( "${trans.user.email | h }", ${int( app.config.is_admin_user( trans.user ) )} );
%else:
parent.user_changed( null, false );
%endif
https://bitbucket.org/galaxy/galaxy-central/commits/3e7adbbe91a0/
Changeset: 3e7adbbe91a0
Branch: stable
User: jmchilton
Date: 2014-12-09 14:31:53+00:00
Summary: Merged in dannon/galaxy-central/stable (pull request #596)
[STABLE] Grafts of next-stable commits for security release.
Affected #: 12 files
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 lib/galaxy/web/framework/helpers/__init__.py
--- a/lib/galaxy/web/framework/helpers/__init__.py
+++ b/lib/galaxy/web/framework/helpers/__init__.py
@@ -3,13 +3,13 @@
"""
import time
-from cgi import escape
from datetime import datetime, timedelta
from galaxy import eggs
from galaxy.util import hash_util
from galaxy.util.json import dumps
eggs.require( "MarkupSafe" ) #required by WebHelpers
eggs.require( "WebHelpers" )
+from markupsafe import escape
from webhelpers import date
from webhelpers.html.tags import stylesheet_link, javascript_link
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 lib/galaxy/webapps/galaxy/controllers/dataset.py
--- a/lib/galaxy/webapps/galaxy/controllers/dataset.py
+++ b/lib/galaxy/webapps/galaxy/controllers/dataset.py
@@ -11,8 +11,7 @@
from galaxy.util.sanitize_html import sanitize_html
from galaxy.util.json import loads
from galaxy.web.base.controller import BaseUIController, ERROR, SUCCESS, url_for, UsesHistoryDatasetAssociationMixin, UsesHistoryMixin, UsesExtendedMetadataMixin
-from galaxy.web.framework.helpers import grids, iff, time_ago
-from galaxy.web.framework.helpers import to_unicode
+from galaxy.web.framework.helpers import grids, iff, time_ago, to_unicode, escape
from galaxy.tools.errors import EmailErrorReporter
eggs.require( "Paste" )
@@ -510,7 +509,7 @@
# Set referer message.
referer = trans.request.referer
if referer is not "":
- referer_message = "<a href='%s'>return to the previous page</a>" % referer
+ referer_message = "<a href='%s'>return to the previous page</a>" % escape(referer)
else:
referer_message = "<a href='%s'>go to Galaxy's start page</a>" % url_for( '/' )
# Error checking.
@@ -939,9 +938,12 @@
"""
Show the parameters used for an HDA
"""
- hda = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
+ try:
+ hda = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( trans.security.decode_id( dataset_id ) )
+ except ValueError:
+ hda = None
if not hda:
- raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid reference dataset id: %s." % str( dataset_id ) )
+ raise paste.httpexceptions.HTTPRequestRangeNotSatisfiable( "Invalid reference dataset id: %s." % escape( str( dataset_id ) ) )
if not self._can_access_dataset( trans, hda ):
return trans.show_error_message( "You are not allowed to access this dataset" )
@@ -973,7 +975,10 @@
params_objects = job.get_param_values( trans.app, ignore_errors=False )
except:
params_objects = job.get_param_values( trans.app, ignore_errors=True )
- upgrade_messages = tool.check_and_update_param_values( job.get_param_values( trans.app, ignore_errors=True ), trans, update_values=False ) #use different param_objects here, since we want to display original values as much as possible
+ # use different param_objects in the following line, since we want to display original values as much as possible
+ upgrade_messages = tool.check_and_update_param_values( job.get_param_values( trans.app, ignore_errors=True ),
+ trans,
+ update_values=False )
has_parameter_errors = True
except:
pass
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 lib/galaxy/webapps/galaxy/controllers/history.py
--- a/lib/galaxy/webapps/galaxy/controllers/history.py
+++ b/lib/galaxy/webapps/galaxy/controllers/history.py
@@ -17,7 +17,7 @@
from galaxy.web.base.controller import ExportsHistoryMixin
from galaxy.web.base.controller import ImportsHistoryMixin
from galaxy.web.base.controller import ERROR, INFO, SUCCESS, WARNING
-from galaxy.web.framework.helpers import grids, iff, time_ago
+from galaxy.web.framework.helpers import grids, iff, time_ago, escape
log = logging.getLogger( __name__ )
@@ -1234,7 +1234,7 @@
else:
referer = trans.request.referer
if referer is not "":
- referer_message = "<a href='%s'>return to the previous page</a>" % referer
+ referer_message = "<a href='%s'>return to the previous page</a>" % escape(referer)
else:
referer_message = "<a href='%s'>go to Galaxy's start page</a>" % url_for( '/' )
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 lib/galaxy/webapps/galaxy/controllers/search.py
--- a/lib/galaxy/webapps/galaxy/controllers/search.py
+++ b/lib/galaxy/webapps/galaxy/controllers/search.py
@@ -1,4 +1,3 @@
-
"""
Contains a basic search interface for Galaxy
"""
@@ -8,7 +7,13 @@
log = logging.getLogger( __name__ )
+
class SearchController( BaseUIController ):
+
@web.expose
def index(self, trans):
- return trans.fill_template( "search/index.mako")
+ """
+ Not ready for human consumption, yet. Power users can still use the
+ search API.
+ """
+ return trans.show_message("Sorry, the search interface isn't quite ready for use, yet.")
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 lib/galaxy/webapps/galaxy/controllers/tool_runner.py
--- a/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
+++ b/lib/galaxy/webapps/galaxy/controllers/tool_runner.py
@@ -15,8 +15,8 @@
from galaxy.tools.parameters.basic import UnvalidatedValue
from galaxy.util.bunch import Bunch
from galaxy.util.hash_util import is_hashable
-from galaxy.web import error
-from galaxy.web import url_for
+from galaxy.web import error, url_for
+from galaxy.web.framework.helpers import escape
from galaxy.web.base.controller import BaseUIController
import tool_shed.util.shed_util_common as suc
@@ -57,7 +57,7 @@
return trans.response.send_redirect( url_for( controller="root", action="welcome" ) )
# When the tool form is initially loaded, the received kwd will not include a 'refresh'
# entry (which only is included when another option is selected in the tool_version_select_field),
- # so the default selected option should be the most recent version of the tool. The following
+ # so the default selected option should be the most recent version of the tool. The following
# check will mae sure this occurs.
refreshed_on_change = kwd.get( 'refresh', False )
tool_version_select_field, tools, tool = self.__get_tool_components( tool_id,
@@ -69,7 +69,7 @@
log.error( "index called with tool id '%s' but no such tool exists", tool_id )
trans.log_event( "Tool id '%s' does not exist" % tool_id )
trans.response.status = 404
- return "Tool '%s' does not exist, kwd=%s " % ( tool_id, kwd )
+ return trans.show_error_message("Tool '%s' does not exist." % ( escape(tool_id) ))
if tool.require_login and not trans.user:
message = "You must be logged in to use this tool."
status = "info"
@@ -290,7 +290,7 @@
log.error( "data_source_redirect called with tool id '%s' but no such tool exists", tool_id )
trans.log_event( "Tool id '%s' does not exist" % tool_id )
trans.response.status = 404
- return "Tool '%s' does not exist, kwd=%s " % ( tool_id, kwd )
+ return trans.show_error_message("Tool '%s' does not exist." % ( escape(tool_id) ))
if isinstance( tool, DataSourceTool ):
link = url_for( tool.action, **tool.get_static_param_values( trans ) )
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 lib/galaxy/webapps/galaxy/controllers/user.py
--- a/lib/galaxy/webapps/galaxy/controllers/user.py
+++ b/lib/galaxy/webapps/galaxy/controllers/user.py
@@ -605,8 +605,8 @@
else:
refresh_frames = [ 'masthead' ]
trans.handle_user_logout( logout_all=logout_all )
- message = 'You have been logged out.<br>You can log in again, <a target="_top" href="%s">go back to the page you were visiting</a> or <a target="_top" href="%s">go to the home page</a>.' % \
- ( escape( trans.request.referer ), url_for( '/' ) )
+ message = 'You have been logged out.<br>To log in again <a target="_top" href="%s">go to the home page</a>.' % \
+ ( url_for( '/' ) )
if biostar.biostar_logged_in( trans ):
biostar_url = biostar.biostar_logout( trans )
if biostar_url:
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 lib/galaxy/webapps/galaxy/controllers/visualization.py
--- a/lib/galaxy/webapps/galaxy/controllers/visualization.py
+++ b/lib/galaxy/webapps/galaxy/controllers/visualization.py
@@ -9,7 +9,7 @@
from galaxy import model, web
from galaxy.model.item_attrs import UsesAnnotations, UsesItemRatings
from galaxy.web.base.controller import BaseUIController, SharableMixin, UsesVisualizationMixin
-from galaxy.web.framework.helpers import time_ago, grids
+from galaxy.web.framework.helpers import time_ago, grids, escape
from galaxy import util
from galaxy.datatypes.interval import Bed
from galaxy.util.json import loads
@@ -123,7 +123,7 @@
# or_( "metadata like '%%\"dbkey\": [\"?\"]%%'", "metadata like '%%\"dbkey\": \"?\"%%'" ) \
# )
# )
-
+
class HistoryColumn( grids.GridColumn ):
""" Column for filtering by history id. """
@@ -360,7 +360,7 @@
@web.expose
@web.require_login( "use Galaxy visualizations", use_panels=True )
def list( self, trans, *args, **kwargs ):
-
+
# Handle operation
if 'operation' in kwargs and 'id' in kwargs:
session = trans.sa_session
@@ -388,7 +388,7 @@
kwargs[ 'embedded' ] = True
grid = self._user_list_grid( trans, *args, **kwargs )
return trans.fill_template( "visualization/list.mako", embedded_grid=grid, shared_by_others=shared_by_others )
-
+
#
# -- Functions for operating on visualizations. --
#
@@ -459,7 +459,7 @@
# Set referer message.
referer = trans.request.referer
if referer is not "":
- referer_message = "<a href='%s'>return to the previous page</a>" % referer
+ referer_message = "<a href='%s'>return to the previous page</a>" % escape(referer)
else:
referer_message = "<a href='%s'>go to Galaxy's start page</a>" % web.url_for( '/' )
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 lib/galaxy/webapps/galaxy/controllers/workflow.py
--- a/lib/galaxy/webapps/galaxy/controllers/workflow.py
+++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py
@@ -22,8 +22,7 @@
from galaxy.web import error, url_for
from galaxy.web.base.controller import BaseUIController, SharableMixin, UsesStoredWorkflowMixin
from galaxy.web.framework.formbuilder import form
-from galaxy.web.framework.helpers import grids, time_ago
-from galaxy.web.framework.helpers import to_unicode
+from galaxy.web.framework.helpers import grids, time_ago, to_unicode, escape
from galaxy.workflow.modules import WorkflowModuleInjector
from galaxy.workflow.modules import MissingToolException
from galaxy.workflow.modules import module_factory, is_tool_module_type
@@ -389,7 +388,7 @@
# Set referer message.
referer = trans.request.referer
if referer is not "":
- referer_message = "<a href='%s'>return to the previous page</a>" % referer
+ referer_message = "<a href='%s'>return to the previous page</a>" % escape(referer)
else:
referer_message = "<a href='%s'>go to Galaxy's start page</a>" % url_for( '/' )
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 templates/base/base_panels.mako
--- a/templates/base/base_panels.mako
+++ b/templates/base/base_panels.mako
@@ -45,7 +45,7 @@
<script>
Raven.config('${app.config.sentry_dsn_public}').install();
%if trans.user:
- Raven.setUser( { email: "${trans.user.email}" } );
+ Raven.setUser( { email: "${trans.user.email | h}" } );
%endif
</script>
%endif
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 templates/refresh_frames.mako
--- a/templates/refresh_frames.mako
+++ b/templates/refresh_frames.mako
@@ -21,7 +21,7 @@
## Refresh masthead == user changes (backward compatibility)
if ( parent.user_changed ) {
%if trans.user:
- parent.user_changed( "${trans.user.email}", ${int( app.config.is_admin_user( trans.user ) )} );
+ parent.user_changed( "${trans.user.email | h }", ${int( app.config.is_admin_user( trans.user ) )} );
%else:
parent.user_changed( null, false );
%endif
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 templates/search/index.mako
--- a/templates/search/index.mako
+++ b/templates/search/index.mako
@@ -1,52 +1,112 @@
-
<%inherit file="/webapps/galaxy/base_panels.mako"/>
-<%namespace file="/search/search.mako" import="search_init" />
-<%namespace file="/search/search.mako" import="search_dialog" /><%def name="init()">
-<%
- self.has_left_panel=False
- self.has_right_panel=False
- self.message_box_visible=False
- self.active_view="shared"
- self.overlay_visible=False
-%>
+ <%
+ self.has_left_panel=False
+ self.has_right_panel=False
+ self.message_box_visible=False
+ self.active_view="shared"
+ self.overlay_visible=False
+ %></%def><%def name="stylesheets()">
-${parent.stylesheets()}
-<style>
-.searchResult {
- border-style:dashed;
- border-width:1px;
- margin: 5px;
-}
-</style>
+ ${parent.stylesheets()}
+ <style>
+ .searchResult {
+ border-style:dashed;
+ border-width:1px;
+ margin: 5px;
+ }
+ </style>
+</%def>
+
+
+<%def name="javascripts()">
+ ${parent.javascripts()}
+ ${h.js(
+ "libs/jquery/jquery",
+ )}
+ <script type="text/javascript">
+
+ function search_format_output(doc) {
+ var div_class = "historyItem";
+ var a = $("<div class='" + div_class + "'>")
+ a.append($("<div>").append(doc['model_class']));
+ b = a.append( $("<div class='historyItemTitle'><a href='/file/" + doc['id'] + "'>" + doc['name'] + "</a></div>") );
+ if ('misc_blurb' in doc) {
+ b.append( $("<div>").append(doc["misc_blurb"]) );
+ }
+ if ('peek' in doc) {
+ b.append( $("<pre class='peek'>").append( doc["peek"]) );
+ }
+ return a;
+ }
+
+ function doSearch(query) {
+ if (query.length > 1) {
+ var url = "/api/search";
+ $.ajax({
+ type : 'POST',
+ url: url,
+ data: JSON.stringify({"query" : query }),
+ contentType : 'application/json',
+ dataType : 'json',
+ success : function(data) {
+ var p = $("#output");
+ p.empty();
+ _.each(data.results, function(doc){
+ var div_class = "historyItem";
+ var a = $("<div class='" + div_class + "'>")
+ a.append($("<div>").append(doc['model_class']));
+ b = a.append( $("<div class='historyItemTitle'><a href='/file/" + doc['id'] + "'>" + doc['name'] + "</a></div>") );
+ if ('misc_blurb' in doc) {
+ b.append( $("<div>").append(doc["misc_blurb"]) );
+ }
+ if ('peek' in doc) {
+ b.append( $("<pre class='peek'>").append( doc["peek"]) );
+ }
+ p.append(b);
+ });
+ }
+ });
+ }
+ };
+
+
+ var queryURL = function (query) {
+ var url = "/api/search" + encodeURIComponent(query);
+ url = url + "&field=" + $("#searchFields").val();
+ if ($("#fileType").val() != "All") {
+ url = url + "&type=" + $("#fileType").val()
+ }
+ return url;
+ }
+
+ $(document).ready( function() {
+ $("#search_button").click(function() {
+ doSearch($("#search_text").val());
+ });
+ $('#search_text').keyup(function(e){
+ if(e.keyCode == 13) {
+ doSearch($("#search_text").val());
+ }
+ });
+ doSearch($("#search_text").val());
+ });
+ </script></%def>
+
<%def name="center_panel()">
-${search_init()}
-<script type="text/javascript">
-var search_format_output = function(doc) {
- var div_class = "historyItem";
- var a = $("<div class='" + div_class + "'>")
- a.append($("<div>").append(doc['model_class']));
- b = a.append( $("<div class='historyItemTitle'><a href='/file/" + doc['id'] + "'>" + doc['name'] + "</a></div>") );
- if ('misc_blurb' in doc) {
- b.append( $("<div>").append(doc["misc_blurb"]) );
- }
- if ('peek' in doc) {
- b.append( $("<pre class='peek'>").append( doc["peek"]) );
- }
- return a;
-}
+ <div id="search_box" style="margin: 20px;">
+ <input type="text" id="search_text" size="90"/>
+ </div>
+ <div style="margin: 20px;">
+ <input type="button" id="search_button" value="Search"/>
+ </div>
+ <div id="output"></div>
-</script>
-<div style="overflow: auto; height: 100%">
-${search_dialog("search_format_output")}
-</div></%def>
-
-
diff -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 -r 3e7adbbe91a06d30a96e7a7101707e040376aba1 templates/webapps/galaxy/cloud/run.mako
--- a/templates/webapps/galaxy/cloud/run.mako
+++ /dev/null
@@ -1,41 +0,0 @@
-<%inherit file="/webapps/galaxy/base_panels.mako"/>
-
-<%def name="init()">
-<%
- self.has_left_panel=False
- self.has_right_panel=False
- self.active_view="shared"
- self.message_box_visible=False
-%>
-</%def>
-
-
-<%def name="center_panel()">
- <div style="overflow: auto; height: 100%;">
- <div class="page-container" style="padding: 10px;">
- <h2>Launching a Galaxy Cloud Instance</h2>
-%if error:
- <p>${error}</p>
-%elif instance:
- %if kp_material:
- <h3>Very Important Key Pair Information</h3>
- <p>A new key pair named '${kp_name}' has been created in your AWS
- account and will be used to access this instance via ssh. It is
- <strong>very important</strong> that you save the following private key
- as it is not saved on this Galaxy instance and will be permanently lost
- once you leave this page. To do this, save the following key block as
- a plain text file named '${kp_name}'.</p>
- <pre>${kp_material}</pre>
- %endif
- <p>The instance '${instance.id} has been successfully launched using the
- '${instance.image_id}' AMI.<br/> Access it at <a
- href="http://${instance.public_dns_name}">http://${instance.public_dns_name}</a></p>
- <p>SSH access is available using your private key '${kp_name}'.</p>
-%else:
- <p> Unknown failure, no instance. Please refer to your AWS console at <a
- href="https://console.aws.amazon.com">https://console.aws.amazon.com</a></p>
-%endif
- </div>
- </div>
-</%def>
-
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
09 Dec '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/511fbb67b820/
Changeset: 511fbb67b820
User: jmchilton
Date: 2014-12-09 14:17:25+00:00
Summary: Merge next-stable.
Affected #: 44 files
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e .hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -20,4 +20,4 @@
ca45b78adb4152fc6e7395514d46eba6b7d0b838 release_2014.08.11
548ab24667d6206780237bd807f7d857a484c461 latest_2014.08.11
2092948937ac30ef82f71463a235c66d34987088 release_2014.10.06
-acb2548443ae42d39ef200d035ccc0481d6b930c latest_2014.10.06
+782cf1a1f6b56f8a9c0b3e5e9ffd29fd93b16ce3 latest_2014.10.06
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e lib/galaxy/security/validate_user_input.py
--- a/lib/galaxy/security/validate_user_input.py
+++ b/lib/galaxy/security/validate_user_input.py
@@ -1,3 +1,9 @@
+"""
+Utilities for validating inputs related to user objects.
+
+The validate_* methods in this file return simple messages that do not contain
+user inputs - so these methods do not need to be escaped.
+"""
import logging
import re
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -67,6 +67,7 @@
from galaxy.util.template import fill_template
from galaxy.web import url_for
from galaxy.web.form_builder import SelectField
+from galaxy.web.framework.helpers import escape
from galaxy.model.item_attrs import Dictifiable
from galaxy.model import Workflow
from tool_shed.util import common_util
@@ -791,7 +792,7 @@
success = True
# Make sure the tool is actually loaded.
if tool_id not in self.tools_by_id:
- return None, False, "No tool with id %s" % tool_id
+ return None, False, "No tool with id %s" % escape( tool_id )
else:
tool = self.tools_by_id[ tool_id ]
tarball_files = []
@@ -902,7 +903,7 @@
replace the old tool.
"""
if tool_id not in self.tools_by_id:
- message = "No tool with id %s" % tool_id
+ message = "No tool with id %s" % escape( tool_id )
status = 'error'
else:
old_tool = self.tools_by_id[ tool_id ]
@@ -939,7 +940,7 @@
Attempt to remove the tool identified by 'tool_id'.
"""
if tool_id not in self.tools_by_id:
- message = "No tool with id %s" % tool_id
+ message = "No tool with id %s" % escape( tool_id )
status = 'error'
else:
tool = self.tools_by_id[ tool_id ]
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e lib/galaxy/tools/filters/__init__.py
--- a/lib/galaxy/tools/filters/__init__.py
+++ b/lib/galaxy/tools/filters/__init__.py
@@ -1,6 +1,10 @@
+import logging
from galaxy.util import listify
from copy import deepcopy
+log = logging.getLogger( __name__ )
+
+
class FilterFactory( object ):
"""
An instance of this class is responsible for filtering the list
@@ -37,17 +41,21 @@
elif name == 'toolbox_label_filters':
category = "label"
if category:
- self.__init_filters( category, user_filters, filters )
+ validate = getattr( trans.app.config, 'user_%s_filters' % category, [] )
+ self.__init_filters( category, user_filters, filters, validate=validate )
else:
if kwds.get( "trackster", False ):
filters[ "tool" ].append( _has_trackster_conf )
return filters
- def __init_filters( self, key, filters, toolbox_filters ):
+ def __init_filters( self, key, filters, toolbox_filters, validate=None ):
for filter in filters:
- filter_function = self.__build_filter_function( filter )
- toolbox_filters[ key ].append( filter_function )
+ if validate is None or filter in validate or filter in self.default_filters:
+ filter_function = self.__build_filter_function( filter )
+ toolbox_filters[ key ].append( filter_function )
+ else:
+ log.warning( "Refusing to load %s filter '%s' which is not defined in config", key, filter )
return toolbox_filters
def __build_filter_function( self, filter_name ):
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -1664,7 +1664,8 @@
stored.user = trans.user
stored.published = publish
if data[ 'annotation' ]:
- self.add_item_annotation( trans.sa_session, stored.user, stored, data[ 'annotation' ] )
+ annotation = sanitize_html( data[ 'annotation' ], 'utf-8', 'text/html' )
+ self.add_item_annotation( trans.sa_session, stored.user, stored, annotation )
# Persist
trans.sa_session.add( stored )
@@ -2571,6 +2572,8 @@
def set_public_username( self, trans, id, username, **kwargs ):
""" Set user's public username and delegate to sharing() """
user = trans.get_user()
+ # message from validate_publicname does not contain input, no need
+ # to escape.
message = validate_publicname( trans, username, user )
if message:
return trans.fill_template( '/sharing_base.mako', item=self.get_item( trans, id ), message=message, status='error' )
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e lib/galaxy/web/form_builder.py
--- a/lib/galaxy/web/form_builder.py
+++ b/lib/galaxy/web/form_builder.py
@@ -563,7 +563,7 @@
html += '<input name="__switch_default__" type="hidden" value="%s" />' % self.default_field
options = []
for name, delegate_field in self.delegate_fields.items():
- field = dumps( delegate_field.to_dict() )
+ field = escape( dumps( delegate_field.to_dict() ) )
option = " '%s': %s" % ( name, field )
options.append( option )
html += '<script>$(document).ready( function() {\nvar switchOptions = {\n'
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e lib/galaxy/webapps/galaxy/controllers/history.py
--- a/lib/galaxy/webapps/galaxy/controllers/history.py
+++ b/lib/galaxy/webapps/galaxy/controllers/history.py
@@ -718,7 +718,9 @@
for husa in husas:
trans.sa_session.delete( husa )
if not deleted_sharing_relation:
- message = "History '%s' does not seem to be shared with user '%s'" % ( history.name, user.email )
+ history_name = escape( history.name )
+ user_email = escape( user.email )
+ message = "History '%s' does not seem to be shared with user '%s'" % ( history_name, user_email )
return trans.fill_template( '/sharing_base.mako', item=history,
message=message, status='error' )
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e lib/galaxy/webapps/galaxy/controllers/mobile.py
--- a/lib/galaxy/webapps/galaxy/controllers/mobile.py
+++ b/lib/galaxy/webapps/galaxy/controllers/mobile.py
@@ -1,60 +1,71 @@
+from galaxy import web
from galaxy.web.base.controller import *
+
class Mobile( BaseUIController ):
+
@web.expose
def index( self, trans, **kwargs ):
- return trans.fill_template( "mobile/index.mako" )
+ return trans.response.send_redirect( web.url_for(controller='root', action='index' ) )
+ # return trans.fill_template( "mobile/index.mako" )
@web.expose
def history_list( self, trans ):
- return trans.fill_template( "mobile/history/list.mako" )
+ return trans.response.send_redirect( web.url_for(controller='root', action='index' ) )
+ # return trans.fill_template( "mobile/history/list.mako" )
@web.expose
def history_detail( self, trans, id ):
- history = trans.sa_session.query( trans.app.model.History ).get( id )
- assert history.user == trans.user
- return trans.fill_template( "mobile/history/detail.mako", history=history )
+ return trans.response.send_redirect( web.url_for(controller='root', action='index' ) )
+ # history = trans.sa_session.query( trans.app.model.History ).get( id )
+ # assert history.user == trans.user
+ # return trans.fill_template( "mobile/history/detail.mako", history=history )
@web.expose
def dataset_detail( self, trans, id ):
- dataset = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( id )
- assert dataset.history.user == trans.user
- return trans.fill_template( "mobile/dataset/detail.mako", dataset=dataset )
+ return trans.response.send_redirect( web.url_for(controller='root', action='index' ) )
+ # dataset = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( id )
+ # assert dataset.history.user == trans.user
+ # return trans.fill_template( "mobile/dataset/detail.mako", dataset=dataset )
@web.expose
def dataset_peek( self, trans, id ):
- dataset = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( id )
- assert dataset.history.user == trans.user
- return trans.fill_template( "mobile/dataset/peek.mako", dataset=dataset )
+ return trans.response.send_redirect( web.url_for(controller='root', action='index' ) )
+ # dataset = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( id )
+ # assert dataset.history.user == trans.user
+ # return trans.fill_template( "mobile/dataset/peek.mako", dataset=dataset )
@web.expose
def settings( self, trans, email=None, password=None ):
- message = None
- if email is not None and password is not None:
- if email == "":
- self.__logout( trans )
- message = "Logged out"
- else:
- error = self.__login( trans, email, password )
- message = error or "Login changed"
- return trans.fill_template( "mobile/settings.mako", message=message )
+ return trans.response.send_redirect( web.url_for(controller='root', action='index' ) )
+ # message = None
+ # if email is not None and password is not None:
+ # if email == "":
+ # self.__logout( trans )
+ # message = "Logged out"
+ # else:
+ # error = self.__login( trans, email, password )
+ # message = error or "Login changed"
+ # return trans.fill_template( "mobile/settings.mako", message=message )
def __logout( self, trans ):
- trans.log_event( "User logged out" )
- trans.handle_user_logout()
+ return trans.response.send_redirect( web.url_for(controller='root', action='index' ) )
+ # trans.log_event( "User logged out" )
+ # trans.handle_user_logout()
def __login( self, trans, email="", password="" ):
- error = password_error = None
- user = trans.sa_session.query( model.User ).filter_by( email = email ).first()
- if not user:
- error = "No such user (please note that login is case sensitive)"
- elif user.deleted:
- error = "This account has been marked deleted, contact your Galaxy administrator to restore the account."
- elif user.external:
- error = "This account was created for use with an external authentication method, contact your local Galaxy administrator to activate it."
- elif not user.check_password( password ):
- error = "Invalid password"
- else:
- trans.handle_user_login( user )
- trans.log_event( "User logged in" )
- return error
+ return trans.response.send_redirect( web.url_for(controller='root', action='index' ) )
+ # error = password_error = None
+ # user = trans.sa_session.query( model.User ).filter_by( email = email ).first()
+ # if not user:
+ # error = "No such user (please note that login is case sensitive)"
+ # elif user.deleted:
+ # error = "This account has been marked deleted, contact your Galaxy administrator to restore the account."
+ # elif user.external:
+ # error = "This account was created for use with an external authentication method, contact your local Galaxy administrator to activate it."
+ # elif not user.check_password( password ):
+ # error = "Invalid password"
+ # else:
+ # trans.handle_user_login( user )
+ # trans.log_event( "User logged in" )
+ # return error
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e lib/galaxy/webapps/galaxy/controllers/page.py
--- a/lib/galaxy/webapps/galaxy/controllers/page.py
+++ b/lib/galaxy/webapps/galaxy/controllers/page.py
@@ -500,14 +500,14 @@
.first()
if not other:
mtype = "error"
- msg = ( "User '%s' does not exist" % email )
+ msg = ( "User '%s' does not exist" % escape( email ) )
elif other == trans.get_user():
mtype = "error"
msg = ( "You cannot share a page with yourself" )
elif trans.sa_session.query( model.PageUserShareAssociation ) \
.filter_by( user=other, page=page ).count() > 0:
mtype = "error"
- msg = ( "Page already shared with '%s'" % email )
+ msg = ( "Page already shared with '%s'" % escape( email ) )
else:
share = model.PageUserShareAssociation()
share.page = page
@@ -516,7 +516,9 @@
session.add( share )
self.create_item_slug( session, page )
session.flush()
- trans.set_message( "Page '%s' shared with user '%s'" % ( page.title, other.email ) )
+ page_title = escape( page.title )
+ other_email = escape( other.email )
+ trans.set_message( "Page '%s' shared with user '%s'" % ( page_title, other_email ) )
return trans.response.send_redirect( url_for( controller='page', action='sharing', id=id ) )
return trans.fill_template( "/ind_share_base.mako",
message = msg,
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e lib/galaxy/webapps/galaxy/controllers/visualization.py
--- a/lib/galaxy/webapps/galaxy/controllers/visualization.py
+++ b/lib/galaxy/webapps/galaxy/controllers/visualization.py
@@ -535,14 +535,14 @@
.first()
if not other:
mtype = "error"
- msg = ( "User '%s' does not exist" % email )
+ msg = ( "User '%s' does not exist" % escape( email ) )
elif other == trans.get_user():
mtype = "error"
msg = ( "You cannot share a visualization with yourself" )
elif trans.sa_session.query( model.VisualizationUserShareAssociation ) \
.filter_by( user=other, visualization=visualization ).count() > 0:
mtype = "error"
- msg = ( "Visualization already shared with '%s'" % email )
+ msg = ( "Visualization already shared with '%s'" % escape( email ) )
else:
share = model.VisualizationUserShareAssociation()
share.visualization = visualization
@@ -551,7 +551,9 @@
session.add( share )
self.create_item_slug( session, visualization )
session.flush()
- trans.set_message( "Visualization '%s' shared with user '%s'" % ( visualization.title, other.email ) )
+ viz_title = escape( visualization.title )
+ other_email = escape( other.email )
+ trans.set_message( "Visualization '%s' shared with user '%s'" % ( viz_title, other_email ) )
return trans.response.send_redirect( web.url_for(controller='visualization', action='sharing', id=id ) )
return trans.fill_template( "/ind_share_base.mako",
message = msg,
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e lib/galaxy/webapps/galaxy/controllers/workflow.py
--- a/lib/galaxy/webapps/galaxy/controllers/workflow.py
+++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py
@@ -310,14 +310,14 @@
.first()
if not other:
mtype = "error"
- msg = ( "User '%s' does not exist" % email )
+ msg = ( "User '%s' does not exist" % escape( email ) )
elif other == trans.get_user():
mtype = "error"
msg = ( "You cannot share a workflow with yourself" )
elif trans.sa_session.query( model.StoredWorkflowUserShareAssociation ) \
.filter_by( user=other, stored_workflow=stored ).count() > 0:
mtype = "error"
- msg = ( "Workflow already shared with '%s'" % email )
+ msg = ( "Workflow already shared with '%s'" % escape( email ) )
else:
share = model.StoredWorkflowUserShareAssociation()
share.stored_workflow = stored
@@ -325,7 +325,7 @@
session = trans.sa_session
session.add( share )
session.flush()
- trans.set_message( "Workflow '%s' shared with user '%s'" % ( stored.name, other.email ) )
+ trans.set_message( "Workflow '%s' shared with user '%s'" % ( escape( stored.name ), escape( other.email ) ) )
return trans.response.send_redirect( url_for( controller='workflow', action='sharing', id=id ) )
return trans.fill_template( "/ind_share_base.mako",
message=msg,
@@ -415,7 +415,7 @@
stored.latest_workflow.name = san_new_name
trans.sa_session.flush()
# For current workflows grid:
- trans.set_message( "Workflow renamed to '%s'." % new_name )
+ trans.set_message( "Workflow renamed to '%s'." % san_new_name )
return self.list( trans )
# For new workflows grid:
#message = "Workflow renamed to '%s'." % new_name
@@ -535,7 +535,7 @@
session.add( new_stored )
session.flush()
# Display the management page
- trans.set_message( 'Created new workflow with name "%s"' % new_stored.name )
+ trans.set_message( 'Created new workflow with name "%s"' % escape( new_stored.name ) )
return self.list( trans )
@web.expose
@@ -582,7 +582,7 @@
trans.sa_session.add( stored )
trans.sa_session.flush()
# Display the management page
- trans.set_message( "Workflow '%s' deleted" % stored.name )
+ trans.set_message( "Workflow '%s' deleted" % escape( stored.name ) )
return self.list( trans )
@web.expose
@@ -1089,7 +1089,7 @@
message += "Imported, but this workflow contains cycles. "
status = "error"
else:
- message += "Workflow <b>%s</b> imported successfully. " % workflow.name
+ message += "Workflow <b>%s</b> imported successfully. " % escape( workflow.name )
if missing_tool_tups:
if trans.user_is_admin():
# A required tool is not available in the local Galaxy instance.
@@ -1103,7 +1103,7 @@
message += "You can likely install the required tools from one of the Galaxy tool sheds listed below.<br/>"
for missing_tool_tup in missing_tool_tups:
missing_tool_id, missing_tool_name, missing_tool_version = missing_tool_tup
- message += "<b>Tool name</b> %s, <b>id</b> %s, <b>version</b> %s<br/>" % ( missing_tool_name, missing_tool_id, missing_tool_version )
+ message += "<b>Tool name</b> %s, <b>id</b> %s, <b>version</b> %s<br/>" % ( escape( missing_tool_name ), escape( missing_tool_id ), escape( missing_tool_version ) )
message += "<br/>"
for shed_name, shed_url in trans.app.tool_shed_registry.tool_sheds.items():
if shed_url.endswith( '/' ):
@@ -1113,7 +1113,7 @@
url += '&tool_id='
for missing_tool_tup in missing_tool_tups:
missing_tool_id = missing_tool_tup[0]
- url += '%s,' % missing_tool_id
+ url += '%s,' % escape( missing_tool_id )
message += '<a href="%s">%s</a><br/>' % ( url, shed_name )
status = 'error'
if installed_repository_file or tool_shed_url:
@@ -1133,13 +1133,13 @@
pass
if tool_shed_url:
# We've received the textual representation of a workflow from a Galaxy tool shed.
- message = "Workflow <b>%s</b> imported successfully." % workflow.name
+ message = "Workflow <b>%s</b> imported successfully." % escape( workflow.name )
url = '%s/workflow/view_workflow?repository_metadata_id=%s&workflow_name=%s&message=%s' % \
( tool_shed_url, repository_metadata_id, encoding_util.tool_shed_encode( workflow_name ), message )
return trans.response.send_redirect( url )
elif installed_repository_file:
# The workflow was read from a file included with an installed tool shed repository.
- message = "Workflow <b>%s</b> imported successfully." % workflow.name
+ message = "Workflow <b>%s</b> imported successfully." % escape( workflow.name )
if cntrller == 'api':
return status, message
return trans.response.send_redirect( web.url_for( controller='admin_toolshed',
@@ -1184,7 +1184,7 @@
# Index page with message
workflow_id = trans.security.encode_id( stored_workflow.id )
return trans.show_message( 'Workflow "%s" created from current history. You can <a href="%s" target="_parent">edit</a> or <a href="%s">run</a> the workflow.' %
- ( workflow_name, url_for( controller='workflow', action='editor', id=workflow_id ),
+ ( escape( workflow_name ), url_for( controller='workflow', action='editor', id=workflow_id ),
url_for( controller='workflow', action='run', id=workflow_id ) ) )
@web.expose
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e templates/embed_base.mako
--- a/templates/embed_base.mako
+++ b/templates/embed_base.mako
@@ -51,7 +51,7 @@
</div><h4><a class="toggle-embed" href="${display_href}" title="Show or hide ${item_display_name} content">Galaxy ${get_class_display_name( item.__class__ )} | ${get_item_name( item ) | h}</a></h4>
%if hasattr( item, "annotation") and item.annotation:
- <div class="annotation">${item.annotation}</div>
+ <div class="annotation">${item.annotation | h}</div>
%endif
## Use a hidden var to store the ajax URL for getting an item's content.
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e templates/ind_share_base.mako
--- a/templates/ind_share_base.mako
+++ b/templates/ind_share_base.mako
@@ -91,7 +91,7 @@
Email address of user to share with
</label><div style="float: left; width: 250px; margin-right: 10px;">
- <input type="text" name="email" value="${email}" size="40">
+ <input type="text" name="email" value="${email | h}" size="40"></div><div style="clear: both"></div></div>
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e templates/user/dbkeys.mako
--- a/templates/user/dbkeys.mako
+++ b/templates/user/dbkeys.mako
@@ -148,7 +148,7 @@
Processing
% endif
</td>
- <td><form action="dbkeys" method="post"><input type="hidden" name="key" value="${key}" /><input type="submit" name="delete" value="Delete" /></form></td>
+ <td><form action="dbkeys" method="post"><input type="hidden" name="key" value="${key | h}" /><input type="submit" name="delete" value="Delete" /></form></td></tr>
% endfor
</table>
@@ -194,7 +194,7 @@
<div style="clear: both; padding-bottom: 0.5em"></div><select id="fasta_input" name="dataset_id">
%for dataset in fasta_hdas:
- <option value="${trans.security.encode_id( dataset.id )}">${dataset.hid}: ${dataset.name}</option>
+ <option value="${trans.security.encode_id( dataset.id )}">${dataset.hid | h}: ${dataset.name | h}</option>
%endfor
</select><input type="file" id="len_file_input" name="len_file" /></input>
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e templates/user/openid_associate.mako
--- a/templates/user/openid_associate.mako
+++ b/templates/user/openid_associate.mako
@@ -48,13 +48,13 @@
The following OpenIDs will be associated with the account chosen or created below.
<ul>
%for openid in openids:
- <li>${openid.openid}</li>
+ <li>${openid.openid | h}</li>
%endfor
</ul></div>
%else:
<div>
- The OpenID <strong>${openids[0].openid}</strong> will be associated with the account chosen or created.
+ The OpenID <strong>${openids[0].openid | h}</strong> will be associated with the account chosen or created.
</div>
%endif
<br/>
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e templates/user/toolbox_filters.mako
--- a/templates/user/toolbox_filters.mako
+++ b/templates/user/toolbox_filters.mako
@@ -15,7 +15,7 @@
%if tool_filters or section_filters or label_filters:
<div class="toolForm">
- <form name="toolbox_filter" id="toolbox_filter" action="${h.url_for( controller='user', action='edit_toolbox_filters', cntrller=cntrller, user_id=trans.security.encode_id( user.id ) )}" method="post" >
+ <form name="toolbox_filter" id="toolbox_filter" action="${h.url_for( controller='user', action='edit_toolbox_filters', cntrller=cntrller )}" method="post" >
% if tool_filters:
<div class="toolFormTitle">Edit ToolBox filters :: Tools</div><div class="toolFormBody">
@@ -87,5 +87,5 @@
</form></div>
%else:
- ${render_msg( 'No filter available. Contact you system administrator or check your configuration file.', 'info' )}
+ ${render_msg( 'No filters available. Contact your system administrator or check your configuration file.', 'info' )}
%endif
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e templates/user/username.mako
--- a/templates/user/username.mako
+++ b/templates/user/username.mako
@@ -1,4 +1,9 @@
<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+%if message:
+ ${render_msg( message, status )}
+%endif
<% is_admin = cntrller == 'admin' and trans.user_is_admin() %>
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e templates/webapps/galaxy/root/tool_menu.mako
--- a/templates/webapps/galaxy/root/tool_menu.mako
+++ b/templates/webapps/galaxy/root/tool_menu.mako
@@ -82,7 +82,7 @@
%if t.user.stored_workflow_menu_entries:
%for m in t.user.stored_workflow_menu_entries:
<div class="toolTitle">
- <a href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id(m.stored_workflow_id) )}" target="galaxy_main">${ util.unicodify( m.stored_workflow.name ) }</a>
+ <a href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id(m.stored_workflow_id) )}" target="galaxy_main">${ util.unicodify( m.stored_workflow.name ) | h}</a></div>
%endfor
%endif
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e templates/webapps/galaxy/tool_executed.mako
--- a/templates/webapps/galaxy/tool_executed.mako
+++ b/templates/webapps/galaxy/tool_executed.mako
@@ -61,7 +61,7 @@
${jobs_str} been successfully added to the queue - resulting in the following ${datasets_str}:
</p>
%for _, data in out_data:
- <div style="padding: 10px"><b> ${data.hid}: ${data.name}</b></div>
+ <div style="padding: 10px"><b> ${data.hid}: ${data.name | h}</b></div>
%endfor
<p>
@@ -83,7 +83,7 @@
<ul><!-- Styling on this list is a little flat. Consider identing these error messages. -->
%for job_error in job_errors:
- <li><b>${job_error}</b></li>
+ <li><b>${job_error | h}</b></li>
%endfor
</ul></div>
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e 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>
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e templates/webapps/galaxy/workflow/configure_menu.mako
--- a/templates/webapps/galaxy/workflow/configure_menu.mako
+++ b/templates/webapps/galaxy/workflow/configure_menu.mako
@@ -1,4 +1,5 @@
<%inherit file="/webapps/galaxy/base_panels.mako"/>
+<%page expression_filter="h"/><%def name="init()"><%
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e templates/webapps/galaxy/workflow/display.mako
--- a/templates/webapps/galaxy/workflow/display.mako
+++ b/templates/webapps/galaxy/workflow/display.mako
@@ -40,7 +40,7 @@
<%def name="row_for_param( param, value, other_values, prefix, step )"><% cls = "form-row" %><div class="${cls}">
- <label>${param.get_label()}</label>
+ <label>${param.get_label() | h}</label><div>
%if isinstance( param, DataToolParameter ) or isinstance( param, DataCollectionToolParameter ):
%if ( prefix + param.name ) in step.input_connections_by_name:
@@ -93,19 +93,19 @@
%><div class="toolForm">
%if tool:
- <div class="toolFormTitle">Step ${int(step.order_index)+1}: ${tool.name}</div>
+ <div class="toolFormTitle">Step ${int(step.order_index)+1}: ${tool.name | h}</div><div class="toolFormBody">
${do_inputs( tool.inputs, step.state.inputs, "", step )}
</div>
%else:
- <div class="toolFormTitle">Step ${int(step.order_index)+1}: Unknown Tool with id '${step.tool_id}'</div>
+ <div class="toolFormTitle">Step ${int(step.order_index)+1}: Unknown Tool with id '${step.tool_id | h}'</div>
%endif
</div>
%else:
## TODO: always input dataset?
<% module = step.module %><div class="toolForm">
- <div class="toolFormTitle">Step ${int(step.order_index)+1}: ${module.name}</div>
+ <div class="toolFormTitle">Step ${int(step.order_index)+1}: ${module.name | h}</div><div class="toolFormBody">
${do_inputs( module.get_runtime_inputs(), step.state.inputs, "", step )}
</div>
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e templates/webapps/galaxy/workflow/list.mako
--- a/templates/webapps/galaxy/workflow/list.mako
+++ b/templates/webapps/galaxy/workflow/list.mako
@@ -94,7 +94,7 @@
<% workflow = association.stored_workflow %><tr><td>
- <a class="menubutton" id="shared-${i}-popup" href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id(workflow.id) )}">${h.to_unicode( workflow.name )}</a>
+ <a class="menubutton" id="shared-${i}-popup" href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id(workflow.id) )}">${h.to_unicode( workflow.name ) | h}</a></td><td>${workflow.user.email}</td><td>${len(workflow.latest_workflow.steps)}</td>
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e templates/webapps/galaxy/workflow/list_for_run.mako
--- a/templates/webapps/galaxy/workflow/list_for_run.mako
+++ b/templates/webapps/galaxy/workflow/list_for_run.mako
@@ -23,7 +23,7 @@
%for i, workflow in enumerate( workflows ):
<tr><td>
- <a href="${h.url_for(controller='workflow', action='run', id=trans.security.encode_id(workflow.id) )}">${h.to_unicode( workflow.name )}</a>
+ <a href="${h.url_for(controller='workflow', action='run', id=trans.security.encode_id(workflow.id) )}">${h.to_unicode( workflow.name ) | h}</a><a id="wf-${i}-popup" class="popup-arrow" style="display: none;">▼</a></td><td>${len(workflow.latest_workflow.steps)}</td>
@@ -51,10 +51,10 @@
<% workflow = association.stored_workflow %><tr><td>
- <a href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id(workflow.id) )}">${workflow.name}</a>
+ <a href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id(workflow.id) )}">${workflow.name | h}</a><a id="shared-${i}-popup" class="popup-arrow" style="display: none;">▼</a></td>
- <td>${workflow.user.email}</td>
+ <td>${workflow.user.email | h}</td><td>${len(workflow.latest_workflow.steps)}</td></tr>
%endfor
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e templates/webapps/galaxy/workflow/missing_tools.mako
--- a/templates/webapps/galaxy/workflow/missing_tools.mako
+++ b/templates/webapps/galaxy/workflow/missing_tools.mako
@@ -1,6 +1,6 @@
<%inherit file="/base.mako"/>
-<h2>Cannot run workflow "${h.to_unicode( workflow.name )}"</h2>
+<h2>Cannot run workflow "${h.to_unicode( workflow.name ) | h}"</h2>
%if workflow.annotation:
<div class="workflow-annotation">${workflow.annotation}</div>
@@ -11,7 +11,7 @@
<strong>This workflow utilizes tools which are unavailable, and cannot be run. Enable the tools listed below, or <a href="${h.url_for(controller='workflow', action='editor', id=trans.security.encode_id(workflow.id) )}" target="_parent">edit the workflow</a> to correct these errors.</strong><br/><ul>
%for i, tool in enumerate( missing_tools ):
- <li>${tool}</li>
+ <li>${tool | h}</li>
%endfor
</ul></div>
\ No newline at end of file
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e templates/webapps/galaxy/workflow/myexp_export.mako
--- a/templates/webapps/galaxy/workflow/myexp_export.mako
+++ b/templates/webapps/galaxy/workflow/myexp_export.mako
@@ -9,7 +9,7 @@
## Generate request.
<?xml version="1.0"?><workflow>
- <title>${workflow_name}</title>
+ <title>${workflow_name | h}</title><description>${workflow_description}</description><type>Galaxy</type><content encoding="base64" type="binary">
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e templates/webapps/galaxy/workflow/rename.mako
--- a/templates/webapps/galaxy/workflow/rename.mako
+++ b/templates/webapps/galaxy/workflow/rename.mako
@@ -15,7 +15,7 @@
%endif
<div class="toolForm">
- <div class="toolFormTitle">Rename workflow '${stored.name}'</div>
+ <div class="toolFormTitle">Rename workflow '${stored.name | h}'</div><div class="toolFormBody"><form action="${h.url_for(controller='workflow', action='rename', id=trans.security.encode_id(stored.id) )}" method="POST"><div class="form-row">
@@ -23,7 +23,7 @@
New name
</label><div style="float: left; width: 250px; margin-right: 10px;">
- <input type="text" name="new_name" value="${stored.name}" size="40">
+ <input type="text" name="new_name" value="${stored.name | h}" size="40"></div><div style="clear: both"></div></div>
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e templates/webapps/galaxy/workflow/run.mako
--- a/templates/webapps/galaxy/workflow/run.mako
+++ b/templates/webapps/galaxy/workflow/run.mako
@@ -372,7 +372,7 @@
<% cls = "form-row" %>
%endif
<div class="${cls}">
- <label>${param.get_label()}</label>
+ <label>${param.get_label() | h}</label><div>
%if isinstance( param, DataToolParameter ) or isinstance( param, DataCollectionToolParameter ):
%if ( prefix + param.name ) in step.input_connections_by_name:
@@ -444,7 +444,7 @@
%else:
<span class="workflow_parameters"><span class="uneditable_field">
- ${param.value_to_display_text( value, app )}
+ ${param.value_to_display_text( value, app ) | h}
</span><span class="editable_field"><span class="editable">
@@ -474,7 +474,7 @@
<span class="action-button" id="hide_all_tool_body">Collapse</span></div>
-<h2>Running workflow "${h.to_unicode( workflow.name )}"</h2>
+<h2>Running workflow "${h.to_unicode( workflow.name ) | h}"</h2>
%if has_upgrade_messages:
<div class="warningmessage">
@@ -574,6 +574,7 @@
<%
pja_ss_all = []
for pja_ss in [ActionBox.get_short_str(pja) for pja in step.post_job_actions]:
+ pja_ss = h.escape( pja_ss )
for rematch in re.findall('\$\{.+?\}', pja_ss):
pja_ss = pja_ss.replace(rematch, '<span style="background-color:%s" class="wfpspan wf_parm__%s pja_wfp">%s</span>' % (wf_parms[rematch[2:-1]], rematch[2:-1], rematch[2:-1]))
pja_ss_all.append(pja_ss)
@@ -586,7 +587,7 @@
%else:
<div class="toolForm"><div class="toolFormTitle">
- <span class='title_ul_text'>Step ${int(step.order_index)+1}: ${module.name}</span>
+ <span class='title_ul_text'>Step ${int(step.order_index)+1}: ${module.name | h}</span>
% if step.annotations:
<div class="step-annotation">${step.annotations[0].annotation}</div>
% endif
diff -r 8d8bce91a289e040d47f9b757aae4051a0501743 -r 511fbb67b820735128ee02e526fb6873beaa961e templates/webapps/galaxy/workflow/run_complete.mako
--- a/templates/webapps/galaxy/workflow/run_complete.mako
+++ b/templates/webapps/galaxy/workflow/run_complete.mako
@@ -1,4 +1,5 @@
<%inherit file="/base.mako"/>
+<%page expression_filter="h"/><div class="donemessagelarge">
%if scheduled:
@@ -7,7 +8,7 @@
<div class="workflow-invocation-complete">
%if invocation['new_history']:
<p>These datasets will appear in a new history:
- <a target='galaxy_history' href="${h.url_for( controller='history', action='list', operation="Switch", id=trans.security.encode_id(invocation['new_history'].id), use_panels=False, show_deleted=False )}">
+ <a target='galaxy_history' href="${h.url_for( controller='history', action='list', operation="Switch", id=trans.security.encode_id(invocation['new_history'].id), use_panels=False, show_deleted=False ) | n}">
'${h.to_unicode(invocation['new_history'].name)}'.
</a></p>
%endif
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
09 Dec '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/ac30ccd5cd1e/
Changeset: ac30ccd5cd1e
Branch: next-stable
User: jmchilton
Date: 2014-12-09 14:14:54+00:00
Summary: Merge stable.
Affected #: 42 files
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac .hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -20,4 +20,4 @@
ca45b78adb4152fc6e7395514d46eba6b7d0b838 release_2014.08.11
548ab24667d6206780237bd807f7d857a484c461 latest_2014.08.11
2092948937ac30ef82f71463a235c66d34987088 release_2014.10.06
-acb2548443ae42d39ef200d035ccc0481d6b930c latest_2014.10.06
+782cf1a1f6b56f8a9c0b3e5e9ffd29fd93b16ce3 latest_2014.10.06
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac lib/galaxy/security/validate_user_input.py
--- a/lib/galaxy/security/validate_user_input.py
+++ b/lib/galaxy/security/validate_user_input.py
@@ -1,3 +1,9 @@
+"""
+Utilities for validating inputs related to user objects.
+
+The validate_* methods in this file return simple messages that do not contain
+user inputs - so these methods do not need to be escaped.
+"""
import logging
import re
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac lib/galaxy/tools/__init__.py
--- a/lib/galaxy/tools/__init__.py
+++ b/lib/galaxy/tools/__init__.py
@@ -67,6 +67,7 @@
from galaxy.util.template import fill_template
from galaxy.web import url_for
from galaxy.web.form_builder import SelectField
+from galaxy.web.framework.helpers import escape
from galaxy.model.item_attrs import Dictifiable
from galaxy.model import Workflow
from tool_shed.util import common_util
@@ -791,7 +792,7 @@
success = True
# Make sure the tool is actually loaded.
if tool_id not in self.tools_by_id:
- return None, False, "No tool with id %s" % tool_id
+ return None, False, "No tool with id %s" % escape( tool_id )
else:
tool = self.tools_by_id[ tool_id ]
tarball_files = []
@@ -902,7 +903,7 @@
replace the old tool.
"""
if tool_id not in self.tools_by_id:
- message = "No tool with id %s" % tool_id
+ message = "No tool with id %s" % escape( tool_id )
status = 'error'
else:
old_tool = self.tools_by_id[ tool_id ]
@@ -939,7 +940,7 @@
Attempt to remove the tool identified by 'tool_id'.
"""
if tool_id not in self.tools_by_id:
- message = "No tool with id %s" % tool_id
+ message = "No tool with id %s" % escape( tool_id )
status = 'error'
else:
tool = self.tools_by_id[ tool_id ]
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac lib/galaxy/tools/filters/__init__.py
--- a/lib/galaxy/tools/filters/__init__.py
+++ b/lib/galaxy/tools/filters/__init__.py
@@ -1,6 +1,10 @@
+import logging
from galaxy.util import listify
from copy import deepcopy
+log = logging.getLogger( __name__ )
+
+
class FilterFactory( object ):
"""
An instance of this class is responsible for filtering the list
@@ -37,17 +41,21 @@
elif name == 'toolbox_label_filters':
category = "label"
if category:
- self.__init_filters( category, user_filters, filters )
+ validate = getattr( trans.app.config, 'user_%s_filters' % category, [] )
+ self.__init_filters( category, user_filters, filters, validate=validate )
else:
if kwds.get( "trackster", False ):
filters[ "tool" ].append( _has_trackster_conf )
return filters
- def __init_filters( self, key, filters, toolbox_filters ):
+ def __init_filters( self, key, filters, toolbox_filters, validate=None ):
for filter in filters:
- filter_function = self.__build_filter_function( filter )
- toolbox_filters[ key ].append( filter_function )
+ if validate is None or filter in validate or filter in self.default_filters:
+ filter_function = self.__build_filter_function( filter )
+ toolbox_filters[ key ].append( filter_function )
+ else:
+ log.warning( "Refusing to load %s filter '%s' which is not defined in config", key, filter )
return toolbox_filters
def __build_filter_function( self, filter_name ):
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac lib/galaxy/web/base/controller.py
--- a/lib/galaxy/web/base/controller.py
+++ b/lib/galaxy/web/base/controller.py
@@ -1664,7 +1664,8 @@
stored.user = trans.user
stored.published = publish
if data[ 'annotation' ]:
- self.add_item_annotation( trans.sa_session, stored.user, stored, data[ 'annotation' ] )
+ annotation = sanitize_html( data[ 'annotation' ], 'utf-8', 'text/html' )
+ self.add_item_annotation( trans.sa_session, stored.user, stored, annotation )
# Persist
trans.sa_session.add( stored )
@@ -2571,6 +2572,8 @@
def set_public_username( self, trans, id, username, **kwargs ):
""" Set user's public username and delegate to sharing() """
user = trans.get_user()
+ # message from validate_publicname does not contain input, no need
+ # to escape.
message = validate_publicname( trans, username, user )
if message:
return trans.fill_template( '/sharing_base.mako', item=self.get_item( trans, id ), message=message, status='error' )
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac lib/galaxy/web/form_builder.py
--- a/lib/galaxy/web/form_builder.py
+++ b/lib/galaxy/web/form_builder.py
@@ -563,7 +563,7 @@
html += '<input name="__switch_default__" type="hidden" value="%s" />' % self.default_field
options = []
for name, delegate_field in self.delegate_fields.items():
- field = dumps( delegate_field.to_dict() )
+ field = escape( dumps( delegate_field.to_dict() ) )
option = " '%s': %s" % ( name, field )
options.append( option )
html += '<script>$(document).ready( function() {\nvar switchOptions = {\n'
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac lib/galaxy/webapps/galaxy/controllers/history.py
--- a/lib/galaxy/webapps/galaxy/controllers/history.py
+++ b/lib/galaxy/webapps/galaxy/controllers/history.py
@@ -718,7 +718,9 @@
for husa in husas:
trans.sa_session.delete( husa )
if not deleted_sharing_relation:
- message = "History '%s' does not seem to be shared with user '%s'" % ( history.name, user.email )
+ history_name = escape( history.name )
+ user_email = escape( user.email )
+ message = "History '%s' does not seem to be shared with user '%s'" % ( history_name, user_email )
return trans.fill_template( '/sharing_base.mako', item=history,
message=message, status='error' )
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac lib/galaxy/webapps/galaxy/controllers/mobile.py
--- a/lib/galaxy/webapps/galaxy/controllers/mobile.py
+++ b/lib/galaxy/webapps/galaxy/controllers/mobile.py
@@ -1,60 +1,71 @@
+from galaxy import web
from galaxy.web.base.controller import *
+
class Mobile( BaseUIController ):
+
@web.expose
def index( self, trans, **kwargs ):
- return trans.fill_template( "mobile/index.mako" )
+ return trans.response.send_redirect( web.url_for(controller='root', action='index' ) )
+ # return trans.fill_template( "mobile/index.mako" )
@web.expose
def history_list( self, trans ):
- return trans.fill_template( "mobile/history/list.mako" )
+ return trans.response.send_redirect( web.url_for(controller='root', action='index' ) )
+ # return trans.fill_template( "mobile/history/list.mako" )
@web.expose
def history_detail( self, trans, id ):
- history = trans.sa_session.query( trans.app.model.History ).get( id )
- assert history.user == trans.user
- return trans.fill_template( "mobile/history/detail.mako", history=history )
+ return trans.response.send_redirect( web.url_for(controller='root', action='index' ) )
+ # history = trans.sa_session.query( trans.app.model.History ).get( id )
+ # assert history.user == trans.user
+ # return trans.fill_template( "mobile/history/detail.mako", history=history )
@web.expose
def dataset_detail( self, trans, id ):
- dataset = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( id )
- assert dataset.history.user == trans.user
- return trans.fill_template( "mobile/dataset/detail.mako", dataset=dataset )
+ return trans.response.send_redirect( web.url_for(controller='root', action='index' ) )
+ # dataset = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( id )
+ # assert dataset.history.user == trans.user
+ # return trans.fill_template( "mobile/dataset/detail.mako", dataset=dataset )
@web.expose
def dataset_peek( self, trans, id ):
- dataset = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( id )
- assert dataset.history.user == trans.user
- return trans.fill_template( "mobile/dataset/peek.mako", dataset=dataset )
+ return trans.response.send_redirect( web.url_for(controller='root', action='index' ) )
+ # dataset = trans.sa_session.query( trans.app.model.HistoryDatasetAssociation ).get( id )
+ # assert dataset.history.user == trans.user
+ # return trans.fill_template( "mobile/dataset/peek.mako", dataset=dataset )
@web.expose
def settings( self, trans, email=None, password=None ):
- message = None
- if email is not None and password is not None:
- if email == "":
- self.__logout( trans )
- message = "Logged out"
- else:
- error = self.__login( trans, email, password )
- message = error or "Login changed"
- return trans.fill_template( "mobile/settings.mako", message=message )
+ return trans.response.send_redirect( web.url_for(controller='root', action='index' ) )
+ # message = None
+ # if email is not None and password is not None:
+ # if email == "":
+ # self.__logout( trans )
+ # message = "Logged out"
+ # else:
+ # error = self.__login( trans, email, password )
+ # message = error or "Login changed"
+ # return trans.fill_template( "mobile/settings.mako", message=message )
def __logout( self, trans ):
- trans.log_event( "User logged out" )
- trans.handle_user_logout()
+ return trans.response.send_redirect( web.url_for(controller='root', action='index' ) )
+ # trans.log_event( "User logged out" )
+ # trans.handle_user_logout()
def __login( self, trans, email="", password="" ):
- error = password_error = None
- user = trans.sa_session.query( model.User ).filter_by( email = email ).first()
- if not user:
- error = "No such user (please note that login is case sensitive)"
- elif user.deleted:
- error = "This account has been marked deleted, contact your Galaxy administrator to restore the account."
- elif user.external:
- error = "This account was created for use with an external authentication method, contact your local Galaxy administrator to activate it."
- elif not user.check_password( password ):
- error = "Invalid password"
- else:
- trans.handle_user_login( user )
- trans.log_event( "User logged in" )
- return error
+ return trans.response.send_redirect( web.url_for(controller='root', action='index' ) )
+ # error = password_error = None
+ # user = trans.sa_session.query( model.User ).filter_by( email = email ).first()
+ # if not user:
+ # error = "No such user (please note that login is case sensitive)"
+ # elif user.deleted:
+ # error = "This account has been marked deleted, contact your Galaxy administrator to restore the account."
+ # elif user.external:
+ # error = "This account was created for use with an external authentication method, contact your local Galaxy administrator to activate it."
+ # elif not user.check_password( password ):
+ # error = "Invalid password"
+ # else:
+ # trans.handle_user_login( user )
+ # trans.log_event( "User logged in" )
+ # return error
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac lib/galaxy/webapps/galaxy/controllers/page.py
--- a/lib/galaxy/webapps/galaxy/controllers/page.py
+++ b/lib/galaxy/webapps/galaxy/controllers/page.py
@@ -500,14 +500,14 @@
.first()
if not other:
mtype = "error"
- msg = ( "User '%s' does not exist" % email )
+ msg = ( "User '%s' does not exist" % escape( email ) )
elif other == trans.get_user():
mtype = "error"
msg = ( "You cannot share a page with yourself" )
elif trans.sa_session.query( model.PageUserShareAssociation ) \
.filter_by( user=other, page=page ).count() > 0:
mtype = "error"
- msg = ( "Page already shared with '%s'" % email )
+ msg = ( "Page already shared with '%s'" % escape( email ) )
else:
share = model.PageUserShareAssociation()
share.page = page
@@ -516,7 +516,9 @@
session.add( share )
self.create_item_slug( session, page )
session.flush()
- trans.set_message( "Page '%s' shared with user '%s'" % ( page.title, other.email ) )
+ page_title = escape( page.title )
+ other_email = escape( other.email )
+ trans.set_message( "Page '%s' shared with user '%s'" % ( page_title, other_email ) )
return trans.response.send_redirect( url_for( controller='page', action='sharing', id=id ) )
return trans.fill_template( "/ind_share_base.mako",
message = msg,
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac lib/galaxy/webapps/galaxy/controllers/visualization.py
--- a/lib/galaxy/webapps/galaxy/controllers/visualization.py
+++ b/lib/galaxy/webapps/galaxy/controllers/visualization.py
@@ -535,14 +535,14 @@
.first()
if not other:
mtype = "error"
- msg = ( "User '%s' does not exist" % email )
+ msg = ( "User '%s' does not exist" % escape( email ) )
elif other == trans.get_user():
mtype = "error"
msg = ( "You cannot share a visualization with yourself" )
elif trans.sa_session.query( model.VisualizationUserShareAssociation ) \
.filter_by( user=other, visualization=visualization ).count() > 0:
mtype = "error"
- msg = ( "Visualization already shared with '%s'" % email )
+ msg = ( "Visualization already shared with '%s'" % escape( email ) )
else:
share = model.VisualizationUserShareAssociation()
share.visualization = visualization
@@ -551,7 +551,9 @@
session.add( share )
self.create_item_slug( session, visualization )
session.flush()
- trans.set_message( "Visualization '%s' shared with user '%s'" % ( visualization.title, other.email ) )
+ viz_title = escape( visualization.title )
+ other_email = escape( other.email )
+ trans.set_message( "Visualization '%s' shared with user '%s'" % ( viz_title, other_email ) )
return trans.response.send_redirect( web.url_for(controller='visualization', action='sharing', id=id ) )
return trans.fill_template( "/ind_share_base.mako",
message = msg,
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac lib/galaxy/webapps/galaxy/controllers/workflow.py
--- a/lib/galaxy/webapps/galaxy/controllers/workflow.py
+++ b/lib/galaxy/webapps/galaxy/controllers/workflow.py
@@ -310,14 +310,14 @@
.first()
if not other:
mtype = "error"
- msg = ( "User '%s' does not exist" % email )
+ msg = ( "User '%s' does not exist" % escape( email ) )
elif other == trans.get_user():
mtype = "error"
msg = ( "You cannot share a workflow with yourself" )
elif trans.sa_session.query( model.StoredWorkflowUserShareAssociation ) \
.filter_by( user=other, stored_workflow=stored ).count() > 0:
mtype = "error"
- msg = ( "Workflow already shared with '%s'" % email )
+ msg = ( "Workflow already shared with '%s'" % escape( email ) )
else:
share = model.StoredWorkflowUserShareAssociation()
share.stored_workflow = stored
@@ -325,7 +325,7 @@
session = trans.sa_session
session.add( share )
session.flush()
- trans.set_message( "Workflow '%s' shared with user '%s'" % ( stored.name, other.email ) )
+ trans.set_message( "Workflow '%s' shared with user '%s'" % ( escape( stored.name ), escape( other.email ) ) )
return trans.response.send_redirect( url_for( controller='workflow', action='sharing', id=id ) )
return trans.fill_template( "/ind_share_base.mako",
message=msg,
@@ -436,7 +436,7 @@
stored.latest_workflow.name = san_new_name
trans.sa_session.flush()
# For current workflows grid:
- trans.set_message( "Workflow renamed to '%s'." % new_name )
+ trans.set_message( "Workflow renamed to '%s'." % san_new_name )
return self.list( trans )
# For new workflows grid:
#message = "Workflow renamed to '%s'." % new_name
@@ -556,7 +556,7 @@
session.add( new_stored )
session.flush()
# Display the management page
- trans.set_message( 'Created new workflow with name "%s"' % new_stored.name )
+ trans.set_message( 'Created new workflow with name "%s"' % escape( new_stored.name ) )
return self.list( trans )
@web.expose
@@ -603,7 +603,7 @@
trans.sa_session.add( stored )
trans.sa_session.flush()
# Display the management page
- trans.set_message( "Workflow '%s' deleted" % stored.name )
+ trans.set_message( "Workflow '%s' deleted" % escape( stored.name ) )
return self.list( trans )
@web.expose
@@ -1110,7 +1110,7 @@
message += "Imported, but this workflow contains cycles. "
status = "error"
else:
- message += "Workflow <b>%s</b> imported successfully. " % workflow.name
+ message += "Workflow <b>%s</b> imported successfully. " % escape( workflow.name )
if missing_tool_tups:
if trans.user_is_admin():
# A required tool is not available in the local Galaxy instance.
@@ -1124,7 +1124,7 @@
message += "You can likely install the required tools from one of the Galaxy tool sheds listed below.<br/>"
for missing_tool_tup in missing_tool_tups:
missing_tool_id, missing_tool_name, missing_tool_version = missing_tool_tup
- message += "<b>Tool name</b> %s, <b>id</b> %s, <b>version</b> %s<br/>" % ( missing_tool_name, missing_tool_id, missing_tool_version )
+ message += "<b>Tool name</b> %s, <b>id</b> %s, <b>version</b> %s<br/>" % ( escape( missing_tool_name ), escape( missing_tool_id ), escape( missing_tool_version ) )
message += "<br/>"
for shed_name, shed_url in trans.app.tool_shed_registry.tool_sheds.items():
if shed_url.endswith( '/' ):
@@ -1134,7 +1134,7 @@
url += '&tool_id='
for missing_tool_tup in missing_tool_tups:
missing_tool_id = missing_tool_tup[0]
- url += '%s,' % missing_tool_id
+ url += '%s,' % escape( missing_tool_id )
message += '<a href="%s">%s</a><br/>' % ( url, shed_name )
status = 'error'
if installed_repository_file or tool_shed_url:
@@ -1154,13 +1154,13 @@
pass
if tool_shed_url:
# We've received the textual representation of a workflow from a Galaxy tool shed.
- message = "Workflow <b>%s</b> imported successfully." % workflow.name
+ message = "Workflow <b>%s</b> imported successfully." % escape( workflow.name )
url = '%s/workflow/view_workflow?repository_metadata_id=%s&workflow_name=%s&message=%s' % \
( tool_shed_url, repository_metadata_id, encoding_util.tool_shed_encode( workflow_name ), message )
return trans.response.send_redirect( url )
elif installed_repository_file:
# The workflow was read from a file included with an installed tool shed repository.
- message = "Workflow <b>%s</b> imported successfully." % workflow.name
+ message = "Workflow <b>%s</b> imported successfully." % escape( workflow.name )
if cntrller == 'api':
return status, message
return trans.response.send_redirect( web.url_for( controller='admin_toolshed',
@@ -1205,7 +1205,7 @@
# Index page with message
workflow_id = trans.security.encode_id( stored_workflow.id )
return trans.show_message( 'Workflow "%s" created from current history. You can <a href="%s" target="_parent">edit</a> or <a href="%s">run</a> the workflow.' %
- ( workflow_name, url_for( controller='workflow', action='editor', id=workflow_id ),
+ ( escape( workflow_name ), url_for( controller='workflow', action='editor', id=workflow_id ),
url_for( controller='workflow', action='run', id=workflow_id ) ) )
@web.expose
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac templates/embed_base.mako
--- a/templates/embed_base.mako
+++ b/templates/embed_base.mako
@@ -51,7 +51,7 @@
</div><h4><a class="toggle-embed" href="${display_href}" title="Show or hide ${item_display_name} content">Galaxy ${get_class_display_name( item.__class__ )} | ${get_item_name( item ) | h}</a></h4>
%if hasattr( item, "annotation") and item.annotation:
- <div class="annotation">${item.annotation}</div>
+ <div class="annotation">${item.annotation | h}</div>
%endif
## Use a hidden var to store the ajax URL for getting an item's content.
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac templates/ind_share_base.mako
--- a/templates/ind_share_base.mako
+++ b/templates/ind_share_base.mako
@@ -91,7 +91,7 @@
Email address of user to share with
</label><div style="float: left; width: 250px; margin-right: 10px;">
- <input type="text" name="email" value="${email}" size="40">
+ <input type="text" name="email" value="${email | h}" size="40"></div><div style="clear: both"></div></div>
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac templates/user/dbkeys.mako
--- a/templates/user/dbkeys.mako
+++ b/templates/user/dbkeys.mako
@@ -148,7 +148,7 @@
Processing
% endif
</td>
- <td><form action="dbkeys" method="post"><input type="hidden" name="key" value="${key}" /><input type="submit" name="delete" value="Delete" /></form></td>
+ <td><form action="dbkeys" method="post"><input type="hidden" name="key" value="${key | h}" /><input type="submit" name="delete" value="Delete" /></form></td></tr>
% endfor
</table>
@@ -194,7 +194,7 @@
<div style="clear: both; padding-bottom: 0.5em"></div><select id="fasta_input" name="dataset_id">
%for dataset in fasta_hdas:
- <option value="${trans.security.encode_id( dataset.id )}">${dataset.hid}: ${dataset.name}</option>
+ <option value="${trans.security.encode_id( dataset.id )}">${dataset.hid | h}: ${dataset.name | h}</option>
%endfor
</select><input type="file" id="len_file_input" name="len_file" /></input>
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac templates/user/openid_associate.mako
--- a/templates/user/openid_associate.mako
+++ b/templates/user/openid_associate.mako
@@ -48,13 +48,13 @@
The following OpenIDs will be associated with the account chosen or created below.
<ul>
%for openid in openids:
- <li>${openid.openid}</li>
+ <li>${openid.openid | h}</li>
%endfor
</ul></div>
%else:
<div>
- The OpenID <strong>${openids[0].openid}</strong> will be associated with the account chosen or created.
+ The OpenID <strong>${openids[0].openid | h}</strong> will be associated with the account chosen or created.
</div>
%endif
<br/>
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac templates/user/toolbox_filters.mako
--- a/templates/user/toolbox_filters.mako
+++ b/templates/user/toolbox_filters.mako
@@ -15,7 +15,7 @@
%if tool_filters or section_filters or label_filters:
<div class="toolForm">
- <form name="toolbox_filter" id="toolbox_filter" action="${h.url_for( controller='user', action='edit_toolbox_filters', cntrller=cntrller, user_id=trans.security.encode_id( user.id ) )}" method="post" >
+ <form name="toolbox_filter" id="toolbox_filter" action="${h.url_for( controller='user', action='edit_toolbox_filters', cntrller=cntrller )}" method="post" >
% if tool_filters:
<div class="toolFormTitle">Edit ToolBox filters :: Tools</div><div class="toolFormBody">
@@ -87,5 +87,5 @@
</form></div>
%else:
- ${render_msg( 'No filter available. Contact you system administrator or check your configuration file.', 'info' )}
+ ${render_msg( 'No filters available. Contact your system administrator or check your configuration file.', 'info' )}
%endif
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac templates/user/username.mako
--- a/templates/user/username.mako
+++ b/templates/user/username.mako
@@ -1,4 +1,9 @@
<%inherit file="/base.mako"/>
+<%namespace file="/message.mako" import="render_msg" />
+
+%if message:
+ ${render_msg( message, status )}
+%endif
<% is_admin = cntrller == 'admin' and trans.user_is_admin() %>
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac templates/webapps/galaxy/root/tool_menu.mako
--- a/templates/webapps/galaxy/root/tool_menu.mako
+++ b/templates/webapps/galaxy/root/tool_menu.mako
@@ -82,7 +82,7 @@
%if t.user.stored_workflow_menu_entries:
%for m in t.user.stored_workflow_menu_entries:
<div class="toolTitle">
- <a href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id(m.stored_workflow_id) )}" target="galaxy_main">${ util.unicodify( m.stored_workflow.name ) }</a>
+ <a href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id(m.stored_workflow_id) )}" target="galaxy_main">${ util.unicodify( m.stored_workflow.name ) | h}</a></div>
%endfor
%endif
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac templates/webapps/galaxy/tool_executed.mako
--- a/templates/webapps/galaxy/tool_executed.mako
+++ b/templates/webapps/galaxy/tool_executed.mako
@@ -61,7 +61,7 @@
${jobs_str} been successfully added to the queue - resulting in the following ${datasets_str}:
</p>
%for _, data in out_data:
- <div style="padding: 10px"><b> ${data.hid}: ${data.name}</b></div>
+ <div style="padding: 10px"><b> ${data.hid}: ${data.name | h}</b></div>
%endfor
<p>
@@ -83,7 +83,7 @@
<ul><!-- Styling on this list is a little flat. Consider identing these error messages. -->
%for job_error in job_errors:
- <li><b>${job_error}</b></li>
+ <li><b>${job_error | h}</b></li>
%endfor
</ul></div>
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac 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>
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac templates/webapps/galaxy/workflow/configure_menu.mako
--- a/templates/webapps/galaxy/workflow/configure_menu.mako
+++ b/templates/webapps/galaxy/workflow/configure_menu.mako
@@ -1,4 +1,5 @@
<%inherit file="/webapps/galaxy/base_panels.mako"/>
+<%page expression_filter="h"/><%def name="init()"><%
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac templates/webapps/galaxy/workflow/display.mako
--- a/templates/webapps/galaxy/workflow/display.mako
+++ b/templates/webapps/galaxy/workflow/display.mako
@@ -40,7 +40,7 @@
<%def name="row_for_param( param, value, other_values, prefix, step )"><% cls = "form-row" %><div class="${cls}">
- <label>${param.get_label()}</label>
+ <label>${param.get_label() | h}</label><div>
%if isinstance( param, DataToolParameter ) or isinstance( param, DataCollectionToolParameter ):
%if ( prefix + param.name ) in step.input_connections_by_name:
@@ -93,19 +93,19 @@
%><div class="toolForm">
%if tool:
- <div class="toolFormTitle">Step ${int(step.order_index)+1}: ${tool.name}</div>
+ <div class="toolFormTitle">Step ${int(step.order_index)+1}: ${tool.name | h}</div><div class="toolFormBody">
${do_inputs( tool.inputs, step.state.inputs, "", step )}
</div>
%else:
- <div class="toolFormTitle">Step ${int(step.order_index)+1}: Unknown Tool with id '${step.tool_id}'</div>
+ <div class="toolFormTitle">Step ${int(step.order_index)+1}: Unknown Tool with id '${step.tool_id | h}'</div>
%endif
</div>
%else:
## TODO: always input dataset?
<% module = step.module %><div class="toolForm">
- <div class="toolFormTitle">Step ${int(step.order_index)+1}: ${module.name}</div>
+ <div class="toolFormTitle">Step ${int(step.order_index)+1}: ${module.name | h}</div><div class="toolFormBody">
${do_inputs( module.get_runtime_inputs(), step.state.inputs, "", step )}
</div>
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac templates/webapps/galaxy/workflow/list.mako
--- a/templates/webapps/galaxy/workflow/list.mako
+++ b/templates/webapps/galaxy/workflow/list.mako
@@ -94,7 +94,7 @@
<% workflow = association.stored_workflow %><tr><td>
- <a class="menubutton" id="shared-${i}-popup" href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id(workflow.id) )}">${h.to_unicode( workflow.name )}</a>
+ <a class="menubutton" id="shared-${i}-popup" href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id(workflow.id) )}">${h.to_unicode( workflow.name ) | h}</a></td><td>${workflow.user.email}</td><td>${len(workflow.latest_workflow.steps)}</td>
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac templates/webapps/galaxy/workflow/list_for_run.mako
--- a/templates/webapps/galaxy/workflow/list_for_run.mako
+++ b/templates/webapps/galaxy/workflow/list_for_run.mako
@@ -36,7 +36,7 @@
%for i, workflow in enumerate( workflows ):
<tr><td>
- <a href="${h.url_for(controller='workflow', action='run', id=trans.security.encode_id(workflow.id) )}">${h.to_unicode( workflow.name )}</a>
+ <a href="${h.url_for(controller='workflow', action='run', id=trans.security.encode_id(workflow.id) )}">${h.to_unicode( workflow.name ) | h}</a><a id="wf-${i}-popup" class="popup-arrow" style="display: none;">▼</a></td><td>${len(workflow.latest_workflow.steps)}</td>
@@ -64,10 +64,10 @@
<% workflow = association.stored_workflow %><tr><td>
- <a href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id(workflow.id) )}">${workflow.name}</a>
+ <a href="${h.url_for( controller='workflow', action='run', id=trans.security.encode_id(workflow.id) )}">${workflow.name | h}</a><a id="shared-${i}-popup" class="popup-arrow" style="display: none;">▼</a></td>
- <td>${workflow.user.email}</td>
+ <td>${workflow.user.email | h}</td><td>${len(workflow.latest_workflow.steps)}</td></tr>
%endfor
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac templates/webapps/galaxy/workflow/missing_tools.mako
--- a/templates/webapps/galaxy/workflow/missing_tools.mako
+++ b/templates/webapps/galaxy/workflow/missing_tools.mako
@@ -1,6 +1,6 @@
<%inherit file="/base.mako"/>
-<h2>Cannot run workflow "${h.to_unicode( workflow.name )}"</h2>
+<h2>Cannot run workflow "${h.to_unicode( workflow.name ) | h}"</h2>
%if workflow.annotation:
<div class="workflow-annotation">${workflow.annotation}</div>
@@ -11,7 +11,7 @@
<strong>This workflow utilizes tools which are unavailable, and cannot be run. Enable the tools listed below, or <a href="${h.url_for(controller='workflow', action='editor', id=trans.security.encode_id(workflow.id) )}" target="_parent">edit the workflow</a> to correct these errors.</strong><br/><ul>
%for i, tool in enumerate( missing_tools ):
- <li>${tool}</li>
+ <li>${tool | h}</li>
%endfor
</ul></div>
\ No newline at end of file
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac templates/webapps/galaxy/workflow/myexp_export.mako
--- a/templates/webapps/galaxy/workflow/myexp_export.mako
+++ b/templates/webapps/galaxy/workflow/myexp_export.mako
@@ -9,7 +9,7 @@
## Generate request.
<?xml version="1.0"?><workflow>
- <title>${workflow_name}</title>
+ <title>${workflow_name | h}</title><description>${workflow_description}</description><type>Galaxy</type><content encoding="base64" type="binary">
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac templates/webapps/galaxy/workflow/rename.mako
--- a/templates/webapps/galaxy/workflow/rename.mako
+++ b/templates/webapps/galaxy/workflow/rename.mako
@@ -15,7 +15,7 @@
%endif
<div class="toolForm">
- <div class="toolFormTitle">Rename workflow '${stored.name}'</div>
+ <div class="toolFormTitle">Rename workflow '${stored.name | h}'</div><div class="toolFormBody"><form action="${h.url_for(controller='workflow', action='rename', id=trans.security.encode_id(stored.id) )}" method="POST"><div class="form-row">
@@ -23,7 +23,7 @@
New name
</label><div style="float: left; width: 250px; margin-right: 10px;">
- <input type="text" name="new_name" value="${stored.name}" size="40">
+ <input type="text" name="new_name" value="${stored.name | h}" size="40"></div><div style="clear: both"></div></div>
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac templates/webapps/galaxy/workflow/run.mako
--- a/templates/webapps/galaxy/workflow/run.mako
+++ b/templates/webapps/galaxy/workflow/run.mako
@@ -372,7 +372,7 @@
<% cls = "form-row" %>
%endif
<div class="${cls}">
- <label>${param.get_label()}</label>
+ <label>${param.get_label() | h}</label><div>
%if isinstance( param, DataToolParameter ) or isinstance( param, DataCollectionToolParameter ):
%if ( prefix + param.name ) in step.input_connections_by_name:
@@ -444,7 +444,7 @@
%else:
<span class="workflow_parameters"><span class="uneditable_field">
- ${param.value_to_display_text( value, app )}
+ ${param.value_to_display_text( value, app ) | h}
</span><span class="editable_field"><span class="editable">
@@ -474,7 +474,7 @@
<span class="action-button" id="hide_all_tool_body">Collapse</span></div>
-<h2>Running workflow "${h.to_unicode( workflow.name )}"</h2>
+<h2>Running workflow "${h.to_unicode( workflow.name ) | h}"</h2>
%if has_upgrade_messages:
<div class="warningmessage">
@@ -574,6 +574,7 @@
<%
pja_ss_all = []
for pja_ss in [ActionBox.get_short_str(pja) for pja in step.post_job_actions]:
+ pja_ss = h.escape( pja_ss )
for rematch in re.findall('\$\{.+?\}', pja_ss):
pja_ss = pja_ss.replace(rematch, '<span style="background-color:%s" class="wfpspan wf_parm__%s pja_wfp">%s</span>' % (wf_parms[rematch[2:-1]], rematch[2:-1], rematch[2:-1]))
pja_ss_all.append(pja_ss)
@@ -586,7 +587,7 @@
%else:
<div class="toolForm"><div class="toolFormTitle">
- <span class='title_ul_text'>Step ${int(step.order_index)+1}: ${module.name}</span>
+ <span class='title_ul_text'>Step ${int(step.order_index)+1}: ${module.name | h}</span>
% if step.annotations:
<div class="step-annotation">${step.annotations[0].annotation}</div>
% endif
diff -r 49ef0cdbf45acca661900c22869f33cd01536ecf -r ac30ccd5cd1e285390706592a0b005f79456cbac templates/webapps/galaxy/workflow/run_complete.mako
--- a/templates/webapps/galaxy/workflow/run_complete.mako
+++ b/templates/webapps/galaxy/workflow/run_complete.mako
@@ -1,4 +1,5 @@
<%inherit file="/base.mako"/>
+<%page expression_filter="h"/><div class="donemessagelarge">
%if scheduled:
@@ -7,7 +8,7 @@
<div class="workflow-invocation-complete">
%if invocation['new_history']:
<p>These datasets will appear in a new history:
- <a target='galaxy_history' href="${h.url_for( controller='history', action='list', operation="Switch", id=trans.security.encode_id(invocation['new_history'].id), use_panels=False, show_deleted=False )}">
+ <a target='galaxy_history' href="${h.url_for( controller='history', action='list', operation="Switch", id=trans.security.encode_id(invocation['new_history'].id), use_panels=False, show_deleted=False ) | n}">
'${h.to_unicode(invocation['new_history'].name)}'.
</a></p>
%endif
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: natefoo: Update tag latest_2014.10.06 for changeset 782cf1a1f6b5
by commits-noreply@bitbucket.org 09 Dec '14
by commits-noreply@bitbucket.org 09 Dec '14
09 Dec '14
1 new commit in galaxy-central:
https://bitbucket.org/galaxy/galaxy-central/commits/3e3ac0894aba/
Changeset: 3e3ac0894aba
Branch: stable
User: natefoo
Date: 2014-12-09 14:00:15+00:00
Summary: Update tag latest_2014.10.06 for changeset 782cf1a1f6b5
Affected #: 1 file
diff -r 782cf1a1f6b56f8a9c0b3e5e9ffd29fd93b16ce3 -r 3e3ac0894aba44bba95bce6dd6833e639e01f890 .hgtags
--- a/.hgtags
+++ b/.hgtags
@@ -20,4 +20,4 @@
ca45b78adb4152fc6e7395514d46eba6b7d0b838 release_2014.08.11
548ab24667d6206780237bd807f7d857a484c461 latest_2014.08.11
2092948937ac30ef82f71463a235c66d34987088 release_2014.10.06
-8e45b1cefba16727d4d3a7d0dceaaaf1ef400a0c latest_2014.10.06
+782cf1a1f6b56f8a9c0b3e5e9ffd29fd93b16ce3 latest_2014.10.06
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: martenson: show wrapper versions and requirement versions in the toolform header
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/8d8bce91a289/
Changeset: 8d8bce91a289
User: martenson
Date: 2014-12-08 22:07:58+00:00
Summary: show wrapper versions and requirement versions in the toolform header
Affected #: 1 file
diff -r 8e98c3d9b9a8bc3cabff60c67367fbaf5b328320 -r 8d8bce91a289e040d47f9b757aae4051a0501743 templates/webapps/galaxy/tool_form.mako
--- a/templates/webapps/galaxy/tool_form.mako
+++ b/templates/webapps/galaxy/tool_form.mako
@@ -353,10 +353,25 @@
%if tool.has_multiple_pages:
<div class="toolFormTitle">${tool.name} (step ${tool_state.page+1} of ${tool.npages})
%elif not tool_version_select_field:
- <div class="toolFormTitle">${tool.name} (version ${tool.version})
+ <div class="toolFormTitle">${tool.name} (Galaxy tool version ${tool.version})
%else:
<div class="toolFormTitle">${tool.name} ${tool_version_select_field.get_html()}
%endif
+ ## Show information button with underlying requirements and their versions
+ %if tool.requirements:
+ <a href="#" class="icon-btn" title="Underlying versions" tabindex="0" data-toggle="popover" data-placement="bottom" data-content=
+ "
+ %for i, requirement in enumerate( tool.requirements ):
+ ${ requirement.name } v ${ requirement.version } ${ '' if i + 1 == len( tool.requirements ) else ' | ' }
+ %endfor
+ "
+ onclick="$(function () {
+ $( '[ data-toggle=\'popover\' ]' ).popover();
+ $( this ).popover( 'show' );
+ })">
+ <span class="fa fa-info-circle"></span>
+ </a>
+ %endif
<span class="pull-right">
%if trans.app.config.biostar_url:
@@ -442,4 +457,4 @@
<div id="citations"></div>
%endif
-</div>
\ No newline at end of file
+</div>
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 carlfeberhard/carlfeberhard-galaxy-central-stable/stable (pull request #600)
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/782cf1a1f6b5/
Changeset: 782cf1a1f6b5
Branch: stable
User: dannon
Date: 2014-12-08 22:06:25+00:00
Summary: Merged in carlfeberhard/carlfeberhard-galaxy-central-stable/stable (pull request #600)
[STABLE] Fix to 04a072e to use the correct mako method in the masthead.
Affected #: 1 file
diff -r 07404a82972d877b5529fffaeb3e7e05b69a02a3 -r 782cf1a1f6b56f8a9c0b3e5e9ffd29fd93b16ce3 templates/webapps/galaxy/galaxy.masthead.mako
--- a/templates/webapps/galaxy/galaxy.masthead.mako
+++ b/templates/webapps/galaxy/galaxy.masthead.mako
@@ -1,4 +1,4 @@
-<%namespace file="/galaxy_client_app.mako" import="get_user_json" />
+<%namespace file="/galaxy_client_app.mako" import="get_user_dict" />
## masthead head generator
<%def name="load(active_view = None)">
@@ -34,7 +34,7 @@
'requests' : bool(trans.user and (trans.user.requests or trans.app.security_agent.get_accessible_request_types(trans, trans.user))),
'email' : trans.user.email if (trans.user) else "",
'valid' : bool(trans.user != None),
- 'json' : get_user_json()
+ 'json' : get_user_dict()
}
}
%>
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/030db20e2f4f/
Changeset: 030db20e2f4f
Branch: stable
User: carlfeberhard
Date: 2014-12-08 21:47:33+00:00
Summary: Fix to 04a072e: use proper mako dict method instead of printing json string
Affected #: 1 file
diff -r 374e94196a14673993540a60d446addc6b1780a0 -r 030db20e2f4f15d63f622ccdd224d65214cb8d7a templates/webapps/galaxy/galaxy.masthead.mako
--- a/templates/webapps/galaxy/galaxy.masthead.mako
+++ b/templates/webapps/galaxy/galaxy.masthead.mako
@@ -1,4 +1,4 @@
-<%namespace file="/galaxy_client_app.mako" import="get_user_json" />
+<%namespace file="/galaxy_client_app.mako" import="get_user_dict" />
## masthead head generator
<%def name="load(active_view = None)">
@@ -34,7 +34,7 @@
'requests' : bool(trans.user and (trans.user.requests or trans.app.security_agent.get_accessible_request_types(trans, trans.user))),
'email' : trans.user.email if (trans.user) else "",
'valid' : bool(trans.user != None),
- 'json' : get_user_json()
+ 'json' : get_user_dict()
}
}
%>
https://bitbucket.org/galaxy/galaxy-central/commits/782cf1a1f6b5/
Changeset: 782cf1a1f6b5
Branch: stable
User: dannon
Date: 2014-12-08 22:06:25+00:00
Summary: Merged in carlfeberhard/carlfeberhard-galaxy-central-stable/stable (pull request #600)
[STABLE] Fix to 04a072e to use the correct mako method in the masthead.
Affected #: 1 file
diff -r 07404a82972d877b5529fffaeb3e7e05b69a02a3 -r 782cf1a1f6b56f8a9c0b3e5e9ffd29fd93b16ce3 templates/webapps/galaxy/galaxy.masthead.mako
--- a/templates/webapps/galaxy/galaxy.masthead.mako
+++ b/templates/webapps/galaxy/galaxy.masthead.mako
@@ -1,4 +1,4 @@
-<%namespace file="/galaxy_client_app.mako" import="get_user_json" />
+<%namespace file="/galaxy_client_app.mako" import="get_user_dict" />
## masthead head generator
<%def name="load(active_view = None)">
@@ -34,7 +34,7 @@
'requests' : bool(trans.user and (trans.user.requests or trans.app.security_agent.get_accessible_request_types(trans, trans.user))),
'email' : trans.user.email if (trans.user) else "",
'valid' : bool(trans.user != None),
- 'json' : get_user_json()
+ 'json' : get_user_dict()
}
}
%>
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 mode-button plugin from ui.js; update history/multi-view.js and history/view.mako
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/8e98c3d9b9a8/
Changeset: 8e98c3d9b9a8
User: carlfeberhard
Date: 2014-12-08 21:34:09+00:00
Summary: Client build: modularize mode-button plugin from ui.js; update history/multi-view.js and history/view.mako
Affected #: 10 files
diff -r 0fb0c56af3cf25bb8c3bb366d34c6aedcbb9df14 -r 8e98c3d9b9a8bc3cabff60c67367fbaf5b328320 client/galaxy/scripts/jq-plugins/ui/mode-button.js
--- /dev/null
+++ b/client/galaxy/scripts/jq-plugins/ui/mode-button.js
@@ -0,0 +1,191 @@
+// 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) {
+ //TODO: So...this turns out to be an all or nothing thing. If I load jQuery in the define below, it will
+ // (of course) wipe the old jquery *and all the plugins loaded into it*. So the define below *is still
+ // relying on jquery being loaded globally* in order to preserve plugins.
+ define([], factory);
+ } else {
+ // Browser globals
+ factory(jQuery);
+ }
+
+}(function () {
+
+ /** Multi 'mode' button (or any element really) that changes the html
+ * contents of itself when clicked. Pass in an ordered list of
+ * objects with 'html' and (optional) onclick functions.
+ *
+ * When clicked in a particular node, the onclick function will
+ * be called (with the element as this) and the element will
+ * switch to the next mode, replacing its html content with
+ * that mode's html.
+ *
+ * If there is no next mode, the element will switch back to
+ * the first mode.
+ * @example:
+ * $( '.myElement' ).modeButton({
+ * modes : [
+ * {
+ * mode: 'bler',
+ * html: '<h5>Bler</h5>',
+ * onclick : function(){
+ * $( 'body' ).css( 'background-color', 'red' );
+ * }
+ * },
+ * {
+ * mode: 'bloo',
+ * html: '<h4>Bloo</h4>',
+ * onclick : function(){
+ * $( 'body' ).css( 'background-color', 'blue' );
+ * }
+ * },
+ * {
+ * mode: 'blah',
+ * html: '<h3>Blah</h3>',
+ * onclick : function(){
+ * $( 'body' ).css( 'background-color', 'grey' );
+ * }
+ * },
+ * ]
+ * });
+ * $( '.myElement' ).modeButton( 'callModeFn', 'bler' );
+ */
+ /** constructor */
+ function ModeButton( element, options ){
+ this.currModeIndex = 0;
+ return this._init( element, options );
+ }
+
+ /** html5 data key to store this object inside an element */
+ ModeButton.prototype.DATA_KEY = 'mode-button';
+ /** default options */
+ ModeButton.prototype.defaults = {
+ switchModesOnClick : true
+ };
+
+ // ---- private interface
+ /** set up options, intial mode, and the click handler */
+ ModeButton.prototype._init = function _init( element, options ){
+ //console.debug( 'ModeButton._init:', element, options );
+ options = options || {};
+ this.$element = $( element );
+ this.options = $.extend( true, {}, this.defaults, options );
+ if( !options.modes ){
+ throw new Error( 'ModeButton requires a "modes" array' );
+ }
+
+ var modeButton = this;
+ this.$element.click( function _ModeButtonClick( event ){
+ // call the curr mode fn
+ modeButton.callModeFn();
+ // inc the curr mode index
+ if( modeButton.options.switchModesOnClick ){ modeButton._incModeIndex(); }
+ // set the element html
+ $( this ).html( modeButton.options.modes[ modeButton.currModeIndex ].html );
+ });
+ return this.reset();
+ };
+ /** increment the mode index to the next in the array, looping back to zero if at the last */
+ ModeButton.prototype._incModeIndex = function _incModeIndex(){
+ this.currModeIndex += 1;
+ if( this.currModeIndex >= this.options.modes.length ){
+ this.currModeIndex = 0;
+ }
+ return this;
+ };
+ /** get the mode index in the modes array for the given key (mode name) */
+ ModeButton.prototype._getModeIndex = function _getModeIndex( modeKey ){
+ for( var i=0; i<this.options.modes.length; i+=1 ){
+ if( this.options.modes[ i ].mode === modeKey ){ return i; }
+ }
+ throw new Error( 'mode not found: ' + modeKey );
+ };
+ /** set the current mode to the one with the given index and set button html */
+ ModeButton.prototype._setModeByIndex = function _setModeByIndex( index ){
+ var newMode = this.options.modes[ index ];
+ if( !newMode ){
+ throw new Error( 'mode index not found: ' + index );
+ }
+ this.currModeIndex = index;
+ if( newMode.html ){
+ this.$element.html( newMode.html );
+ }
+ return this;
+ };
+
+ // ---- public interface
+ /** get the current mode object (not just the mode name) */
+ ModeButton.prototype.currentMode = function currentMode(){
+ return this.options.modes[ this.currModeIndex ];
+ };
+ /** return the mode key of the current mode */
+ ModeButton.prototype.current = function current(){
+ // sugar for returning mode name
+ return this.currentMode().mode;
+ };
+ /** get the mode with the given modeKey or the current mode if modeKey is undefined */
+ ModeButton.prototype.getMode = function getMode( modeKey ){
+ if( !modeKey ){ return this.currentMode(); }
+ return this.options.modes[( this._getModeIndex( modeKey ) )];
+ };
+ /** T/F if the button has the given mode */
+ ModeButton.prototype.hasMode = function hasMode( modeKey ){
+ try {
+ return !!this.getMode( modeKey );
+ } catch( err ){}
+ return false;
+ };
+ /** set the current mode to the mode with the given name */
+ ModeButton.prototype.setMode = function setMode( modeKey ){
+ return this._setModeByIndex( this._getModeIndex( modeKey ) );
+ };
+ /** reset to the initial mode */
+ ModeButton.prototype.reset = function reset(){
+ this.currModeIndex = 0;
+ if( this.options.initialMode ){
+ this.currModeIndex = this._getModeIndex( this.options.initialMode );
+ }
+ return this._setModeByIndex( this.currModeIndex );
+ };
+ /** manually call the click handler of the given mode */
+ ModeButton.prototype.callModeFn = function callModeFn( modeKey ){
+ var modeFn = this.getMode( modeKey ).onclick;
+ if( modeFn && $.type( modeFn === 'function' ) ){
+ // call with the element as context (std jquery pattern)
+ return modeFn.call( this.$element.get(0) );
+ }
+ return undefined;
+ };
+
+ // as jq plugin
+ $.fn.modeButton = function $modeButton( options ){
+ if( !this.size() ){ return this; }
+
+ //TODO: does map still work with jq multi selection (i.e. $( '.class-for-many-btns' ).modeButton)?
+ if( $.type( options ) === 'object' ){
+ return this.map( function(){
+ var $this = $( this );
+ $this.data( 'mode-button', new ModeButton( $this, options ) );
+ return this;
+ });
+ }
+
+ var $first = $( this[0] ),
+ button = $first.data( 'mode-button' );
+
+ if( !button ){
+ throw new Error( 'modeButton needs an options object or string name of a function' );
+ }
+
+ if( button && $.type( options ) === 'string' ){
+ var fnName = options;
+ if( button && $.type( button[ fnName ] ) === 'function' ){
+ return button[ fnName ].apply( button, $.makeArray( arguments ).slice( 1 ) );
+ }
+ }
+ return button;
+ };
+
+}));
diff -r 0fb0c56af3cf25bb8c3bb366d34c6aedcbb9df14 -r 8e98c3d9b9a8bc3cabff60c67367fbaf5b328320 client/galaxy/scripts/mvc/history/multi-panel.js
--- a/client/galaxy/scripts/mvc/history/multi-panel.js
+++ b/client/galaxy/scripts/mvc/history/multi-panel.js
@@ -2,7 +2,8 @@
"mvc/history/history-model",
"mvc/history/history-panel-edit",
"mvc/base-mvc",
- "utils/ajax-queue"
+ "utils/ajax-queue",
+ "jq-plugins/ui/mode-button"
], function( HISTORY_MODEL, HPANEL_EDIT, baseMVC, ajaxQueue ){
window.HISTORY_MODEL = HISTORY_MODEL;
//==============================================================================
diff -r 0fb0c56af3cf25bb8c3bb366d34c6aedcbb9df14 -r 8e98c3d9b9a8bc3cabff60c67367fbaf5b328320 client/galaxy/scripts/mvc/ui.js
--- a/client/galaxy/scripts/mvc/ui.js
+++ b/client/galaxy/scripts/mvc/ui.js
@@ -749,184 +749,3 @@
}
});
}());
-
-
-//==============================================================================
-(function(){
- /** Multi 'mode' button (or any element really) that changes the html
- * contents of itself when clicked. Pass in an ordered list of
- * objects with 'html' and (optional) onclick functions.
- *
- * When clicked in a particular node, the onclick function will
- * be called (with the element as this) and the element will
- * switch to the next mode, replacing its html content with
- * that mode's html.
- *
- * If there is no next mode, the element will switch back to
- * the first mode.
- * @example:
- * $( '.myElement' ).modeButton({
- * modes : [
- * {
- * mode: 'bler',
- * html: '<h5>Bler</h5>',
- * onclick : function(){
- * $( 'body' ).css( 'background-color', 'red' );
- * }
- * },
- * {
- * mode: 'bloo',
- * html: '<h4>Bloo</h4>',
- * onclick : function(){
- * $( 'body' ).css( 'background-color', 'blue' );
- * }
- * },
- * {
- * mode: 'blah',
- * html: '<h3>Blah</h3>',
- * onclick : function(){
- * $( 'body' ).css( 'background-color', 'grey' );
- * }
- * },
- * ]
- * });
- * $( '.myElement' ).modeButton( 'callModeFn', 'bler' );
- */
- /** constructor */
- function ModeButton( element, options ){
- this.currModeIndex = 0;
- return this._init( element, options );
- }
-
- /** html5 data key to store this object inside an element */
- ModeButton.prototype.DATA_KEY = 'mode-button';
- /** default options */
- ModeButton.prototype.defaults = {
- switchModesOnClick : true
- };
-
- // ---- private interface
- /** set up options, intial mode, and the click handler */
- ModeButton.prototype._init = function _init( element, options ){
- //console.debug( 'ModeButton._init:', element, options );
- options = options || {};
- this.$element = $( element );
- this.options = jQuery.extend( true, {}, this.defaults, options );
- if( !options.modes ){
- throw new Error( 'ModeButton requires a "modes" array' );
- }
-
- var modeButton = this;
- this.$element.click( function _ModeButtonClick( event ){
- // call the curr mode fn
- modeButton.callModeFn();
- // inc the curr mode index
- if( modeButton.options.switchModesOnClick ){ modeButton._incModeIndex(); }
- // set the element html
- $( this ).html( modeButton.options.modes[ modeButton.currModeIndex ].html );
- });
- return this.reset();
- };
- /** increment the mode index to the next in the array, looping back to zero if at the last */
- ModeButton.prototype._incModeIndex = function _incModeIndex(){
- this.currModeIndex += 1;
- if( this.currModeIndex >= this.options.modes.length ){
- this.currModeIndex = 0;
- }
- return this;
- };
- /** get the mode index in the modes array for the given key (mode name) */
- ModeButton.prototype._getModeIndex = function _getModeIndex( modeKey ){
- for( var i=0; i<this.options.modes.length; i+=1 ){
- if( this.options.modes[ i ].mode === modeKey ){ return i; }
- }
- throw new Error( 'mode not found: ' + modeKey );
- };
- /** set the current mode to the one with the given index and set button html */
- ModeButton.prototype._setModeByIndex = function _setModeByIndex( index ){
- var newMode = this.options.modes[ index ];
- if( !newMode ){
- throw new Error( 'mode index not found: ' + index );
- }
- this.currModeIndex = index;
- if( newMode.html ){
- this.$element.html( newMode.html );
- }
- return this;
- };
-
- // ---- public interface
- /** get the current mode object (not just the mode name) */
- ModeButton.prototype.currentMode = function currentMode(){
- return this.options.modes[ this.currModeIndex ];
- };
- /** return the mode key of the current mode */
- ModeButton.prototype.current = function current(){
- // sugar for returning mode name
- return this.currentMode().mode;
- };
- /** get the mode with the given modeKey or the current mode if modeKey is undefined */
- ModeButton.prototype.getMode = function getMode( modeKey ){
- if( !modeKey ){ return this.currentMode(); }
- return this.options.modes[( this._getModeIndex( modeKey ) )];
- };
- /** T/F if the button has the given mode */
- ModeButton.prototype.hasMode = function hasMode( modeKey ){
- try {
- return !!this.getMode( modeKey );
- } catch( err ){}
- return false;
- };
- /** set the current mode to the mode with the given name */
- ModeButton.prototype.setMode = function setMode( modeKey ){
- return this._setModeByIndex( this._getModeIndex( modeKey ) );
- };
- /** reset to the initial mode */
- ModeButton.prototype.reset = function reset(){
- this.currModeIndex = 0;
- if( this.options.initialMode ){
- this.currModeIndex = this._getModeIndex( this.options.initialMode );
- }
- return this._setModeByIndex( this.currModeIndex );
- };
- /** manually call the click handler of the given mode */
- ModeButton.prototype.callModeFn = function callModeFn( modeKey ){
- var modeFn = this.getMode( modeKey ).onclick;
- if( modeFn && jQuery.type( modeFn === 'function' ) ){
- // call with the element as context (std jquery pattern)
- return modeFn.call( this.$element.get(0) );
- }
- return undefined;
- };
-
- // as jq plugin
- jQuery.fn.extend({
- modeButton : function $modeButton( options ){
- if( !this.size() ){ return this; }
-
- //TODO: does map still work with jq multi selection (i.e. $( '.class-for-many-btns' ).modeButton)?
- if( jQuery.type( options ) === 'object' ){
- return this.map( function(){
- var $this = $( this );
- $this.data( 'mode-button', new ModeButton( $this, options ) );
- return this;
- });
- }
-
- var $first = $( this[0] ),
- button = $first.data( 'mode-button' );
-
- if( !button ){
- throw new Error( 'modeButton needs an options object or string name of a function' );
- }
-
- if( button && jQuery.type( options ) === 'string' ){
- var fnName = options;
- if( button && jQuery.type( button[ fnName ] ) === 'function' ){
- return button[ fnName ].apply( button, jQuery.makeArray( arguments ).slice( 1 ) );
- }
- }
- return button;
- }
- });
-}());
diff -r 0fb0c56af3cf25bb8c3bb366d34c6aedcbb9df14 -r 8e98c3d9b9a8bc3cabff60c67367fbaf5b328320 static/scripts/jq-plugins/ui/mode-button.js
--- /dev/null
+++ b/static/scripts/jq-plugins/ui/mode-button.js
@@ -0,0 +1,191 @@
+// 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) {
+ //TODO: So...this turns out to be an all or nothing thing. If I load jQuery in the define below, it will
+ // (of course) wipe the old jquery *and all the plugins loaded into it*. So the define below *is still
+ // relying on jquery being loaded globally* in order to preserve plugins.
+ define([], factory);
+ } else {
+ // Browser globals
+ factory(jQuery);
+ }
+
+}(function () {
+
+ /** Multi 'mode' button (or any element really) that changes the html
+ * contents of itself when clicked. Pass in an ordered list of
+ * objects with 'html' and (optional) onclick functions.
+ *
+ * When clicked in a particular node, the onclick function will
+ * be called (with the element as this) and the element will
+ * switch to the next mode, replacing its html content with
+ * that mode's html.
+ *
+ * If there is no next mode, the element will switch back to
+ * the first mode.
+ * @example:
+ * $( '.myElement' ).modeButton({
+ * modes : [
+ * {
+ * mode: 'bler',
+ * html: '<h5>Bler</h5>',
+ * onclick : function(){
+ * $( 'body' ).css( 'background-color', 'red' );
+ * }
+ * },
+ * {
+ * mode: 'bloo',
+ * html: '<h4>Bloo</h4>',
+ * onclick : function(){
+ * $( 'body' ).css( 'background-color', 'blue' );
+ * }
+ * },
+ * {
+ * mode: 'blah',
+ * html: '<h3>Blah</h3>',
+ * onclick : function(){
+ * $( 'body' ).css( 'background-color', 'grey' );
+ * }
+ * },
+ * ]
+ * });
+ * $( '.myElement' ).modeButton( 'callModeFn', 'bler' );
+ */
+ /** constructor */
+ function ModeButton( element, options ){
+ this.currModeIndex = 0;
+ return this._init( element, options );
+ }
+
+ /** html5 data key to store this object inside an element */
+ ModeButton.prototype.DATA_KEY = 'mode-button';
+ /** default options */
+ ModeButton.prototype.defaults = {
+ switchModesOnClick : true
+ };
+
+ // ---- private interface
+ /** set up options, intial mode, and the click handler */
+ ModeButton.prototype._init = function _init( element, options ){
+ //console.debug( 'ModeButton._init:', element, options );
+ options = options || {};
+ this.$element = $( element );
+ this.options = $.extend( true, {}, this.defaults, options );
+ if( !options.modes ){
+ throw new Error( 'ModeButton requires a "modes" array' );
+ }
+
+ var modeButton = this;
+ this.$element.click( function _ModeButtonClick( event ){
+ // call the curr mode fn
+ modeButton.callModeFn();
+ // inc the curr mode index
+ if( modeButton.options.switchModesOnClick ){ modeButton._incModeIndex(); }
+ // set the element html
+ $( this ).html( modeButton.options.modes[ modeButton.currModeIndex ].html );
+ });
+ return this.reset();
+ };
+ /** increment the mode index to the next in the array, looping back to zero if at the last */
+ ModeButton.prototype._incModeIndex = function _incModeIndex(){
+ this.currModeIndex += 1;
+ if( this.currModeIndex >= this.options.modes.length ){
+ this.currModeIndex = 0;
+ }
+ return this;
+ };
+ /** get the mode index in the modes array for the given key (mode name) */
+ ModeButton.prototype._getModeIndex = function _getModeIndex( modeKey ){
+ for( var i=0; i<this.options.modes.length; i+=1 ){
+ if( this.options.modes[ i ].mode === modeKey ){ return i; }
+ }
+ throw new Error( 'mode not found: ' + modeKey );
+ };
+ /** set the current mode to the one with the given index and set button html */
+ ModeButton.prototype._setModeByIndex = function _setModeByIndex( index ){
+ var newMode = this.options.modes[ index ];
+ if( !newMode ){
+ throw new Error( 'mode index not found: ' + index );
+ }
+ this.currModeIndex = index;
+ if( newMode.html ){
+ this.$element.html( newMode.html );
+ }
+ return this;
+ };
+
+ // ---- public interface
+ /** get the current mode object (not just the mode name) */
+ ModeButton.prototype.currentMode = function currentMode(){
+ return this.options.modes[ this.currModeIndex ];
+ };
+ /** return the mode key of the current mode */
+ ModeButton.prototype.current = function current(){
+ // sugar for returning mode name
+ return this.currentMode().mode;
+ };
+ /** get the mode with the given modeKey or the current mode if modeKey is undefined */
+ ModeButton.prototype.getMode = function getMode( modeKey ){
+ if( !modeKey ){ return this.currentMode(); }
+ return this.options.modes[( this._getModeIndex( modeKey ) )];
+ };
+ /** T/F if the button has the given mode */
+ ModeButton.prototype.hasMode = function hasMode( modeKey ){
+ try {
+ return !!this.getMode( modeKey );
+ } catch( err ){}
+ return false;
+ };
+ /** set the current mode to the mode with the given name */
+ ModeButton.prototype.setMode = function setMode( modeKey ){
+ return this._setModeByIndex( this._getModeIndex( modeKey ) );
+ };
+ /** reset to the initial mode */
+ ModeButton.prototype.reset = function reset(){
+ this.currModeIndex = 0;
+ if( this.options.initialMode ){
+ this.currModeIndex = this._getModeIndex( this.options.initialMode );
+ }
+ return this._setModeByIndex( this.currModeIndex );
+ };
+ /** manually call the click handler of the given mode */
+ ModeButton.prototype.callModeFn = function callModeFn( modeKey ){
+ var modeFn = this.getMode( modeKey ).onclick;
+ if( modeFn && $.type( modeFn === 'function' ) ){
+ // call with the element as context (std jquery pattern)
+ return modeFn.call( this.$element.get(0) );
+ }
+ return undefined;
+ };
+
+ // as jq plugin
+ $.fn.modeButton = function $modeButton( options ){
+ if( !this.size() ){ return this; }
+
+ //TODO: does map still work with jq multi selection (i.e. $( '.class-for-many-btns' ).modeButton)?
+ if( $.type( options ) === 'object' ){
+ return this.map( function(){
+ var $this = $( this );
+ $this.data( 'mode-button', new ModeButton( $this, options ) );
+ return this;
+ });
+ }
+
+ var $first = $( this[0] ),
+ button = $first.data( 'mode-button' );
+
+ if( !button ){
+ throw new Error( 'modeButton needs an options object or string name of a function' );
+ }
+
+ if( button && $.type( options ) === 'string' ){
+ var fnName = options;
+ if( button && $.type( button[ fnName ] ) === 'function' ){
+ return button[ fnName ].apply( button, $.makeArray( arguments ).slice( 1 ) );
+ }
+ }
+ return button;
+ };
+
+}));
diff -r 0fb0c56af3cf25bb8c3bb366d34c6aedcbb9df14 -r 8e98c3d9b9a8bc3cabff60c67367fbaf5b328320 static/scripts/mvc/history/multi-panel.js
--- a/static/scripts/mvc/history/multi-panel.js
+++ b/static/scripts/mvc/history/multi-panel.js
@@ -2,7 +2,8 @@
"mvc/history/history-model",
"mvc/history/history-panel-edit",
"mvc/base-mvc",
- "utils/ajax-queue"
+ "utils/ajax-queue",
+ "jq-plugins/ui/mode-button"
], function( HISTORY_MODEL, HPANEL_EDIT, baseMVC, ajaxQueue ){
window.HISTORY_MODEL = HISTORY_MODEL;
//==============================================================================
diff -r 0fb0c56af3cf25bb8c3bb366d34c6aedcbb9df14 -r 8e98c3d9b9a8bc3cabff60c67367fbaf5b328320 static/scripts/mvc/ui.js
--- a/static/scripts/mvc/ui.js
+++ b/static/scripts/mvc/ui.js
@@ -749,184 +749,3 @@
}
});
}());
-
-
-//==============================================================================
-(function(){
- /** Multi 'mode' button (or any element really) that changes the html
- * contents of itself when clicked. Pass in an ordered list of
- * objects with 'html' and (optional) onclick functions.
- *
- * When clicked in a particular node, the onclick function will
- * be called (with the element as this) and the element will
- * switch to the next mode, replacing its html content with
- * that mode's html.
- *
- * If there is no next mode, the element will switch back to
- * the first mode.
- * @example:
- * $( '.myElement' ).modeButton({
- * modes : [
- * {
- * mode: 'bler',
- * html: '<h5>Bler</h5>',
- * onclick : function(){
- * $( 'body' ).css( 'background-color', 'red' );
- * }
- * },
- * {
- * mode: 'bloo',
- * html: '<h4>Bloo</h4>',
- * onclick : function(){
- * $( 'body' ).css( 'background-color', 'blue' );
- * }
- * },
- * {
- * mode: 'blah',
- * html: '<h3>Blah</h3>',
- * onclick : function(){
- * $( 'body' ).css( 'background-color', 'grey' );
- * }
- * },
- * ]
- * });
- * $( '.myElement' ).modeButton( 'callModeFn', 'bler' );
- */
- /** constructor */
- function ModeButton( element, options ){
- this.currModeIndex = 0;
- return this._init( element, options );
- }
-
- /** html5 data key to store this object inside an element */
- ModeButton.prototype.DATA_KEY = 'mode-button';
- /** default options */
- ModeButton.prototype.defaults = {
- switchModesOnClick : true
- };
-
- // ---- private interface
- /** set up options, intial mode, and the click handler */
- ModeButton.prototype._init = function _init( element, options ){
- //console.debug( 'ModeButton._init:', element, options );
- options = options || {};
- this.$element = $( element );
- this.options = jQuery.extend( true, {}, this.defaults, options );
- if( !options.modes ){
- throw new Error( 'ModeButton requires a "modes" array' );
- }
-
- var modeButton = this;
- this.$element.click( function _ModeButtonClick( event ){
- // call the curr mode fn
- modeButton.callModeFn();
- // inc the curr mode index
- if( modeButton.options.switchModesOnClick ){ modeButton._incModeIndex(); }
- // set the element html
- $( this ).html( modeButton.options.modes[ modeButton.currModeIndex ].html );
- });
- return this.reset();
- };
- /** increment the mode index to the next in the array, looping back to zero if at the last */
- ModeButton.prototype._incModeIndex = function _incModeIndex(){
- this.currModeIndex += 1;
- if( this.currModeIndex >= this.options.modes.length ){
- this.currModeIndex = 0;
- }
- return this;
- };
- /** get the mode index in the modes array for the given key (mode name) */
- ModeButton.prototype._getModeIndex = function _getModeIndex( modeKey ){
- for( var i=0; i<this.options.modes.length; i+=1 ){
- if( this.options.modes[ i ].mode === modeKey ){ return i; }
- }
- throw new Error( 'mode not found: ' + modeKey );
- };
- /** set the current mode to the one with the given index and set button html */
- ModeButton.prototype._setModeByIndex = function _setModeByIndex( index ){
- var newMode = this.options.modes[ index ];
- if( !newMode ){
- throw new Error( 'mode index not found: ' + index );
- }
- this.currModeIndex = index;
- if( newMode.html ){
- this.$element.html( newMode.html );
- }
- return this;
- };
-
- // ---- public interface
- /** get the current mode object (not just the mode name) */
- ModeButton.prototype.currentMode = function currentMode(){
- return this.options.modes[ this.currModeIndex ];
- };
- /** return the mode key of the current mode */
- ModeButton.prototype.current = function current(){
- // sugar for returning mode name
- return this.currentMode().mode;
- };
- /** get the mode with the given modeKey or the current mode if modeKey is undefined */
- ModeButton.prototype.getMode = function getMode( modeKey ){
- if( !modeKey ){ return this.currentMode(); }
- return this.options.modes[( this._getModeIndex( modeKey ) )];
- };
- /** T/F if the button has the given mode */
- ModeButton.prototype.hasMode = function hasMode( modeKey ){
- try {
- return !!this.getMode( modeKey );
- } catch( err ){}
- return false;
- };
- /** set the current mode to the mode with the given name */
- ModeButton.prototype.setMode = function setMode( modeKey ){
- return this._setModeByIndex( this._getModeIndex( modeKey ) );
- };
- /** reset to the initial mode */
- ModeButton.prototype.reset = function reset(){
- this.currModeIndex = 0;
- if( this.options.initialMode ){
- this.currModeIndex = this._getModeIndex( this.options.initialMode );
- }
- return this._setModeByIndex( this.currModeIndex );
- };
- /** manually call the click handler of the given mode */
- ModeButton.prototype.callModeFn = function callModeFn( modeKey ){
- var modeFn = this.getMode( modeKey ).onclick;
- if( modeFn && jQuery.type( modeFn === 'function' ) ){
- // call with the element as context (std jquery pattern)
- return modeFn.call( this.$element.get(0) );
- }
- return undefined;
- };
-
- // as jq plugin
- jQuery.fn.extend({
- modeButton : function $modeButton( options ){
- if( !this.size() ){ return this; }
-
- //TODO: does map still work with jq multi selection (i.e. $( '.class-for-many-btns' ).modeButton)?
- if( jQuery.type( options ) === 'object' ){
- return this.map( function(){
- var $this = $( this );
- $this.data( 'mode-button', new ModeButton( $this, options ) );
- return this;
- });
- }
-
- var $first = $( this[0] ),
- button = $first.data( 'mode-button' );
-
- if( !button ){
- throw new Error( 'modeButton needs an options object or string name of a function' );
- }
-
- if( button && jQuery.type( options ) === 'string' ){
- var fnName = options;
- if( button && jQuery.type( button[ fnName ] ) === 'function' ){
- return button[ fnName ].apply( button, jQuery.makeArray( arguments ).slice( 1 ) );
- }
- }
- return button;
- }
- });
-}());
diff -r 0fb0c56af3cf25bb8c3bb366d34c6aedcbb9df14 -r 8e98c3d9b9a8bc3cabff60c67367fbaf5b328320 static/scripts/packed/jq-plugins/ui/mode-button.js
--- /dev/null
+++ b/static/scripts/packed/jq-plugins/ui/mode-button.js
@@ -0,0 +1,1 @@
+(function(a){if(typeof define==="function"&&define.amd){define([],a)}else{a(jQuery)}}(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=$.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&&$.type(o==="function")){return o.call(this.$element.get(0))}return undefined};$.fn.modeButton=function k(n){if(!this.size()){return this}if($.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&&$.type(n)==="string"){var q=n;if(o&&$.type(o[q])==="function"){return o[q].apply(o,$.makeArray(arguments).slice(1))}}return o}}));
\ No newline at end of file
diff -r 0fb0c56af3cf25bb8c3bb366d34c6aedcbb9df14 -r 8e98c3d9b9a8bc3cabff60c67367fbaf5b328320 static/scripts/packed/mvc/history/multi-panel.js
--- a/static/scripts/packed/mvc/history/multi-panel.js
+++ b/static/scripts/packed/mvc/history/multi-panel.js
@@ -1,1 +1,1 @@
-define(["mvc/history/history-model","mvc/history/history-panel-edit","mvc/base-mvc","utils/ajax-queue"],function(d,l,z,a){window.HISTORY_MODEL=d;function g(H,E){E=E||{};if(!(Galaxy&&Galaxy.modal)){return H.copy()}var F=H.get("name"),C="Copy of '"+F+"'";function D(J){if(!J){if(!Galaxy.modal.$("#invalid-title").size()){var I=$("<p/>").attr("id","invalid-title").css({color:"red","margin-top":"8px"}).addClass("bg-danger").text(_l("Please enter a valid history title"));Galaxy.modal.$(".modal-body").append(I)}return false}return J}function G(I){var J=$('<p><span class="fa fa-spinner fa-spin"></span> Copying history...</p>').css("margin-top","8px");Galaxy.modal.$(".modal-body").append(J);H.copy(true,I).fail(function(){alert(_l("History could not be copied. Please contact a Galaxy administrator"))}).always(function(){Galaxy.modal.hide()})}Galaxy.modal.show(_.extend({title:_l("Copying history")+' "'+F+'"',body:$(['<label for="copy-modal-title">',_l("Enter a title for the copied history"),":","</label><br />",'<input id="copy-modal-title" class="form-control" style="width: 100%" value="',C,'" />'].join("")),buttons:{Cancel:function(){Galaxy.modal.hide()},Copy:function(){var I=Galaxy.modal.$("#copy-modal-title").val();if(!D(I)){return}G(I)}}},E));$("#copy-modal-title").focus().select()}var B=Backbone.View.extend(z.LoggableMixin).extend({tagName:"div",className:"history-column flex-column flex-row-container",id:function q(){if(!this.model){return""}return"history-column-"+this.model.get("id")},initialize:function c(C){C=C||{};this.panel=C.panel||this.createPanel(C);this.setUpListeners()},createPanel:function u(D){D=_.extend({model:this.model,dragItems:true},D);var C=new l.HistoryPanelEdit(D);C._renderEmptyMessage=this.__patch_renderEmptyMessage;return C},__patch_renderEmptyMessage:function(E){var D=this,F=_.chain(this.model.get("state_ids")).values().flatten().value().length,C=D.$emptyMessage(E);if(!_.isEmpty(D.hdaViews)){C.hide()}else{if(F&&!this.model.contents.length){C.empty().append($('<span class="fa fa-spinner fa-spin"></span><i>loading datasets...</i>')).show()}else{if(D.searchFor){C.text(D.noneFoundMsg).show()}else{C.text(D.emptyMsg).show()}}}return C},setUpListeners:function f(){var C=this;this.once("rendered",function(){C.trigger("rendered:initial",C)});this.setUpPanelListeners()},setUpPanelListeners:function k(){var C=this;this.listenTo(this.panel,{rendered:function(){C.trigger("rendered",C)}},this)},inView:function(C,D){var F=this.$el.offset().left,E=F+this.$el.width();if(E<C){return false}if(F>D){return false}return true},$panel:function e(){return this.$(".history-panel")},render:function A(D){D=(D!==undefined)?(D):("fast");var C=this.model?this.model.toJSON():{};this.$el.html(this.template(C));this.renderPanel(D);this.setUpBehaviors();return this},setUpBehaviors:function v(){},template:function w(D){D=D||{};var C=['<div class="panel-controls clear flex-row">',this.controlsLeftTemplate(),'<div class="pull-right">','<button class="delete-history btn btn-default">',D.deleted?_l("Undelete"):_l("Delete"),"</button>",'<button class="copy-history btn btn-default">',_l("Copy"),"</button>","</div>","</div>",'<div class="inner flex-row flex-column-container">','<div id="history-',D.id,'" class="history-column history-panel flex-column"></div>',"</div>"].join("");return $(C)},controlsLeftTemplate:function(){return(this.currentHistory)?['<div class="pull-left">','<button class="create-new btn btn-default">',_l("Create new"),"</button> ","</div>"].join(""):['<div class="pull-left">','<button class="switch-to btn btn-default">',_l("Switch to"),"</button>","</div>"].join("")},renderPanel:function h(C){C=(C!==undefined)?(C):("fast");this.panel.setElement(this.$panel()).render(C);return this},events:{"click .switch-to.btn":function(){this.model.setAsCurrent()},"click .delete-history.btn":function(){var C=this,D;if(this.model.get("deleted")){D=this.model.undelete()}else{D=this.model._delete()}D.fail(function(G,E,F){alert(_l("Could not delete the history")+":\n"+F)}).done(function(E){C.render()})},"click .copy-history.btn":"copy"},copy:function s(){g(this.model)},toString:function(){return"HistoryPanelColumn("+(this.panel?this.panel:"")+")"}});var m=Backbone.View.extend(z.LoggableMixin).extend({initialize:function c(C){C=C||{};this.log(this+".init",C);if(!C.currentHistoryId){throw new Error(this+" requires a currentHistoryId in the options")}this.currentHistoryId=C.currentHistoryId;this.options={columnWidth:312,borderWidth:1,columnGap:8,headerHeight:29,footerHeight:0,controlsHeight:20};this.order=C.order||"update";this.hdaQueue=new a.NamedAjaxQueue([],false);this.collection=null;this.setCollection(C.histories||[]);this.columnMap={};this.createColumns(C.columnOptions);this.setUpListeners()},setUpListeners:function f(){},setCollection:function y(D){var C=this;C.stopListening(C.collection);C.collection=D;C.sortCollection(C.order,{silent:true});C.setUpCollectionListeners();C.trigger("new-collection",C);return C},setUpCollectionListeners:function(){var C=this,D=C.collection;C.listenTo(D,{add:C.addAsCurrentColumn,"set-as-current":C.setCurrentHistory,"change:deleted":C.handleDeletedHistory,sort:function(){C.renderColumns(0)}})},setCurrentHistory:function p(D){var C=this.columnMap[this.currentHistoryId];if(C){C.currentHistory=false;C.$el.height("")}this.currentHistoryId=D.id;var E=this.columnMap[this.currentHistoryId];E.currentHistory=true;this.sortCollection();multipanel._recalcFirstColumnHeight();return E},handleDeletedHistory:function b(D){if(D.get("deleted")){this.log("handleDeletedHistory",this.collection.includeDeleted,D);var C=this;column=C.columnMap[D.id];if(!column){return}if(column.model.id===this.currentHistoryId){}else{if(!C.collection.includeDeleted){C.removeColumn(column)}}}},sortCollection:function(C,D){C=C||this.order;var E=this.currentHistoryId;switch(C){case"name":this.collection.comparator=function(F){return[F.id!==E,F.get("name").toLowerCase()]};break;case"size":this.collection.comparator=function(F){return[F.id!==E,F.get("size")]};break;default:this.collection.comparator=function(F){return[F.id!==E,Date(F.get("update_time"))]}}this.collection.sort(D);return this.collection},setOrder:function(C){if(["update","name","size"].indexOf(C)===-1){C="update"}this.order=C;this.sortCollection();return this},create:function(C){return this.collection.create({current:true})},createColumns:function r(D){D=D||{};var C=this;this.columnMap={};C.collection.each(function(E,F){var G=C.createColumn(E,D);C.columnMap[E.id]=G})},createColumn:function t(E,C){C=_.extend({},C,{model:E});var D=new B(C);if(E.id===this.currentHistoryId){D.currentHistory=true}this.setUpColumnListeners(D);return D},sortedFilteredColumns:function(C){C=C||this.filters;if(!C||!C.length){return this.sortedColumns()}var D=this;return D.sortedColumns().filter(function(G,F){var E=G.currentHistory||_.every(C.map(function(H){return H.call(G)}));return E})},sortedColumns:function(){var D=this;var C=this.collection.map(function(F,E){return D.columnMap[F.id]});return C},addColumn:function o(E,C){C=C!==undefined?C:true;var D=this.createColumn(E);this.columnMap[E.id]=D;if(C){this.renderColumns()}return D},addAsCurrentColumn:function o(E){var D=this,C=this.addColumn(E,false);this.setCurrentHistory(E);C.once("rendered",function(){D.queueHdaFetch(C)});return C},removeColumn:function x(E,D){D=D!==undefined?D:true;this.log("removeColumn",E);if(!E){return}var F=this,C=this.options.columnWidth+this.options.columnGap;E.$el.fadeOut("fast",function(){if(D){$(this).remove();F.$(".middle").width(F.$(".middle").width()-C);F.checkColumnsInView();F._recalcFirstColumnHeight()}F.stopListening(E.panel);F.stopListening(E);delete F.columnMap[E.model.id];E.remove()})},setUpColumnListeners:function n(C){var D=this;D.listenTo(C,{"in-view":D.queueHdaFetch});D.listenTo(C.panel,{"view:draggable:dragstart":function(H,F,E,G){D._dropData=JSON.parse(H.dataTransfer.getData("text"));D.currentColumnDropTargetOn()},"view:draggable:dragend":function(H,F,E,G){D._dropData=null;D.currentColumnDropTargetOff()},"droptarget:drop":function(G,H,F){var I=D._dropData.filter(function(J){return(_.isObject(J)&&J.id&&J.model_class==="HistoryDatasetAssociation")});D._dropData=null;var E=new a.NamedAjaxQueue();I.forEach(function(J){E.add({name:"copy-"+J.id,fn:function(){return F.model.contents.copy(J.id)}})});E.start();E.done(function(J){F.model.fetch()})}})},columnMapLength:function(){return Object.keys(this.columnMap).length},render:function A(D){D=D!==undefined?D:this.fxSpeed;var C=this;C.log(C+".render");C.$el.html(C.template(C.options));C.renderColumns(D);C.setUpBehaviors();C.trigger("rendered",C);return C},template:function w(C){C=C||{};var D=[];if(this.options.headerHeight){D=D.concat(['<div class="loading-overlay flex-row"><div class="loading-overlay-message">loading...</div></div>','<div class="header flex-column-container">','<div class="header-control header-control-left flex-column">','<button class="done btn btn-default">',_l("Done"),"</button>",'<button class="include-deleted btn btn-default"></button>','<div class="order btn-group">','<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">',_l("Order histories by")+'... <span class="caret"></span>',"</button>",'<ul class="dropdown-menu" role="menu">','<li><a href="javascript:void(0);" class="order-update">',_l("Time of last update"),"</a></li>",'<li><a href="javascript:void(0);" class="order-name">',_l("Name"),"</a></li>",'<li><a href="javascript:void(0);" class="order-size">',_l("Size"),"</a></li>","</ul>","</div>",'<div id="search-histories" class="header-search"></div>',"</div>",'<div class="header-control header-control-center flex-column">','<div class="header-info">',"</div>","</div>",'<div class="header-control header-control-right flex-column">','<div id="search-datasets" class="header-search"></div>','<button id="toggle-deleted" class="btn btn-default">',_l("Include deleted datasets"),"</button>",'<button id="toggle-hidden" class="btn btn-default">',_l("Include hidden datasets"),"</button>","</div>","</div>"])}D=D.concat(['<div class="outer-middle flex-row flex-row-container">','<div class="middle flex-column-container flex-row"></div>',"</div>",'<div class="footer flex-column-container">',"</div>"]);return $(D.join(""))},renderColumns:function j(F){F=F!==undefined?F:this.fxSpeed;var E=this,C=E.sortedFilteredColumns();E.$(".middle").width(C.length*(this.options.columnWidth+this.options.columnGap)+this.options.columnGap+16);var D=E.$(".middle");D.empty();C.forEach(function(H,G){H.$el.appendTo(D);H.delegateEvents();E.renderColumn(H,F)});if(this.searchFor&&C.length<=1){}else{E.checkColumnsInView();this._recalcFirstColumnHeight()}return C},renderColumn:function(C,D){D=D!==undefined?D:this.fxSpeed;return C.render(D)},queueHdaFetch:function i(E){if(E.model.contents.length===0&&!E.model.get("empty")){var C={},D=_.values(E.panel.storage.get("expandedIds")).join();if(D){C.dataset_details=D}this.hdaQueue.add({name:E.model.id,fn:function(){var F=E.model.contents.fetch({data:C,silent:true});return F.done(function(G){E.panel.renderItems()})}});if(!this.hdaQueue.running){this.hdaQueue.start()}}},queueHdaFetchDetails:function(C){if((C.model.contents.length===0&&!C.model.get("empty"))||(!C.model.contents.haveDetails())){this.hdaQueue.add({name:C.model.id,fn:function(){var D=C.model.contents.fetch({data:{details:"all"},silent:true});return D.done(function(E){C.panel.renderItems()})}});if(!this.hdaQueue.running){this.hdaQueue.start()}}},renderInfo:function(C){this.$(".header .header-info").text(C)},events:{"click .done.btn":function(){window.location="/"},"click .create-new.btn":"create","click .order .order-update":function(C){this.setOrder("update")},"click .order .order-name":function(C){this.setOrder("name")},"click .order .order-size":function(C){this.setOrder("size")}},includeDeletedHistories:function(){window.location+=(/\?/.test(window.location.toString()))?("&"):("?")+"include_deleted_histories=True"},excludeDeletedHistories:function(){window.location=window.location.toString().replace(/[&\?]include_deleted_histories=True/g,"")},setUpBehaviors:function(){var D=this;D.$(".include-deleted").modeButton({initialMode:this.collection.includeDeleted?"exclude":"include",switchModesOnClick:false,modes:[{mode:"include",html:_l("Include deleted histories"),onclick:_.bind(D.includeDeletedHistories,D)},{mode:"exclude",html:_l("Exclude deleted histories"),onclick:_.bind(D.excludeDeletedHistories,D)}]});D.$("#search-histories").searchInput({name:"search-histories",placeholder:_l("search histories"),onsearch:function(E){D.searchFor=E;D.filters=[function(){return this.model.matchesAll(D.searchFor)}];D.renderColumns(0)},onclear:function(E){D.searchFor=null;D.filters=[];D.renderColumns(0)}});D.$("#search-datasets").searchInput({name:"search-datasets",placeholder:_l("search all datasets"),onfirstsearch:function(E){D.hdaQueue.clear();D.$("#search-datasets").searchInput("toggle-loading");D.searchFor=E;D.sortedFilteredColumns().forEach(function(F){F.panel.searchItems(E);D.queueHdaFetchDetails(F)});D.hdaQueue.progress(function(F){D.renderInfo([_l("searching"),(F.curr+1),_l("of"),F.total].join(" "))});D.hdaQueue.deferred.done(function(){D.renderInfo("");D.$("#search-datasets").searchInput("toggle-loading")})},onsearch:function(E){D.searchFor=E;D.sortedFilteredColumns().forEach(function(F){F.panel.searchItems(E)})},onclear:function(E){D.searchFor=null;D.sortedFilteredColumns().forEach(function(F){F.panel.clearSearch()})}});D.$("#toggle-deleted").modeButton({initialMode:"include",modes:[{mode:"exclude",html:_l("Exclude deleted datasets")},{mode:"include",html:_l("Include deleted datasets")}]}).click(function(){var E=$(this).modeButton("getMode").mode==="exclude";D.sortedFilteredColumns().forEach(function(G,F){_.delay(function(){G.panel.toggleShowDeleted(E,false)},F*200)})});D.$("#toggle-hidden").modeButton({initialMode:"include",modes:[{mode:"exclude",html:_l("Exclude hidden datasets")},{mode:"include",html:_l("Include hidden datasets")}]}).click(function(){var E=$(this).modeButton("getMode").mode==="exclude";D.sortedFilteredColumns().forEach(function(G,F){_.delay(function(){G.panel.toggleShowHidden(E,false)},F*200)})});$(window).resize(function(){D._recalcFirstColumnHeight()});var C=_.debounce(_.bind(this.checkColumnsInView,this),100);this.$(".middle").parent().scroll(C)},_recalcFirstColumnHeight:function(){var C=this.$(".history-column").first(),E=this.$(".middle").height(),D=C.find(".panel-controls").height();C.height(E).find(".inner").height(E-D)},_viewport:function(){var C=this.$(".middle").parent().offset().left;return{left:C,right:C+this.$(".middle").parent().width()}},columnsInView:function(){var C=this._viewport();return this.sortedFilteredColumns().filter(function(D){return D.currentHistory||D.inView(C.left,C.right)})},checkColumnsInView:function(){this.columnsInView().forEach(function(C){C.trigger("in-view",C)})},currentColumnDropTargetOn:function(){var C=this.columnMap[this.currentHistoryId];if(!C){return}C.panel.dataDropped=function(D){};C.panel.dropTargetOn()},currentColumnDropTargetOff:function(){var C=this.columnMap[this.currentHistoryId];if(!C){return}C.panel.dataDropped=l.HistoryPanelEdit.prototype.dataDrop;C.panel.dropTargetOff()},toString:function(){return"MultiPanelColumns("+(this.columns?this.columns.length:0)+")"}});return{MultiPanelColumns:m}});
\ No newline at end of file
+define(["mvc/history/history-model","mvc/history/history-panel-edit","mvc/base-mvc","utils/ajax-queue","jq-plugins/ui/mode-button"],function(d,l,z,a){window.HISTORY_MODEL=d;function g(H,E){E=E||{};if(!(Galaxy&&Galaxy.modal)){return H.copy()}var F=H.get("name"),C="Copy of '"+F+"'";function D(J){if(!J){if(!Galaxy.modal.$("#invalid-title").size()){var I=$("<p/>").attr("id","invalid-title").css({color:"red","margin-top":"8px"}).addClass("bg-danger").text(_l("Please enter a valid history title"));Galaxy.modal.$(".modal-body").append(I)}return false}return J}function G(I){var J=$('<p><span class="fa fa-spinner fa-spin"></span> Copying history...</p>').css("margin-top","8px");Galaxy.modal.$(".modal-body").append(J);H.copy(true,I).fail(function(){alert(_l("History could not be copied. Please contact a Galaxy administrator"))}).always(function(){Galaxy.modal.hide()})}Galaxy.modal.show(_.extend({title:_l("Copying history")+' "'+F+'"',body:$(['<label for="copy-modal-title">',_l("Enter a title for the copied history"),":","</label><br />",'<input id="copy-modal-title" class="form-control" style="width: 100%" value="',C,'" />'].join("")),buttons:{Cancel:function(){Galaxy.modal.hide()},Copy:function(){var I=Galaxy.modal.$("#copy-modal-title").val();if(!D(I)){return}G(I)}}},E));$("#copy-modal-title").focus().select()}var B=Backbone.View.extend(z.LoggableMixin).extend({tagName:"div",className:"history-column flex-column flex-row-container",id:function q(){if(!this.model){return""}return"history-column-"+this.model.get("id")},initialize:function c(C){C=C||{};this.panel=C.panel||this.createPanel(C);this.setUpListeners()},createPanel:function u(D){D=_.extend({model:this.model,dragItems:true},D);var C=new l.HistoryPanelEdit(D);C._renderEmptyMessage=this.__patch_renderEmptyMessage;return C},__patch_renderEmptyMessage:function(E){var D=this,F=_.chain(this.model.get("state_ids")).values().flatten().value().length,C=D.$emptyMessage(E);if(!_.isEmpty(D.hdaViews)){C.hide()}else{if(F&&!this.model.contents.length){C.empty().append($('<span class="fa fa-spinner fa-spin"></span><i>loading datasets...</i>')).show()}else{if(D.searchFor){C.text(D.noneFoundMsg).show()}else{C.text(D.emptyMsg).show()}}}return C},setUpListeners:function f(){var C=this;this.once("rendered",function(){C.trigger("rendered:initial",C)});this.setUpPanelListeners()},setUpPanelListeners:function k(){var C=this;this.listenTo(this.panel,{rendered:function(){C.trigger("rendered",C)}},this)},inView:function(C,D){var F=this.$el.offset().left,E=F+this.$el.width();if(E<C){return false}if(F>D){return false}return true},$panel:function e(){return this.$(".history-panel")},render:function A(D){D=(D!==undefined)?(D):("fast");var C=this.model?this.model.toJSON():{};this.$el.html(this.template(C));this.renderPanel(D);this.setUpBehaviors();return this},setUpBehaviors:function v(){},template:function w(D){D=D||{};var C=['<div class="panel-controls clear flex-row">',this.controlsLeftTemplate(),'<div class="pull-right">','<button class="delete-history btn btn-default">',D.deleted?_l("Undelete"):_l("Delete"),"</button>",'<button class="copy-history btn btn-default">',_l("Copy"),"</button>","</div>","</div>",'<div class="inner flex-row flex-column-container">','<div id="history-',D.id,'" class="history-column history-panel flex-column"></div>',"</div>"].join("");return $(C)},controlsLeftTemplate:function(){return(this.currentHistory)?['<div class="pull-left">','<button class="create-new btn btn-default">',_l("Create new"),"</button> ","</div>"].join(""):['<div class="pull-left">','<button class="switch-to btn btn-default">',_l("Switch to"),"</button>","</div>"].join("")},renderPanel:function h(C){C=(C!==undefined)?(C):("fast");this.panel.setElement(this.$panel()).render(C);return this},events:{"click .switch-to.btn":function(){this.model.setAsCurrent()},"click .delete-history.btn":function(){var C=this,D;if(this.model.get("deleted")){D=this.model.undelete()}else{D=this.model._delete()}D.fail(function(G,E,F){alert(_l("Could not delete the history")+":\n"+F)}).done(function(E){C.render()})},"click .copy-history.btn":"copy"},copy:function s(){g(this.model)},toString:function(){return"HistoryPanelColumn("+(this.panel?this.panel:"")+")"}});var m=Backbone.View.extend(z.LoggableMixin).extend({initialize:function c(C){C=C||{};this.log(this+".init",C);if(!C.currentHistoryId){throw new Error(this+" requires a currentHistoryId in the options")}this.currentHistoryId=C.currentHistoryId;this.options={columnWidth:312,borderWidth:1,columnGap:8,headerHeight:29,footerHeight:0,controlsHeight:20};this.order=C.order||"update";this.hdaQueue=new a.NamedAjaxQueue([],false);this.collection=null;this.setCollection(C.histories||[]);this.columnMap={};this.createColumns(C.columnOptions);this.setUpListeners()},setUpListeners:function f(){},setCollection:function y(D){var C=this;C.stopListening(C.collection);C.collection=D;C.sortCollection(C.order,{silent:true});C.setUpCollectionListeners();C.trigger("new-collection",C);return C},setUpCollectionListeners:function(){var C=this,D=C.collection;C.listenTo(D,{add:C.addAsCurrentColumn,"set-as-current":C.setCurrentHistory,"change:deleted":C.handleDeletedHistory,sort:function(){C.renderColumns(0)}})},setCurrentHistory:function p(D){var C=this.columnMap[this.currentHistoryId];if(C){C.currentHistory=false;C.$el.height("")}this.currentHistoryId=D.id;var E=this.columnMap[this.currentHistoryId];E.currentHistory=true;this.sortCollection();multipanel._recalcFirstColumnHeight();return E},handleDeletedHistory:function b(D){if(D.get("deleted")){this.log("handleDeletedHistory",this.collection.includeDeleted,D);var C=this;column=C.columnMap[D.id];if(!column){return}if(column.model.id===this.currentHistoryId){}else{if(!C.collection.includeDeleted){C.removeColumn(column)}}}},sortCollection:function(C,D){C=C||this.order;var E=this.currentHistoryId;switch(C){case"name":this.collection.comparator=function(F){return[F.id!==E,F.get("name").toLowerCase()]};break;case"size":this.collection.comparator=function(F){return[F.id!==E,F.get("size")]};break;default:this.collection.comparator=function(F){return[F.id!==E,Date(F.get("update_time"))]}}this.collection.sort(D);return this.collection},setOrder:function(C){if(["update","name","size"].indexOf(C)===-1){C="update"}this.order=C;this.sortCollection();return this},create:function(C){return this.collection.create({current:true})},createColumns:function r(D){D=D||{};var C=this;this.columnMap={};C.collection.each(function(E,F){var G=C.createColumn(E,D);C.columnMap[E.id]=G})},createColumn:function t(E,C){C=_.extend({},C,{model:E});var D=new B(C);if(E.id===this.currentHistoryId){D.currentHistory=true}this.setUpColumnListeners(D);return D},sortedFilteredColumns:function(C){C=C||this.filters;if(!C||!C.length){return this.sortedColumns()}var D=this;return D.sortedColumns().filter(function(G,F){var E=G.currentHistory||_.every(C.map(function(H){return H.call(G)}));return E})},sortedColumns:function(){var D=this;var C=this.collection.map(function(F,E){return D.columnMap[F.id]});return C},addColumn:function o(E,C){C=C!==undefined?C:true;var D=this.createColumn(E);this.columnMap[E.id]=D;if(C){this.renderColumns()}return D},addAsCurrentColumn:function o(E){var D=this,C=this.addColumn(E,false);this.setCurrentHistory(E);C.once("rendered",function(){D.queueHdaFetch(C)});return C},removeColumn:function x(E,D){D=D!==undefined?D:true;this.log("removeColumn",E);if(!E){return}var F=this,C=this.options.columnWidth+this.options.columnGap;E.$el.fadeOut("fast",function(){if(D){$(this).remove();F.$(".middle").width(F.$(".middle").width()-C);F.checkColumnsInView();F._recalcFirstColumnHeight()}F.stopListening(E.panel);F.stopListening(E);delete F.columnMap[E.model.id];E.remove()})},setUpColumnListeners:function n(C){var D=this;D.listenTo(C,{"in-view":D.queueHdaFetch});D.listenTo(C.panel,{"view:draggable:dragstart":function(H,F,E,G){D._dropData=JSON.parse(H.dataTransfer.getData("text"));D.currentColumnDropTargetOn()},"view:draggable:dragend":function(H,F,E,G){D._dropData=null;D.currentColumnDropTargetOff()},"droptarget:drop":function(G,H,F){var I=D._dropData.filter(function(J){return(_.isObject(J)&&J.id&&J.model_class==="HistoryDatasetAssociation")});D._dropData=null;var E=new a.NamedAjaxQueue();I.forEach(function(J){E.add({name:"copy-"+J.id,fn:function(){return F.model.contents.copy(J.id)}})});E.start();E.done(function(J){F.model.fetch()})}})},columnMapLength:function(){return Object.keys(this.columnMap).length},render:function A(D){D=D!==undefined?D:this.fxSpeed;var C=this;C.log(C+".render");C.$el.html(C.template(C.options));C.renderColumns(D);C.setUpBehaviors();C.trigger("rendered",C);return C},template:function w(C){C=C||{};var D=[];if(this.options.headerHeight){D=D.concat(['<div class="loading-overlay flex-row"><div class="loading-overlay-message">loading...</div></div>','<div class="header flex-column-container">','<div class="header-control header-control-left flex-column">','<button class="done btn btn-default">',_l("Done"),"</button>",'<button class="include-deleted btn btn-default"></button>','<div class="order btn-group">','<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">',_l("Order histories by")+'... <span class="caret"></span>',"</button>",'<ul class="dropdown-menu" role="menu">','<li><a href="javascript:void(0);" class="order-update">',_l("Time of last update"),"</a></li>",'<li><a href="javascript:void(0);" class="order-name">',_l("Name"),"</a></li>",'<li><a href="javascript:void(0);" class="order-size">',_l("Size"),"</a></li>","</ul>","</div>",'<div id="search-histories" class="header-search"></div>',"</div>",'<div class="header-control header-control-center flex-column">','<div class="header-info">',"</div>","</div>",'<div class="header-control header-control-right flex-column">','<div id="search-datasets" class="header-search"></div>','<button id="toggle-deleted" class="btn btn-default">',_l("Include deleted datasets"),"</button>",'<button id="toggle-hidden" class="btn btn-default">',_l("Include hidden datasets"),"</button>","</div>","</div>"])}D=D.concat(['<div class="outer-middle flex-row flex-row-container">','<div class="middle flex-column-container flex-row"></div>',"</div>",'<div class="footer flex-column-container">',"</div>"]);return $(D.join(""))},renderColumns:function j(F){F=F!==undefined?F:this.fxSpeed;var E=this,C=E.sortedFilteredColumns();E.$(".middle").width(C.length*(this.options.columnWidth+this.options.columnGap)+this.options.columnGap+16);var D=E.$(".middle");D.empty();C.forEach(function(H,G){H.$el.appendTo(D);H.delegateEvents();E.renderColumn(H,F)});if(this.searchFor&&C.length<=1){}else{E.checkColumnsInView();this._recalcFirstColumnHeight()}return C},renderColumn:function(C,D){D=D!==undefined?D:this.fxSpeed;return C.render(D)},queueHdaFetch:function i(E){if(E.model.contents.length===0&&!E.model.get("empty")){var C={},D=_.values(E.panel.storage.get("expandedIds")).join();if(D){C.dataset_details=D}this.hdaQueue.add({name:E.model.id,fn:function(){var F=E.model.contents.fetch({data:C,silent:true});return F.done(function(G){E.panel.renderItems()})}});if(!this.hdaQueue.running){this.hdaQueue.start()}}},queueHdaFetchDetails:function(C){if((C.model.contents.length===0&&!C.model.get("empty"))||(!C.model.contents.haveDetails())){this.hdaQueue.add({name:C.model.id,fn:function(){var D=C.model.contents.fetch({data:{details:"all"},silent:true});return D.done(function(E){C.panel.renderItems()})}});if(!this.hdaQueue.running){this.hdaQueue.start()}}},renderInfo:function(C){this.$(".header .header-info").text(C)},events:{"click .done.btn":function(){window.location="/"},"click .create-new.btn":"create","click .order .order-update":function(C){this.setOrder("update")},"click .order .order-name":function(C){this.setOrder("name")},"click .order .order-size":function(C){this.setOrder("size")}},includeDeletedHistories:function(){window.location+=(/\?/.test(window.location.toString()))?("&"):("?")+"include_deleted_histories=True"},excludeDeletedHistories:function(){window.location=window.location.toString().replace(/[&\?]include_deleted_histories=True/g,"")},setUpBehaviors:function(){var D=this;D.$(".include-deleted").modeButton({initialMode:this.collection.includeDeleted?"exclude":"include",switchModesOnClick:false,modes:[{mode:"include",html:_l("Include deleted histories"),onclick:_.bind(D.includeDeletedHistories,D)},{mode:"exclude",html:_l("Exclude deleted histories"),onclick:_.bind(D.excludeDeletedHistories,D)}]});D.$("#search-histories").searchInput({name:"search-histories",placeholder:_l("search histories"),onsearch:function(E){D.searchFor=E;D.filters=[function(){return this.model.matchesAll(D.searchFor)}];D.renderColumns(0)},onclear:function(E){D.searchFor=null;D.filters=[];D.renderColumns(0)}});D.$("#search-datasets").searchInput({name:"search-datasets",placeholder:_l("search all datasets"),onfirstsearch:function(E){D.hdaQueue.clear();D.$("#search-datasets").searchInput("toggle-loading");D.searchFor=E;D.sortedFilteredColumns().forEach(function(F){F.panel.searchItems(E);D.queueHdaFetchDetails(F)});D.hdaQueue.progress(function(F){D.renderInfo([_l("searching"),(F.curr+1),_l("of"),F.total].join(" "))});D.hdaQueue.deferred.done(function(){D.renderInfo("");D.$("#search-datasets").searchInput("toggle-loading")})},onsearch:function(E){D.searchFor=E;D.sortedFilteredColumns().forEach(function(F){F.panel.searchItems(E)})},onclear:function(E){D.searchFor=null;D.sortedFilteredColumns().forEach(function(F){F.panel.clearSearch()})}});D.$("#toggle-deleted").modeButton({initialMode:"include",modes:[{mode:"exclude",html:_l("Exclude deleted datasets")},{mode:"include",html:_l("Include deleted datasets")}]}).click(function(){var E=$(this).modeButton("getMode").mode==="exclude";D.sortedFilteredColumns().forEach(function(G,F){_.delay(function(){G.panel.toggleShowDeleted(E,false)},F*200)})});D.$("#toggle-hidden").modeButton({initialMode:"include",modes:[{mode:"exclude",html:_l("Exclude hidden datasets")},{mode:"include",html:_l("Include hidden datasets")}]}).click(function(){var E=$(this).modeButton("getMode").mode==="exclude";D.sortedFilteredColumns().forEach(function(G,F){_.delay(function(){G.panel.toggleShowHidden(E,false)},F*200)})});$(window).resize(function(){D._recalcFirstColumnHeight()});var C=_.debounce(_.bind(this.checkColumnsInView,this),100);this.$(".middle").parent().scroll(C)},_recalcFirstColumnHeight:function(){var C=this.$(".history-column").first(),E=this.$(".middle").height(),D=C.find(".panel-controls").height();C.height(E).find(".inner").height(E-D)},_viewport:function(){var C=this.$(".middle").parent().offset().left;return{left:C,right:C+this.$(".middle").parent().width()}},columnsInView:function(){var C=this._viewport();return this.sortedFilteredColumns().filter(function(D){return D.currentHistory||D.inView(C.left,C.right)})},checkColumnsInView:function(){this.columnsInView().forEach(function(C){C.trigger("in-view",C)})},currentColumnDropTargetOn:function(){var C=this.columnMap[this.currentHistoryId];if(!C){return}C.panel.dataDropped=function(D){};C.panel.dropTargetOn()},currentColumnDropTargetOff:function(){var C=this.columnMap[this.currentHistoryId];if(!C){return}C.panel.dataDropped=l.HistoryPanelEdit.prototype.dataDrop;C.panel.dropTargetOff()},toString:function(){return"MultiPanelColumns("+(this.columns?this.columns.length:0)+")"}});return{MultiPanelColumns:m}});
\ No newline at end of file
diff -r 0fb0c56af3cf25bb8c3bb366d34c6aedcbb9df14 -r 8e98c3d9b9a8bc3cabff60c67367fbaf5b328320 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}})}());
\ 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)})}})}());
\ No newline at end of file
diff -r 0fb0c56af3cf25bb8c3bb366d34c6aedcbb9df14 -r 8e98c3d9b9a8bc3cabff60c67367fbaf5b328320 templates/webapps/galaxy/history/view.mako
--- a/templates/webapps/galaxy/history/view.mako
+++ b/templates/webapps/galaxy/history/view.mako
@@ -65,8 +65,7 @@
## ----------------------------------------------------------------------------
<%def name="javascripts()">
-${parent.javascripts()}
-
+ ${parent.javascripts()}
</%def>
## ----------------------------------------------------------------------------
@@ -144,7 +143,7 @@
window.location = "${ switch_to_url }";
}
});
-
+
$( '#import' ).modeButton({
switchModesOnClick : false,
initialMode : "${ 'with_deleted' if show_deleted else 'without_deleted' }",
@@ -175,8 +174,16 @@
({ location: 'mvc/history/history-panel', className: 'HistoryPanel' });
require.config({
- baseUrl : "${h.url_for( '/static/scripts' )}"
- })([ 'mvc/user/user-model', panelToUse.location, 'utils/localization' ], function( user, panelMod, _l ){
+ baseUrl : "${h.url_for( '/static/scripts' )}",
+ paths : {
+ 'jquery' : 'libs/jquery/jquery'
+ }
+ })([
+ 'mvc/user/user-model',
+ panelToUse.location,
+ 'utils/localization',
+ 'jq-plugins/ui/mode-button'
+ ], function( user, panelMod, _l ){
$(function(){
setUpBehaviors();
if( hasMasthead ){
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/39b03ce9d101/
Changeset: 39b03ce9d101
User: guerler
Date: 2014-12-08 20:34:11+00:00
Summary: Security fixes for assigned templates
Affected #: 4 files
diff -r 864fdca5b06e6f15040298a316746448d09cdb40 -r 39b03ce9d101f7177c51342172ba9bab540ee257 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 864fdca5b06e6f15040298a316746448d09cdb40 -r 39b03ce9d101f7177c51342172ba9bab540ee257 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 864fdca5b06e6f15040298a316746448d09cdb40 -r 39b03ce9d101f7177c51342172ba9bab540ee257 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 864fdca5b06e6f15040298a316746448d09cdb40 -r 39b03ce9d101f7177c51342172ba9bab540ee257 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/0fb0c56af3cf/
Changeset: 0fb0c56af3cf
User: guerler
Date: 2014-12-08 20:38:01+00:00
Summary: Parameters: Handle missing context
Affected #: 1 file
diff -r 39b03ce9d101f7177c51342172ba9bab540ee257 -r 0fb0c56af3cf25bb8c3bb366d34c6aedcbb9df14 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
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
[galaxyproject/usegalaxy-playbook] 9dbb49: Swapped event slides on landing page.
by GitHub 08 Dec '14
by GitHub 08 Dec '14
08 Dec '14
Branch: refs/heads/master
Home: https://github.com/galaxyproject/usegalaxy-playbook
Commit: 9dbb498b5b07dfa84454abab62fb540c65fbfebb
https://github.com/galaxyproject/usegalaxy-playbook/commit/9dbb498b5b07dfa8…
Author: Dave Clements <clements(a)Clements-Galaxy.local>
Date: 2014-12-08 (Mon, 08 Dec 2014)
Changed paths:
A files/galaxy/common/static/welcome_img/VSU.png
M templates/galaxy/common/static/welcome.html.j2
Log Message:
-----------
Swapped event slides on landing page.
1
0
commit/galaxy-central: dan: 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.
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/0317c3ec09d9/
Changeset: 0317c3ec09d9
Branch: next-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 caebda8c654aa079b68be736879df61c75f23faf -r 0317c3ec09d99ac40a9217a4725c6a2fe89c3de8 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 caebda8c654aa079b68be736879df61c75f23faf -r 0317c3ec09d99ac40a9217a4725c6a2fe89c3de8 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 caebda8c654aa079b68be736879df61c75f23faf -r 0317c3ec09d99ac40a9217a4725c6a2fe89c3de8 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,45 +149,44 @@
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
@@ -205,7 +204,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()
@@ -221,30 +220,29 @@
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 )
@@ -257,7 +255,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 )
@@ -269,16 +267,15 @@
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:
@@ -291,9 +288,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 (?)
@@ -325,7 +322,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
@@ -337,7 +334,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.
@@ -347,16 +344,15 @@
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:
@@ -364,11 +360,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'
@@ -385,7 +381,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 = []
@@ -405,16 +401,15 @@
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:
@@ -423,14 +418,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 )
@@ -442,7 +437,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
@@ -456,16 +451,15 @@
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:
@@ -474,7 +468,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 ]
@@ -498,26 +492,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'
@@ -530,12 +524,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
@@ -544,7 +538,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():
@@ -559,8 +553,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:
@@ -610,17 +604,16 @@
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 ) )
@@ -660,16 +653,15 @@
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 = []
@@ -693,7 +685,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
@@ -706,7 +698,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 )
@@ -763,8 +755,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 = []
@@ -789,7 +781,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",
@@ -799,32 +791,31 @@
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 = []
@@ -853,7 +844,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
@@ -878,7 +869,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.
@@ -892,7 +883,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
@@ -975,7 +966,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 = ''
@@ -990,7 +981,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
@@ -1049,8 +1040,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
@@ -1064,16 +1055,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', '' ]:
@@ -1102,7 +1092,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."
@@ -1113,9 +1103,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
@@ -1131,7 +1121,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 ]
@@ -1146,7 +1136,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
@@ -1162,8 +1152,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 )
@@ -1262,7 +1252,7 @@
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', '' ) == '':
@@ -1274,33 +1264,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 = []
@@ -1338,9 +1327,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 = []
@@ -1369,7 +1358,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
@@ -1437,12 +1426,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
@@ -1476,8 +1465,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,
@@ -1540,8 +1529,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:
@@ -1574,16 +1562,15 @@
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:
@@ -1591,11 +1578,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'
@@ -1624,16 +1611,15 @@
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:
@@ -1642,7 +1628,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():
@@ -1673,23 +1659,22 @@
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
@@ -1716,7 +1701,7 @@
use_panels=use_panels,
id=library_id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status=status ) )
@web.expose
@@ -1741,12 +1726,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'
@@ -1761,7 +1745,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
@@ -1832,7 +1816,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."
@@ -1993,11 +1977,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,
@@ -2007,8 +1991,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 ):
@@ -2018,12 +2002,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:
@@ -2042,7 +2025,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."
@@ -2106,11 +2089,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
@@ -2120,7 +2105,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,
@@ -2134,16 +2119,15 @@
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:
@@ -2162,7 +2146,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:
@@ -2181,7 +2165,7 @@
folder_id=folder_id,
id=id,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='done' ) )
@web.expose
@@ -2193,11 +2177,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()
@@ -2211,14 +2194,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:
@@ -2233,7 +2216,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'
@@ -2397,8 +2380,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 ):
@@ -2569,7 +2552,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',
@@ -2577,7 +2560,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 ):
@@ -2593,7 +2576,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 ):
@@ -2610,7 +2593,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 ) ):
@@ -2622,7 +2605,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 ):
@@ -2637,7 +2620,7 @@
id=library_id,
use_panels=use_panels,
show_deleted=show_deleted,
- message=util.sanitize_text( message ),
+ message=message,
status='error' ) )
# ---- Utility methods -------------------------------------------------------
@@ -2777,9 +2760,8 @@
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" ]
@@ -2789,9 +2771,8 @@
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 caebda8c654aa079b68be736879df61c75f23faf -r 0317c3ec09d99ac40a9217a4725c6a2fe89c3de8 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 caebda8c654aa079b68be736879df61c75f23faf -r 0317c3ec09d99ac40a9217a4725c6a2fe89c3de8 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 caebda8c654aa079b68be736879df61c75f23faf -r 0317c3ec09d99ac40a9217a4725c6a2fe89c3de8 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 caebda8c654aa079b68be736879df61c75f23faf -r 0317c3ec09d99ac40a9217a4725c6a2fe89c3de8 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 caebda8c654aa079b68be736879df61c75f23faf -r 0317c3ec09d99ac40a9217a4725c6a2fe89c3de8 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