diff --git a/.ci/py3_sources.txt b/.ci/py3_sources.txt deleted file mode 100644 index 9603644ed8d..00000000000 --- a/.ci/py3_sources.txt +++ /dev/null @@ -1,94 +0,0 @@ -client/galaxy/style/source_material/circle.py -contrib/ -cron/ -doc/parse_gx_xsd.py -doc/patch.py -lib/galaxy/actions/ -lib/galaxy/auth/ -lib/galaxy/config.py -lib/galaxy/containers/ -lib/galaxy/dataset_collections/ -lib/galaxy/datatypes/ -lib/galaxy/dependencies/ -lib/galaxy/eggs/ -lib/galaxy/exceptions/ -lib/galaxy/external_services/ -lib/galaxy/forms/ -lib/galaxy/jobs/ -lib/galaxy/managers/ -lib/galaxy/model/ -lib/galaxy/objectstore/ -lib/galaxy/openid/ -lib/galaxy/quota/ -lib/galaxy/sample_tracking/ -lib/galaxy/security/ -lib/galaxy/tags/ -lib/galaxy/tools/ -lib/galaxy/tours/ -lib/galaxy/util/ -lib/galaxy/visualization/ -lib/galaxy/web/base/ -lib/galaxy/web/buildapp.py -lib/galaxy/web/framework/base.py -lib/galaxy/web/framework/decorators.py -lib/galaxy/web/framework/helpers/grids.py -lib/galaxy/web/framework/__init__.py -lib/galaxy/web/framework/middleware/error.py -lib/galaxy/web/framework/middleware/static.py -lib/galaxy/web/framework/middleware/statsd.py -lib/galaxy/web/framework/webapp.py -lib/galaxy/web/__init__.py -lib/galaxy/webapps/galaxy/api/histories.py -lib/galaxy/webapps/galaxy/api/library_datasets.py -lib/galaxy/webapps/galaxy/api/tours.py -lib/galaxy/webapps/galaxy/api/users.py -lib/galaxy/webapps/galaxy/api/workflows.py -lib/galaxy/webapps/galaxy/buildapp.py -lib/galaxy/webapps/galaxy/controllers/admin_toolshed.py -lib/galaxy/webapps/galaxy/controllers/external_services.py -lib/galaxy/webapps/galaxy/controllers/forms.py -lib/galaxy/webapps/galaxy/controllers/history.py -lib/galaxy/webapps/galaxy/controllers/library_common.py -lib/galaxy/webapps/galaxy/controllers/search.py -lib/galaxy/webapps/galaxy/controllers/userskeys.py -lib/galaxy/webapps/reports/config.py -lib/galaxy/webapps/reports/controllers/tools.py -lib/galaxy/webapps/reports/__init__.py -lib/galaxy/webapps/tool_shed/config.py -lib/galaxy/webapps/tool_shed/controllers/groups.py -lib/galaxy/webapps/tool_shed/controllers/user.py -lib/galaxy/webapps/tool_shed/framework/middleware/remoteuser.py -lib/galaxy/webapps/tool_shed/__init__.py -lib/galaxy/webapps/tool_shed/model/__init__.py -lib/galaxy/webapps/tool_shed/search/repo_search.py -lib/galaxy/webapps/tool_shed/util/ratings_util.py -lib/galaxy/webhooks/ -lib/galaxy/work/ -lib/galaxy/workflow/ -lib/galaxy_ext/ -lib/log_tempfile.py -lib/mimeparse.py -lib/psyco_full.py -lib/tool_shed/ -scripts/api/ -scripts/auth/ -scripts/bootstrap_history.py -scripts/build_toolbox.py -scripts/check_eggs.py -scripts/check_galaxy.py -scripts/check_python.py -scripts/cleanup_datasets/ -scripts/communication/ -scripts/data_libraries/build_whoosh_index.py -scripts/db_shell.py -scripts/drmaa_external_runner.py -scripts/metagenomics/ -scripts/microbes/BeautifulSoup.py -scripts/microbes/harvest_bacteria.py -scripts/secret_decoder_ring.py -scripts/tool_shed/api/common.py -scripts/tool_shed/build_ts_whoosh_index.py -scripts/tool_shed/deprecate_repositories_without_metadata.py -test/ -tool_list.py -tools/ diff --git a/client/galaxy/scripts/apps/admin.js b/client/galaxy/scripts/apps/admin.js index 6d6f745e0f4..6ca5bf2a691 100644 --- a/client/galaxy/scripts/apps/admin.js +++ b/client/galaxy/scripts/apps/admin.js @@ -5,13 +5,13 @@ import GalaxyApp from "galaxy"; import AdminPanel from "./panels/admin-panel"; import FormWrapper from "mvc/form/form-wrapper"; import GridView from "mvc/grid/grid-view"; -import Ui from "mvc/ui/ui-misc"; import QueryStringParsing from "utils/query-string-parsing"; import Router from "layout/router"; import Utils from "utils/utils"; import Page from "layout/page"; -import Vue from "libs/vue"; -import UserAPIKeys from "components/UserAPIKeys.vue"; +import UserAPIKeys from "components/admin/UserAPIKeys.vue"; +import DataTables from "components/admin/DataTables.vue"; +import Vue from "vue"; window.app = function app(options, bootstrapped) { window.Galaxy = new GalaxyApp.GalaxyApp(options, bootstrapped); @@ -28,10 +28,11 @@ window.app = function app(options, bootstrapped) { "(/)admin(/)repositories": "show_repositories", "(/)admin(/)forms": "show_forms", "(/)admin(/)form(/)(:form_id)": "show_form", - "(/)admin/api_keys": "show_user_api_keys" + "(/)admin/api_keys": "show_user_api_keys", + "(/)admin/data_tables": "show_data_tables" }, - authenticate: function(args, name) { + authenticate: function() { return Galaxy.user && Galaxy.user.id && Galaxy.user.get("is_admin"); }, @@ -39,8 +40,7 @@ window.app = function app(options, bootstrapped) { this.page.display( new GridView({ url_base: `${Galaxy.root}admin/users_list`, - url_data: Galaxy.params, - dict_format: true + url_data: Galaxy.params }) ); }, @@ -49,8 +49,7 @@ window.app = function app(options, bootstrapped) { this.page.display( new GridView({ url_base: `${Galaxy.root}admin/roles_list`, - url_data: Galaxy.params, - dict_format: true + url_data: Galaxy.params }) ); }, @@ -59,8 +58,7 @@ window.app = function app(options, bootstrapped) { this.page.display( new GridView({ url_base: `${Galaxy.root}admin/groups_list`, - url_data: Galaxy.params, - dict_format: true + url_data: Galaxy.params }) ); }, @@ -69,8 +67,7 @@ window.app = function app(options, bootstrapped) { this.page.display( new GridView({ url_base: `${Galaxy.root}admin_toolshed/browse_repositories`, - url_data: Galaxy.params, - dict_format: true + url_data: Galaxy.params }) ); }, @@ -79,8 +76,7 @@ window.app = function app(options, bootstrapped) { this.page.display( new GridView({ url_base: `${Galaxy.root}admin/tool_versions_list`, - url_data: Galaxy.params, - dict_format: true + url_data: Galaxy.params }) ); }, @@ -89,24 +85,28 @@ window.app = function app(options, bootstrapped) { this.page.display( new GridView({ url_base: `${Galaxy.root}admin/quotas_list`, - url_data: Galaxy.params, - dict_format: true + url_data: Galaxy.params }) ); }, show_user_api_keys: function() { - var vuemount = document.createElement("div"); - this.page.display(vuemount); - new Vue(UserAPIKeys).$mount(vuemount); + var vueMount = document.createElement("div"); + this.page.display(vueMount); + new Vue(UserAPIKeys).$mount(vueMount); + }, + + show_data_tables: function() { + var vueMount = document.createElement("div"); + this.page.display(vueMount); + new Vue(DataTables).$mount(vueMount); }, show_forms: function() { this.page.display( new GridView({ url_base: `${Galaxy.root}forms/forms_list`, - url_data: Galaxy.params, - dict_format: true + url_data: Galaxy.params }) ); }, diff --git a/client/galaxy/scripts/apps/analysis.js b/client/galaxy/scripts/apps/analysis.js index 08d3d12f16c..db946e372e8 100644 --- a/client/galaxy/scripts/apps/analysis.js +++ b/client/galaxy/scripts/apps/analysis.js @@ -22,7 +22,7 @@ import Ui from "mvc/ui/ui-misc"; import DatasetError from "mvc/dataset/dataset-error"; import DatasetEditAttributes from "mvc/dataset/dataset-edit-attributes"; import Citations from "components/Citations.vue"; -import Vue from "libs/vue"; +import Vue from "vue"; /** define the 'Analyze Data'/analysis/main/home page for Galaxy * * has a masthead @@ -47,6 +47,7 @@ window.app = function app(options, bootstrapped) { "(/)tours(/)(:tour_id)": "show_tours", "(/)user(/)": "show_user", "(/)user(/)(:form_id)": "show_user_form", + "(/)openids(/)list": "show_openids", "(/)pages(/)create(/)": "show_pages_create", "(/)pages(/)edit(/)": "show_pages_edit", "(/)pages(/)(:action_id)": "show_pages", @@ -115,8 +116,7 @@ window.app = function app(options, bootstrapped) { show_workflows_published: function() { this.page.display( new GridView({ - url_base: `${Galaxy.root}workflow/list_published`, - dict_format: true + url_base: `${Galaxy.root}workflow/list_published` }) ); }, @@ -157,11 +157,18 @@ window.app = function app(options, bootstrapped) { ); }, + show_openids: function() { + this.page.display( + new GridView({ + url_base: `${Galaxy.root}user/openids_list` + }) + ); + }, + show_datasets: function() { this.page.display( new GridView({ - url_base: `${Galaxy.root}dataset/list`, - dict_format: true + url_base: `${Galaxy.root}dataset/list` }) ); }, diff --git a/client/galaxy/scripts/apps/panels/admin-panel.js b/client/galaxy/scripts/apps/panels/admin-panel.js index 86ee64fd9ea..de90612c750 100644 --- a/client/galaxy/scripts/apps/panels/admin-panel.js +++ b/client/galaxy/scripts/apps/panels/admin-panel.js @@ -22,7 +22,8 @@ var AdminPanel = Backbone.View.extend({ }, { title: _l("Data tables"), - url: "admin/view_tool_data_tables" + url: "admin/data_tables", + target: "__use_router__" }, { title: _l("Display applications"), diff --git a/client/galaxy/scripts/components/Message.vue b/client/galaxy/scripts/components/Message.vue new file mode 100644 index 00000000000..dced6c62e5c --- /dev/null +++ b/client/galaxy/scripts/components/Message.vue @@ -0,0 +1,20 @@ + + + + diff --git a/client/galaxy/scripts/components/admin/BaseGrid.vue b/client/galaxy/scripts/components/admin/BaseGrid.vue new file mode 100644 index 00000000000..d0dc0bc69c2 --- /dev/null +++ b/client/galaxy/scripts/components/admin/BaseGrid.vue @@ -0,0 +1,43 @@ + + + diff --git a/client/galaxy/scripts/components/admin/DataManagerGrid.vue b/client/galaxy/scripts/components/admin/DataManagerGrid.vue new file mode 100644 index 00000000000..50ce7e74c4c --- /dev/null +++ b/client/galaxy/scripts/components/admin/DataManagerGrid.vue @@ -0,0 +1,47 @@ + + + diff --git a/client/galaxy/scripts/components/admin/DataTables.vue b/client/galaxy/scripts/components/admin/DataTables.vue new file mode 100644 index 00000000000..02c1fd6b76b --- /dev/null +++ b/client/galaxy/scripts/components/admin/DataTables.vue @@ -0,0 +1,117 @@ + + + diff --git a/client/galaxy/scripts/components/admin/DataTablesGrid.vue b/client/galaxy/scripts/components/admin/DataTablesGrid.vue new file mode 100644 index 00000000000..a8381edbb86 --- /dev/null +++ b/client/galaxy/scripts/components/admin/DataTablesGrid.vue @@ -0,0 +1,53 @@ + + + diff --git a/client/galaxy/scripts/components/UserAPIKeys.vue b/client/galaxy/scripts/components/admin/UserAPIKeys.vue similarity index 100% rename from client/galaxy/scripts/components/UserAPIKeys.vue rename to client/galaxy/scripts/components/admin/UserAPIKeys.vue diff --git a/client/galaxy/scripts/galaxy.interactive_environments.js b/client/galaxy/scripts/galaxy.interactive_environments.js index 19f41e02f54..e17b3a62835 100644 --- a/client/galaxy/scripts/galaxy.interactive_environments.js +++ b/client/galaxy/scripts/galaxy.interactive_environments.js @@ -6,9 +6,7 @@ export function append_notebook(url) { clear_main_area(); $("#main").append( - `` + `` ); } @@ -21,9 +19,7 @@ export function clear_main_area() { export function display_spinner() { $("#main").append( - `` + `` ); } diff --git a/client/galaxy/scripts/galaxy.pages.js b/client/galaxy/scripts/galaxy.pages.js index 3075f8d4fa7..d5cca10e0cb 100644 --- a/client/galaxy/scripts/galaxy.pages.js +++ b/client/galaxy/scripts/galaxy.pages.js @@ -156,11 +156,7 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm } show_modal( "Create Link", - `


`, + `


`, { "Make link": function() { // Get URL, name/title. @@ -334,7 +330,6 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm var grid = new GridView({ url_base: item_info.list_ajax_url, - dict_format: true, embedded: true }); Galaxy.modal.show({ @@ -428,7 +423,6 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm var grid = new GridView({ url_base: item_info.list_ajax_url, - dict_format: true, embedded: true }); Galaxy.modal.show({ diff --git a/client/galaxy/scripts/layout/modal.js b/client/galaxy/scripts/layout/modal.js index d359f59fef5..4f8120f29a7 100644 --- a/client/galaxy/scripts/layout/modal.js +++ b/client/galaxy/scripts/layout/modal.js @@ -154,9 +154,9 @@ function show_in_overlay(options) { closeButton: true, title: " ", body: $( - `
` + `
` ) }); modal.show({ backdrop: true }); diff --git a/client/galaxy/scripts/legacy/grid/grid-model.js b/client/galaxy/scripts/legacy/grid/grid-model.js new file mode 100644 index 00000000000..f25a4313abe --- /dev/null +++ b/client/galaxy/scripts/legacy/grid/grid-model.js @@ -0,0 +1,114 @@ +// legacy grid model, used by reports and toolshed +export default Backbone.Model.extend({ + defaults: { + url_base: "", + async: false, + async_ops: [], + categorical_filters: [], + filters: {}, + sort_key: null, + show_item_checkboxes: false, + advanced_search: false, + cur_page: 1, + num_pages: 1, + operation: undefined, + item_ids: undefined + }, + + /** + * Return true if operation can be done asynchronously. + */ + can_async_op: function(op) { + return _.indexOf(this.attributes.async_ops, op) !== -1; + }, + + /** + * Add filtering criterion. + */ + add_filter: function(key, value, append) { + // Update URL arg with new condition. + if (append) { + // Update or append value. + var cur_val = this.attributes.filters[key]; + + var new_val; + if (cur_val === null || cur_val === undefined) { + new_val = value; + } else if (typeof cur_val == "string") { + if (cur_val == "All" || cur_val == value) { + new_val = value; + } else { + // Replace string with array. + var values = []; + values[0] = cur_val; + values[1] = value; + new_val = values; + } + } else { + // Current value is an array. + new_val = cur_val; + if (new_val.indexOf(value) === -1) { + new_val.push(value); + } + } + this.attributes.filters[key] = new_val; + } else { + // Replace value. + this.attributes.filters[key] = value; + } + }, + + /** + * Remove filtering criterion. + */ + remove_filter: function(key, condition) { + var cur_val = this.attributes.filters[key]; + if (cur_val === null || cur_val === undefined) { + return false; + } + + if (typeof cur_val === "string") { + // overwrite/remove condition. + this.attributes.filters[key] = ""; + } else { + // filter contains an array of conditions. + var condition_index = _.indexOf(cur_val, condition); + if (condition_index !== -1) { + cur_val[condition_index] = ""; + } + } + }, + + /** + * Returns URL data for obtaining a new grid. + */ + get_url_data: function() { + var url_data = { + async: this.attributes.async, + sort: this.attributes.sort_key, + page: this.attributes.cur_page, + show_item_checkboxes: this.attributes.show_item_checkboxes, + advanced_search: this.attributes.advanced_search + }; + + // Add operation, item_ids only if they have values. + if (this.attributes.operation) { + url_data.operation = this.attributes.operation; + } + if (this.attributes.item_ids) { + url_data.id = this.attributes.item_ids; + } + + // Add filter arguments to data, placing "f-" in front of all arguments. + var self = this; + _.each(_.pairs(self.attributes.filters), k => { + url_data[`f-${k[0]}`] = k[1]; + }); + return url_data; + }, + + // Return URL for obtaining a new grid + get_url: function(args) { + return `${this.get("url_base")}?${$.param(this.get_url_data())}&${$.param(args)}`; + } +}); diff --git a/client/galaxy/scripts/legacy/grid/grid-template.js b/client/galaxy/scripts/legacy/grid/grid-template.js new file mode 100644 index 00000000000..e5fda7d790f --- /dev/null +++ b/client/galaxy/scripts/legacy/grid/grid-template.js @@ -0,0 +1,547 @@ +// dependencies +import Utils from "utils/utils"; +// grid view templates +export default { + // template + grid: function(options) { + var tmpl = ""; + if (options.embedded) { + tmpl = this.grid_header(options) + this.grid_table(options); + } else { + tmpl = `
${this.grid_header( + options + )}
${this.grid_table( + options + )}`; + } + + // add info text + if (options.info_text) { + tmpl += `
${options.info_text}
`; + } + + // return + return tmpl; + }, + + // template + grid_table: function() { + return ` +
+ + + + +
+
`; + }, + + // template + grid_header: function(options) { + var tmpl = '
'; + if (!options.embedded) { + tmpl += `

${options.title}

`; + } + if (options.global_actions) { + tmpl += '
    '; + var show_popup = options.global_actions.length >= 3; + if (show_popup) { + tmpl += + '
  • Actions
  • ' + + '
    '; + } + for (let action of options.global_actions) { + tmpl += `
  • ${action.label}
  • `; + } + if (show_popup) { + tmpl += "
    "; + } + tmpl += "
"; + } + if (options.insert) { + tmpl += options.insert; + } + + // add grid filters + tmpl += this.grid_filters(options); + tmpl += "
"; + + // return template + return tmpl; + }, + + // template + header: function(options) { + // start + var tmpl = ""; + + // add checkbox + if (options.show_item_checkboxes) { + tmpl += ""; + if (options.items.length > 0) { + tmpl += + '' + + ''; + } + tmpl += ""; + } + + // create header elements + for (let column of options.columns) { + if (column.visible) { + tmpl += ``; + if (column.href) { + tmpl += `${column.label}`; + } else { + tmpl += column.label; + } + tmpl += `${column.extra}`; + } + } + + // finalize + tmpl += ""; + + // return template + return tmpl; + }, + + // template + body: function(options) { + // initialize + var tmpl = ""; + var items_length = options.items.length; + + // empty grid? + if (items_length === 0) { + // No results. + tmpl += 'No Items'; + } + + // create rows + for (let item of options.items) { + // Tag current + tmpl += "`; + } + + // Data columns + for (let column of options.columns) { + if (column.visible) { + // Nowrap + var nowrap = ""; + if (column.nowrap) { + nowrap = 'style="white-space:nowrap;"'; + } + + // get column settings + var column_settings = item.column_config[column.label]; + + // load attributes + var link = column_settings.link; + var value = column_settings.value; + var target = column_settings.target; + + // unescape value + if (jQuery.type(value) === "string") { + value = value.replace(/\/\//g, "/"); + } + + // Attach popup menu? + var id = ""; + var cls = ""; + if (column.attach_popup) { + id = `grid-${item.encode_id}-popup`; + cls = "menubutton"; + if (link !== "") { + cls += " split"; + } + cls += " popup"; + } + + // Check for row wrapping + tmpl += ``; + + // Link + if (link) { + if (options.operations.length !== 0) { + tmpl += `
`; + } + tmpl += `${value}`; + if (options.operations.length !== 0) { + tmpl += "
"; + } + } else { + tmpl += `
`; + } + tmpl += ""; + } + } + tmpl += ""; + } + return tmpl; + }, + + // template + footer: function(options) { + // create template string + var tmpl = ""; + + // paging + if (options.use_paging && options.num_pages > 1) { + // get configuration + var num_page_links = options.num_page_links; + var cur_page_num = options.cur_page_num; + var num_pages = options.num_pages; + + // First pass on min page. + var page_link_range = num_page_links / 2; + var min_page = cur_page_num - page_link_range; + var min_offset = 0; + if (min_page <= 0) { + // Min page is too low. + min_page = 1; + min_offset = page_link_range - (cur_page_num - min_page); + } + + // Set max page. + var max_range = page_link_range + min_offset; + var max_page = cur_page_num + max_range; + var max_offset; + if (max_page <= num_pages) { + // Max page is fine. + max_offset = 0; + } else { + // Max page is too high. + max_page = num_pages; + // +1 to account for the +1 in the loop below. + max_offset = max_range - (max_page + 1 - cur_page_num); + } + + // Second and final pass on min page to add any unused + // offset from max to min. + if (max_offset !== 0) { + min_page -= max_offset; + if (min_page < 1) { + min_page = 1; + } + } + + // template header + tmpl += ''; + if (options.show_item_checkboxes) { + tmpl += ""; + } + tmpl += '' + '' + "Page:"; + + if (min_page > 1) { + tmpl += + '1 ...'; + } + + // create page urls + for (var page_index = min_page; page_index < max_page + 1; page_index++) { + if (page_index == options.cur_page_num) { + tmpl += `${page_index}`; + } else { + tmpl += `${page_index}`; + } + } + + // show last page + if (max_page < num_pages) { + tmpl += `...${num_pages}`; + } + tmpl += ""; + + // Show all link + tmpl += ` + | Show All + + `; + } + + // Grid operations for multiple items. + if (options.show_item_checkboxes) { + // start template + tmpl += ` + + + + + For selected items: + `; + + // configure buttons for operations + for (let operation of options.operations) { + if (operation.allow_multiple) { + tmpl += ` `; + } + } + + // finalize template + tmpl += "" + ""; + } + + // count global operations + var found_global = false; + for (let operation of options.operations) { + if (operation.global_operation) { + found_global = true; + break; + } + } + + // add global operations + if (found_global) { + tmpl += "" + ''; + for (let operation of options.operations) { + if (operation.global_operation) { + tmpl += `${operation.label}`; + } + } + tmpl += "" + ""; + } + + // add legend + if (options.legend) { + tmpl += `${options.legend}`; + } + + // return + return tmpl; + }, + + // template + message: function(options) { + var status = options.status; + if (["success", "ok"].indexOf(status) != -1) { + status = "done"; + } + return `

${_.escape( + options.message + )}

`; + }, + + // template + grid_filters: function(options) { + // get filters + var default_filter_dict = options.default_filter_dict; + var filters = options.filters; + + // show advanced search if flag set or if there are filters for advanced search fields + var advanced_search_display = "none"; + if (options.advanced_search) { + advanced_search_display = "block"; + } + + // identify columns with advanced filtering + var show_advanced_search_link = false; + for (let column of options.columns) { + if (column.filterable == "advanced") { + var column_key = column.key; + var f_key = filters[column_key]; + var d_key = default_filter_dict[column_key]; + if (f_key && d_key && f_key != d_key) { + advanced_search_display = "block"; + } + show_advanced_search_link = true; + } + } + + // hide standard search if advanced is shown + var standard_search_display = "block"; + if (advanced_search_display == "block") { + standard_search_display = "none"; + } + + // + // standard search + // + var tmpl = `"; + + // + // advanced search + // + tmpl += `"; + + // return template + return tmpl; + }, + + // template + grid_column_filter: function(options, column) { + // collect parameters + var filters = options.filters; + var column_label = column.label; + var column_key = column.key; + if (column.filterable == "advanced") { + column_label = column_label.toLowerCase(); + } + + // start + var tmpl = ""; + + if (column.filterable == "advanced") { + tmpl += `${column_label}:`; + } + tmpl += ''; + if (column.is_text) { + tmpl += `
`; + // Carry forward filtering criteria with hidden inputs. + for (let column of options.columns) { + var filter_value = filters[column.key]; + if (filter_value) { + if (filter_value != "All") { + if (column.is_text) { + filter_value = JSON.stringify(filter_value); + } + tmpl += ``; + } + } + } + // Print current filtering criteria and links to delete. + tmpl += ``; + + // add filters + var column_filter = filters[column_key]; + if (column_filter) { + // identify type + var type = jQuery.type(column_filter); + + // single filter value + if (type == "string") { + if (column_filter != "All") { + // append template + tmpl += this.filter_element(column_key, column_filter); + } + } + + // multiple filter values + if (type == "array") { + for (let i in column_filter) { + // copy filters and remove entry + var params = column_filter; + params = params.slice(i); + + // append template + tmpl += this.filter_element(column_key, column_filter[i]); + } + } + } + + // close span + tmpl += ""; + + // Set value, size of search input field. Minimum size is 20 characters. + var value = ""; + var size = 20; + if (column.filterable == "standard") { + value = column.label.toLowerCase(); + if (value.length < 20) { + size = value.length; + } + // +4 to account for space after placeholder + size = size + 4; + } + + // print input field for column + tmpl += ` + + + + +
`; + } else { + // filter criteria + tmpl += ``; + + // add category filters + var seperator = false; + for (var cf_label in options.categorical_filters[column_key]) { + // get category filter + var cf = options.categorical_filters[column_key][cf_label]; + + // each filter will have only a single argument, so get that single argument + var cf_key = ""; + var cf_arg = ""; + for (var key in cf) { + cf_key = key; + cf_arg = cf[key]; + } + + // add seperator + if (seperator) { + tmpl += " | "; + } + seperator = true; + + // add category + var filter = filters[column_key]; + if (filter && cf[column_key] && filter == cf_arg) { + tmpl += `${cf_label}`; + } else { + tmpl += `${cf_label}`; + } + } + tmpl += ""; + } + tmpl += "" + ""; + + // return template + return tmpl; + }, + + // template for filter items + filter_element: function(filter_key, filter_value) { + filter_value = Utils.sanitize(filter_value); + return `${filter_value}`; + } +}; diff --git a/client/galaxy/scripts/legacy/grid/grid-view.js b/client/galaxy/scripts/legacy/grid/grid-view.js new file mode 100644 index 00000000000..05e82077321 --- /dev/null +++ b/client/galaxy/scripts/legacy/grid/grid-view.js @@ -0,0 +1,677 @@ +// This is necessary so that, when nested arrays are used in ajax/post/get methods, square brackets ('[]') are +// not appended to the identifier of a nested array. +jQuery.ajaxSettings.traditional = true; + +// dependencies +import Utils from "utils/utils"; +import GridModel from "legacy/grid/grid-model"; +import Templates from "legacy/grid/grid-template"; +import PopupMenu from "mvc/ui/popup-menu"; +// grid view +export default Backbone.View.extend({ + // model + grid: null, + + // Initialize + initialize: function(grid_config) { + this.grid = new GridModel(); + this.dict_format = grid_config.dict_format; + this.title = grid_config.title; + var self = this; + window.add_tag_to_grid_filter = (tag_name, tag_value) => { + // Put tag name and value together. + var tag = tag_name + (tag_value !== undefined && tag_value !== "" ? `:${tag_value}` : ""); + var advanced_search = $("#advanced-search").is(":visible"); + if (!advanced_search) { + $("#standard-search").slideToggle("fast"); + $("#advanced-search").slideToggle("fast"); + } + self.add_filter_condition("tags", tag); + }; + + // set element + if (this.dict_format) { + this.setElement("
"); + if (grid_config.url_base && !grid_config.items) { + var url_data = grid_config.url_data || {}; + _.each(grid_config.filters, (v, k) => { + url_data[`f-${k}`] = v; + }); + $.ajax({ + url: `${grid_config.url_base}?${$.param(url_data)}`, + success: function(response) { + response.embedded = grid_config.embedded; + response.filters = grid_config.filters || {}; + self.init_grid(response); + } + }); + } else { + this.init_grid(grid_config); + } + } else { + this.setElement("#grid-container"); + this.init_grid(grid_config); + } + + // fix padding + if (grid_config.use_panels) { + $("#center").css({ + padding: "10px", + overflow: "auto" + }); + } + }, + + // refresh frames + handle_refresh: function(refresh_frames) { + if (refresh_frames) { + if ($.inArray("history", refresh_frames) > -1) { + if (window.top.Galaxy && window.top.Galaxy.currHistoryPanel) { + window.top.Galaxy.currHistoryPanel.loadCurrentHistory(); + } + } + } + }, + + // Initialize + init_grid: function(grid_config) { + this.grid.set(grid_config); + + // get options + var options = this.grid.attributes; + + if (this.allow_title_display && options.title) { + Utils.setWindowTitle(options.title); + } + // handle refresh requests + this.handle_refresh(options.refresh_frames); + + // strip protocol and domain + var url = this.grid.get("url_base"); + url = url.replace(/^.*\/\/[^\/]+/, ""); + this.grid.set("url_base", url); + + // append main template + this.$el.html(Templates.grid(options)); + + // update div contents + this.$el.find("#grid-table-header").html(Templates.header(options)); + this.$el.find("#grid-table-body").html(Templates.body(options)); + this.$el.find("#grid-table-footer").html(Templates.footer(options)); + + // update message + if (options.message) { + this.$el.find("#grid-message").html(Templates.message(options)); + var self = this; + if (options.use_hide_message) { + setTimeout(() => { + self.$el.find("#grid-message").html(""); + }, 5000); + } + } + + // configure elements + this.init_grid_elements(); + this.init_grid_controls(); + + // attach global event handler + // TODO: redundant (the onload/standard page handlers do this) - but needed because these are constructed after page ready + if (window.init_refresh_on_change) { + window.init_refresh_on_change(); + } + }, + + // Initialize grid controls + init_grid_controls: function() { + // link + var self = this; + + // Initialize grid operation button. + this.$el.find(".operation-button").each(function() { + $(this).off(); + $(this).click(function() { + self.submit_operation(this); + return false; + }); + }); + + // Initialize text filters to select text on click and use normal font when user is typing. + this.$el.find("input[type=text]").each(function() { + $(this).off(); + $(this) + .click(function() { + $(this).select(); + }) + .keyup(function() { + $(this).css("font-style", "normal"); + }); + }); + + // Initialize sort links. + this.$el.find(".sort-link").each(function() { + $(this).off(); + $(this).click(function() { + self.set_sort_condition($(this).attr("sort_key")); + return false; + }); + }); + + // Initialize text filters. + this.$el.find(".text-filter-form").each(function() { + $(this).off(); + $(this).submit(function() { + var column_key = $(this).attr("column_key"); + var text_input_obj = $(`#input-${column_key}-filter`); + var text_input = text_input_obj.val(); + text_input_obj.val(""); + self.add_filter_condition(column_key, text_input); + return false; + }); + }); + + // Initialize categorical filters. + this.$el.find(".text-filter-val > a").each(function() { + $(this).off(); + $(this).click(function() { + // Remove visible element. + $(this) + .parent() + .remove(); + + // Remove filter condition. + self.remove_filter_condition($(this).attr("filter_key"), $(this).attr("filter_val")); + + // Return + return false; + }); + }); + + // Initialize categorical filters. + this.$el.find(".categorical-filter > a").each(function() { + $(this).off(); + $(this).click(function() { + self.set_categorical_filter($(this).attr("filter_key"), $(this).attr("filter_val")); + return false; + }); + }); + + // Initialize standard, advanced search toggles. + this.$el.find(".advanced-search-toggle").each(function() { + $(this).off(); + $(this).click(() => { + self.$el.find("#standard-search").slideToggle("fast"); + self.$el.find("#advanced-search").slideToggle("fast"); + return false; + }); + }); + + // Add event to check all box + this.$el.find("#check_all").off(); + this.$el.find("#check_all").on("click", () => { + self.check_all_items(); + }); + }, + + // Initialize grid elements. + init_grid_elements: function() { + // Initialize grid selection checkboxes. + this.$el.find(".grid").each(function() { + var checkboxes = $(this).find("input.grid-row-select-checkbox"); + var check_count = $(this).find("span.grid-selected-count"); + var update_checked = () => { + check_count.text($(checkboxes).filter(":checked").length); + }; + + $(checkboxes).each(function() { + $(this).change(update_checked); + }); + update_checked(); + }); + + // Initialize ratings. + if (this.$el.find(".community_rating_star").length !== 0) this.$el.find(".community_rating_star").rating({}); + + // get options + var options = this.grid.attributes; + var self = this; + + // + // add page click events + // + this.$el.find(".page-link > a").each(function() { + $(this).click(function() { + self.set_page($(this).attr("page_num")); + return false; + }); + }); + + // + // add inbound/outbound events + // + this.$el.find(".use-target").each(function() { + $(this).click(function() { + self.execute({ + href: $(this).attr("href"), + target: $(this).attr("target") + }); + return false; + }); + }); + + // empty grid? + var items_length = options.items.length; + if (items_length === 0) { + return; + } + + // add operation popup menus + _.each(options.items, (item, index) => { + var button = self.$(`#grid-${item.encode_id}-popup`).off(); + var popup = new PopupMenu(button); + _.each(options.operations, operation => { + self._add_operation(popup, operation, item); + }); + }); + }, + + /** Add an operation to the items menu */ + _add_operation: function(popup, operation, item) { + var self = this; + var settings = item.operation_config[operation.label]; + if (settings.allowed && operation.allow_popup) { + popup.addItem({ + html: operation.label, + href: settings.url_args, + target: settings.target, + confirmation_text: operation.confirm, + func: function(e) { + e.preventDefault(); + var label = $(e.target).html(); + if (operation.onclick) { + operation.onclick(item.encode_id); + } else { + self.execute(this.findItemByHtml(label)); + } + } + }); + } + }, + + // Add a condition to the grid filter; this adds the condition and refreshes the grid. + add_filter_condition: function(name, value) { + // Do nothing is value is empty. + if (value === "") { + return false; + } + + // Add condition to grid. + this.grid.add_filter(name, value, true); + + // Add button that displays filter and provides a button to delete it. + var t = $(Templates.filter_element(name, value)); + var self = this; + t.click(function() { + // Remove visible element. + $(this).remove(); + + // Remove filter condition. + self.remove_filter_condition(name, value); + }); + + // append to container + var container = this.$el.find(`#${name}-filtering-criteria`); + container.append(t); + + // execute + this.go_page_one(); + this.execute(); + }, + + // Remove a condition to the grid filter; this adds the condition and refreshes the grid. + remove_filter_condition: function(name, value) { + // Remove filter condition. + this.grid.remove_filter(name, value); + + // Execute + this.go_page_one(); + this.execute(); + }, + + // Set sort condition for grid. + set_sort_condition: function(col_key) { + // Set new sort condition. New sort is col_key if sorting new column; if reversing sort on + // currently sorted column, sort is reversed. + var cur_sort = this.grid.get("sort_key"); + var new_sort = col_key; + if (cur_sort.indexOf(col_key) !== -1) { + // Reverse sort. + if (cur_sort.substring(0, 1) !== "-") { + new_sort = `-${col_key}`; + } + } + + // Remove sort arrows elements. + this.$el.find(".sort-arrow").remove(); + + // Add sort arrow element to new sort column. + var sort_arrow = new_sort.substring(0, 1) == "-" ? "↑" : "↓"; + var t = $(`${sort_arrow}`).addClass("sort-arrow"); + + // Add to header + this.$el.find(`#${col_key}-header`).append(t); + + // Update grid. + this.grid.set("sort_key", new_sort); + this.go_page_one(); + this.execute(); + }, + + // Set new value for categorical filter. + set_categorical_filter: function(name, new_value) { + // Update filter hyperlinks to reflect new filter value. + var category_filter = this.grid.get("categorical_filters")[name]; + + var cur_value = this.grid.get("filters")[name]; + var self = this; + this.$el.find(`.${name}-filter`).each(function() { + var text = $.trim($(this).text()); + var filter = category_filter[text]; + var filter_value = filter[name]; + if (filter_value == new_value) { + // Remove filter link since grid will be using this filter. It is assumed that + // this element has a single child, a hyperlink/anchor with text. + $(this).empty(); + $(this).addClass("current-filter"); + $(this).append(text); + } else if (filter_value == cur_value) { + // Add hyperlink for this filter since grid will no longer be using this filter. It is assumed that + // this element has a single child, a hyperlink/anchor. + $(this).empty(); + var t = $(`${text}`); + t.click(() => { + self.set_categorical_filter(name, filter_value); + }); + $(this).removeClass("current-filter"); + $(this).append(t); + } + }); + + // Update grid. + this.grid.add_filter(name, new_value); + this.go_page_one(); + this.execute(); + }, + + // Set page to view. + set_page: function(new_page) { + // Update page hyperlink to reflect new page. + var self = this; + this.$el.find(".page-link").each(function() { + var id = $(this).attr("id"); + + var // Id has form 'page-link- + page_num = parseInt(id.split("-")[2], 10); + + var cur_page = self.grid.get("cur_page"); + var text; + if (page_num === new_page) { + // Remove link to page since grid will be on this page. It is assumed that + // this element has a single child, a hyperlink/anchor with text. + text = $(this) + .children() + .text(); + $(this).empty(); + $(this).addClass("inactive-link"); + $(this).text(text); + } else if (page_num === cur_page) { + // Add hyperlink to this page since grid will no longer be on this page. It is assumed that + // this element has a single child, a hyperlink/anchor. + text = $(this).text(); + $(this).empty(); + $(this).removeClass("inactive-link"); + var t = $(`${text}`); + t.click(() => { + self.set_page(page_num); + }); + $(this).append(t); + } + }); + + if (new_page === "all") { + this.grid.set("cur_page", new_page); + } else { + this.grid.set("cur_page", parseInt(new_page, 10)); + } + this.execute(); + }, + + // confirmation/submission of operation request + submit_operation: function(operation_button, confirmation_text) { + // identify operation + var operation_name = $(operation_button).val(); + + // verify any item is selected + var number_of_checked_ids = this.$el.find('input[name="id"]:checked').length; + if (number_of_checked_ids < 1) { + return false; + } + + // Check to see if there's grid confirmation text for this operation + var operation = _.findWhere(this.grid.attributes.operations, { + label: operation_name + }); + if (operation && !confirmation_text) { + confirmation_text = operation.confirm || ""; + } + + // collect ids + var item_ids = []; + this.$el.find("input[name=id]:checked").each(function() { + item_ids.push($(this).val()); + }); + + // execute operation + var options = { + operation: operation_name, + id: item_ids, + confirmation_text: confirmation_text + }; + if (operation.target == "top" || operation.target == "center") { + options = _.extend(options, { + href: operation.href, + target: operation.target + }); + } + this.execute(options); + return true; + }, + + check_all_items: function() { + var check = this.$(".grid-row-select-checkbox"); + var state = this.$("#check_all").prop("checked"); + _.each(check, c => { + $(c).prop("checked", state); + }); + this.init_grid_elements(); + }, + + // Go back to page one; this is useful when a filter is applied. + go_page_one: function() { + // Need to go back to page 1 if not showing all. + var cur_page = this.grid.get("cur_page"); + if (cur_page !== null && cur_page !== undefined && cur_page !== "all") { + this.grid.set("cur_page", 1); + } + }, + + // + // execute operations and hyperlink requests + // + execute: function(options) { + // get url + var id = null; + var href = null; + var operation = null; + var confirmation_text = null; + var target = null; + + // check for options + if (options) { + // get options + href = options.href; + operation = options.operation; + id = options.id; + confirmation_text = options.confirmation_text; + target = options.target; + + // check if input contains the operation tag + if (href !== undefined && href.indexOf("operation=") != -1) { + // Get operation, id in hyperlink's href. + var href_parts = href.split("?"); + if (href_parts.length > 1) { + var href_parms_str = href_parts[1]; + var href_parms = href_parms_str.split("&"); + for (var index = 0; index < href_parms.length; index++) { + if (href_parms[index].indexOf("operation") != -1) { + // Found operation parm; get operation value. + operation = href_parms[index].split("=")[1]; + operation = operation.replace(/\+/g, " "); + } else if (href_parms[index].indexOf("id") != -1) { + // Found id parm; get id value. + id = href_parms[index].split("=")[1]; + } + } + } + } + } + + // check for operation details + if (operation && id) { + // show confirmation box + if ( + confirmation_text && + confirmation_text !== "" && + confirmation_text != "None" && + confirmation_text != "null" + ) + if (!confirm(confirmation_text)) return false; + + // use small characters for operation?! + operation = operation.toLowerCase(); + + // Update grid. + this.grid.set({ + operation: operation, + item_ids: id + }); + + // Do operation. If operation cannot be performed asynchronously, redirect to location. + if (target == "top") { + window.top.location = `${href}?${$.param(this.grid.get_url_data())}`; + } else if (target == "center") { + $("#galaxy_main").attr("src", `${href}?${$.param(this.grid.get_url_data())}`); + } else if (this.grid.can_async_op(operation) || this.dict_format) { + this.update_grid(); + } else { + this.go_to(target, href); + } + + // done + return false; + } + + // refresh grid + if (href) { + this.go_to(target, href); + return false; + } + + // refresh grid + if (this.grid.get("async") || this.dict_format) { + this.update_grid(); + } else { + this.go_to(target, href); + } + + // done + return false; + }, + + // go to url + go_to: function(target, href) { + // get aysnc status + var async = this.grid.get("async"); + this.grid.set("async", false); + + // get slide status + var advanced_search = this.$el.find("#advanced-search").is(":visible"); + this.grid.set("advanced_search", advanced_search); + + // get default url + if (!href) { + href = `${this.grid.get("url_base")}?${$.param(this.grid.get_url_data())}`; + } + + // clear grid of transient request attributes. + this.grid.set({ + operation: undefined, + item_ids: undefined, + async: async + }); + switch (target) { + case "center": + $("#galaxy_main").attr("src", href); + break; + case "top": + window.top.location = href; + break; + default: + window.location = href; + } + }, + + // Update grid. + update_grid: function() { + // If there's an operation, do POST; otherwise, do GET. + var method = this.grid.get("operation") ? "POST" : "GET"; + + // Show overlay to indicate loading and prevent user actions. + this.$el.find(".loading-elt-overlay").show(); + var self = this; + $.ajax({ + type: method, + url: self.grid.get("url_base"), + data: self.grid.get_url_data(), + error: function() { + alert("Grid refresh failed"); + }, + success: function(response_text) { + // backup + var embedded = self.grid.get("embedded"); + var insert = self.grid.get("insert"); + var advanced_search = self.$el.find("#advanced-search").is(":visible"); + + // request new configuration + var json = self.dict_format ? response_text : $.parseJSON(response_text); + + // update + json.embedded = embedded; + json.insert = insert; + json.advanced_search = advanced_search; + + // Initialize new grid config + self.init_grid(json); + + // Hide loading overlay. + self.$el.find(".loading-elt-overlay").hide(); + }, + complete: function() { + // Clear grid of transient request attributes. + self.grid.set({ + operation: undefined, + item_ids: undefined + }); + } + }); + } +}); diff --git a/client/galaxy/scripts/mvc/grid/grid-model.js b/client/galaxy/scripts/mvc/grid/grid-model.js index 6e3fc1698f9..bd4b3c99350 100644 --- a/client/galaxy/scripts/mvc/grid/grid-model.js +++ b/client/galaxy/scripts/mvc/grid/grid-model.js @@ -17,13 +17,6 @@ export default Backbone.Model.extend({ item_ids: undefined }, - /** - * Return true if operation can be done asynchronously. - */ - can_async_op: function(op) { - return _.indexOf(this.attributes.async_ops, op) !== -1; - }, - /** * Add filtering criterion. */ diff --git a/client/galaxy/scripts/mvc/grid/grid-shared.js b/client/galaxy/scripts/mvc/grid/grid-shared.js index a2eebe703a7..34cb89f75f3 100644 --- a/client/galaxy/scripts/mvc/grid/grid-shared.js +++ b/client/galaxy/scripts/mvc/grid/grid-shared.js @@ -10,7 +10,6 @@ var View = Backbone.View.extend({ $.ajax({ url: `${Galaxy.root + this.item}/${this.model.get("action_id")}?${$.param(Galaxy.params)}`, success: function(response) { - response["dict_format"] = true; self.model.set(response); self.render(); } diff --git a/client/galaxy/scripts/mvc/grid/grid-template.js b/client/galaxy/scripts/mvc/grid/grid-template.js index 5fafae695b1..e5fda7d790f 100644 --- a/client/galaxy/scripts/mvc/grid/grid-template.js +++ b/client/galaxy/scripts/mvc/grid/grid-template.js @@ -112,14 +112,12 @@ export default { body: function(options) { // initialize var tmpl = ""; - var num_rows_rendered = 0; var items_length = options.items.length; // empty grid? if (items_length === 0) { // No results. tmpl += 'No Items'; - num_rows_rendered = 1; } // create rows @@ -180,9 +178,7 @@ export default { if (options.operations.length !== 0) { tmpl += `
`; } - tmpl += `${value}`; + tmpl += `${value}`; if (options.operations.length !== 0) { tmpl += "
"; } @@ -195,7 +191,6 @@ export default { } } tmpl += ""; - num_rows_rendered++; } return tmpl; }, @@ -262,21 +257,13 @@ export default { if (page_index == options.cur_page_num) { tmpl += `${page_index}`; } else { - tmpl += `${ - page_index - }`; + tmpl += `${page_index}`; } } // show last page if (max_page < num_pages) { - tmpl += `...${ - num_pages - }`; + tmpl += `...${num_pages}`; } tmpl += ""; @@ -386,9 +373,7 @@ export default { // // standard search // - var tmpl = `