Merge branch 'dev' into bam_unsorted

This commit is contained in:
Björn Grüning
2018-01-15 12:57:56 +01:00
committed by GitHub
421 changed files with 9423 additions and 5687 deletions
+34
View File
@@ -0,0 +1,34 @@
#!/bin/sh
if command -v ack-grep >/dev/null; then
ACK=ack-grep
else
ACK=ack
fi
export ACK_OPTIONS=" --type python \
--ignore-dir=.git/ \
--ignore-dir=.tox/ \
--ignore-dir=.venv/ \
--ignore-dir=client/node_modules/ \
--ignore-dir=database/ \
--ignore-dir=doc/build/ \
--ignore-dir=eggs/ \
--ignore-dir=static/maps/ \
--ignore-dir=static/scripts/"
PYTHON2_ONLY_MODULES="__builtin__ _winreg BaseHTTPServer CGIHTTPServer \
ConfigParser Cookie cookielib copy_reg cPickle cStringIO Dialog dummy_thread \
FileDialog gdbm htmlentitydefs HTMLParser httplib Queue robotparser \
ScrolledText SimpleDialog SimpleHTTPServer SimpleXMLRPCServer SocketServer \
StringIO thread Tix tkColorChooser tkCommonDialog Tkconstants Tkdnd tkFont \
Tkinter tkFileDialog tkMessageBox tkSimpleDialog ttk urllib urllib2 urlparse \
xmlrpclib"
ret=0
for mod in $PYTHON2_ONLY_MODULES; do
$ACK "^import $mod(\n|\.)|^from $mod import "
if [ $? -eq 0 ]; then ret=1; fi
done
exit $ret
+2 -2
View File
@@ -10,7 +10,7 @@ bash run.sh --daemon && \
sleep 1
i=$((i + 1))
done
kill -9 "$(cat paster.pid)"
kill -9 "$(cat galaxy.pid)"
echo "exit code:$EXIT_CODE, showing startup log:"
cat paster.log
cat galaxy.log
exit $EXIT_CODE
-94
View File
@@ -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/
+1
View File
@@ -74,6 +74,7 @@ config/plugins/interactive_environments/**/*.ini
!config/plugins
static/welcome.html.*
static/welcome.html
static/client_build_hash.txt
# Tool data.
tool-data/annotation_profiler_options.xml
+11 -5
View File
@@ -9,18 +9,24 @@ env:
- TOX_ENV=py27-unit
- TOX_ENV=qunit
- TOX_ENV=first_startup
- TOX_ENV=validate-test-tools
- TOX_ENV=py27-lint-docstring-include-list
- TOX_ENV=check-python-dependencies
matrix:
include:
- env: TOX_ENV=validate-test-tools
addons:
apt:
packages:
- libxml2-utils
- env: TOX_ENV=check_py3_compatibility
addons:
apt:
packages:
- ack-grep
allow_failures:
- env: TOX_ENV=check-python-dependencies
addons:
apt:
packages:
- libxml2-utils
install:
- pip install tox
+7
View File
@@ -18,6 +18,7 @@ SLIDESHOW_DIR=$(DOC_SOURCE_DIR)/slideshow
OPEN_RESOURCE=bash -c 'open $$0 || xdg-open $$0'
SLIDESHOW_TO_PDF?=bash -c 'docker run --rm -v `pwd`:/cwd astefanutti/decktape /cwd/$$0 /cwd/`dirname $$0`/`basename -s .html $$0`.pdf'
CLIENT_COMMIT_WARNING="Please remember to 'make client-production' when finished developing, before a commit!"
YARN := $(shell command -v yarn 2> /dev/null)
all: help
@echo "This makefile is used for building Galaxy's JS client, documentation, and drive the release process. A sensible all target is not implemented."
@@ -127,7 +128,13 @@ update-and-commit-dependencies: ## update and commit linting + dev dependencies
sh lib/galaxy/dependencies/pipfiles/update.sh -c
node-deps: ## Install NodeJS dependencies.
ifndef YARN
@echo "Could not find yarn, which is required to build the Galaxy client.\nTo install yarn, please visit \033[0;34mhttps://yarnpkg.com/en/docs/install\033[0m for instructions, and package information for all platforms.\n"
false;
else
cd client && yarn install --check-files
endif
client: node-deps ## Rebuild client-side artifacts for local development.
cd client && yarn run build
+9 -22
View File
@@ -1,26 +1,13 @@
module.exports = function(grunt) {
"use strict";
var GALAXY_PATHS = {
dist : '../static/scripts',
maps : '../static/maps',
// this symlink allows us to serve uncompressed scripts in DEV_PATH for use with sourcemaps
srcSymlink : '../static/src',
},
TOOLSHED_PATHS = {
dist : '../static/toolshed/scripts',
maps : '../static/toolshed/maps',
srcSymlink : '../static/toolshed/src',
};
grunt.config.set( 'app', 'galaxy' );
grunt.config.set( 'paths', GALAXY_PATHS );
if( grunt.option( 'app' ) === 'toolshed' ){
grunt.config.set( 'app', grunt.option( 'app' ) );
grunt.config.set( 'paths', TOOLSHED_PATHS );
}
dist: "../static/scripts",
maps: "../static/maps",
// this symlink allows us to serve uncompressed scripts in DEV_PATH for use with sourcemaps
srcSymlink: "../static/src"
};
grunt.config.set("app", "galaxy");
grunt.config.set("paths", GALAXY_PATHS);
// see the sub directory grunt-tasks/ for individual task definitions
grunt.loadTasks( 'grunt-tasks' );
grunt.registerTask( 'default', [ 'uglify' ] );
grunt.loadTasks("grunt-tasks");
grunt.registerTask("default", ["style"]);
};
+9 -11
View File
@@ -3,13 +3,21 @@ Client Build System
Installs, stages, and builds the client-side scripts necessary for running the
Galaxy webapp. There's no need to use this system unless you are modifying or
developing client-side scripts.
developing client-side scripts, or are running the development branch of
Galaxy.
The base dependencies you will need are Node.js and Yarn. On OSX the easiest
way to get set up is using homebrew and the command `brew install nodejs yarn`.
More information including instructions for other platforms is available at
nodejs.org and yarnpkg.com.
The Galaxy client build has necessarily grown more complex in the past several
years, but we're still trying to keep things as simple as possible for
developers (everyone, really). If you're having any trouble with building the
client after following the instructions below please create an issue on Github
or reach out for help directly on Gitter at
https://gitter.im/galaxyproject/Lobby.
Complete Client Build
================================================
@@ -66,13 +74,3 @@ As mentioned above, 'make client' will also rebuild styles. If you *only* want
to run the style task, use the following command from the `client` directory:
yarn run style
The Toolshed Client Build
=========================
The toolshed client is not tightly integrated with the rest of the build
system. To build the toolshed client, execute the following command from the
`client` directory.
yarn run build-toolshed
+22 -22
View File
@@ -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
})
);
},
+12 -5
View File
@@ -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`
})
);
},
+150 -2
View File
@@ -1,11 +1,37 @@
import _l from "utils/localization";
import GalaxyApp from "galaxy";
import WorkflowView from "mvc/workflow/workflow-view";
import Trackster from "viz/trackster";
import Circster from "viz/circster";
import Phyloviz from "viz/phyloviz";
import Sweepster from "viz/sweepster";
import GalaxyLibrary from "galaxy.library";
import AdminToolshed from "admin.toolshed";
import Masthead from "layout/masthead";
import user from "mvc/user/user-model";
import Modal from "mvc/ui/ui-modal";
import pagesEditorOnload from "galaxy.pages";
import Data from "mvc/dataset/data";
import History from "mvc/history/history-model";
import StructureView from "mvc/history/history-structure-view";
import HistoryContents from "mvc/history/history-contents";
import MultiPanel from "mvc/history/multi-panel";
import HistoryView from "mvc/history/history-view";
import HistoryViewEdit from "mvc/history/history-view-edit";
import HistoryViewAnnotated from "mvc/history/history-view-annotated";
import HistoryCopyDialog from "mvc/history/copy-dialog";
import HDAListItemEdit from "mvc/history/hda-li-edit";
import HDAModel from "mvc/history/hda-model";
import addLogging from "utils/add-logging";
import LegacyGridView from "legacy/grid/grid-view";
import * as run_stats from "reports/run_stats";
import ToolshedGroups from "toolshed/toolshed.groups";
if (window.Galaxy && window.Galaxy.debug === undefined) {
//TODO: (kind of a temporary hack?) Must have Galaxy.logging for some of the imports
//here; remove when imports are all fixed.
addLogging(window.Galaxy, "GalaxyApp");
}
export function mastheadEntry(options) {
if (!Galaxy.user) {
@@ -34,17 +60,139 @@ export function workflowEntry(options) {
new WorkflowView(options);
}
function libraryEntry(options) {
export function libraryEntry(options) {
new GalaxyLibrary.GalaxyApp(options);
}
export function historyEntry(options) {
$("#toggle-deleted").modeButton({
initialMode: options.initialModeDeleted,
modes: [
{ mode: "showing_deleted", html: _l("Exclude deleted") },
{ mode: "not_showing_deleted", html: _l("Include deleted") }
]
});
$("#toggle-hidden").modeButton({
initialMode: options.initialModeHidden,
modes: [
{ mode: "showing_hidden", html: _l("Exclude hidden") },
{ mode: "not_showing_hidden", html: _l("Include hidden") }
]
});
$("#switch").click(function() {
//##HACK:ity hack hack
//##TODO: remove when out of iframe
var hview =
Galaxy.currHistoryPanel || (window.top.Galaxy && window.top.Galaxy.currHistoryPanel)
? window.top.Galaxy.currHistoryPanel
: null;
if (hview) {
hview.switchToHistory("${ history[ 'id' ] }");
} else {
window.location = "${ switch_to_url }";
}
});
// use_panels effects where the the center_panel() is rendered:
// w/o it renders to the body, w/ it renders to #center - we need to adjust a few things for scrolling to work
if (options.hasMasthead) {
$("#center").addClass("flex-vertical-container");
}
let viewClass = options.userIsOwner ? HistoryViewEdit.HistoryViewEdit : HistoryView.HistoryView;
let historyModel = new History.History(options.historyJSON);
// attach the copy dialog to the import button now that we have a history
$("#import").click(function() {
HistoryCopyDialog(historyModel, {
useImport: true,
// use default datasets option to match the toggle-deleted button
allDatasets: $("#toggle-deleted").modeButton("getMode").mode === "showing_deleted"
}).done(function() {
if (window === window.parent) {
window.location = Galaxy.root;
} else if (Galaxy.currHistoryPanel) {
Galaxy.currHistoryPanel.loadCurrentHistory();
}
});
});
let historyView = new viewClass({
el: $("#history-" + options.historyJSON.id),
className: viewClass.prototype.className + " wide",
$scrollContainer: options.hasMasthead
? function() {
return this.$el.parent();
}
: undefined,
model: historyModel,
show_deleted: options.showDeletedJson,
show_hidden: options.showHiddenJson,
purgeAllowed: options.allow_user_dataset_purge
});
historyView.trigger("loading");
historyModel
.fetchContents({ silent: true })
.fail(function() {
alert("Galaxy history failed to load");
})
.done(function() {
historyView.trigger("loading-done");
historyView.render();
});
$("#toggle-deleted").on("click", function() {
historyView.toggleShowDeleted();
});
$("#toggle-hidden").on("click", function() {
historyView.toggleShowHidden();
});
}
export function multiHistoryEntry(options) {
let histories = new History.HistoryCollection([], {
includeDeleted: options.includingDeleted,
order: options.order,
limitOnFirstFetch: options.limit,
limitPerFetch: options.limit,
currentHistoryId: options.current_history_id
});
let multipanel = new MultiPanel.MultiPanelColumns({
el: $("#center").get(0),
histories: histories
});
histories.fetchFirst({ silent: true }).done(function() {
multipanel.createColumns();
multipanel.render(0);
});
}
export const bundleEntries = {
library: libraryEntry,
masthead: mastheadEntry,
workflow: workflowEntry,
trackster: tracksterEntry,
circster: circsterEntry,
adminToolshed: adminToolshedEntry
adminToolshed: adminToolshedEntry,
pages: pagesEditorOnload,
phyloviz: Phyloviz.PhylovizView,
createTabularDatasetChunkedView: Data.createTabularDatasetChunkedView,
multiHistory: multiHistoryEntry,
history: historyEntry,
History: History.History,
HistoryContents: HistoryContents.HistoryContents,
SweepsterVisualization: Sweepster.SweepsterVisualization,
SweepsterVisualizationView: Sweepster.SweepsterVisualizationView,
HistoryCopyDialog,
HistoryViewAnnotated,
Trackster,
StructureView,
HDAListItemEdit,
HDAModel,
GalaxyApp,
LegacyGridView,
run_stats,
ToolshedGroups
};
window.bundleEntries = bundleEntries;
@@ -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"),
@@ -57,7 +57,10 @@ export default {
},
computed: {
formattedReferences: function() {
return this.citations.reduce((a, b) => a.concat(`<p class="formatted-reference">${this.formattedReference(b)}</p>`), "");
return this.citations.reduce(
(a, b) => a.concat(`<p class="formatted-reference">${this.formattedReference(b)}</p>`),
""
);
}
},
created: function() {
@@ -187,16 +190,16 @@ export default {
};
</script>
<style>
.citations-formatted{
.citations-formatted {
word-wrap: break-word;
}
.citations-bibtex-text{
.citations-bibtex-text {
width: 100%;
height: 500px;
}
.citation-padding{
padding:5px 10px;
.citation-padding {
padding: 5px 10px;
}
</style>
@@ -60,4 +60,4 @@ export default {
.ui-message {
display: block;
}
</style>
</style>
@@ -0,0 +1,19 @@
<template>
<div v-if="message" :class="[status + 'message']">{{ message }}</div>
</template>
<script>
export default {
name: "Message",
props: {
message: {
type: String,
required: true
},
status: {
type: String,
default: "done"
}
}
};
</script>
@@ -0,0 +1,43 @@
<template>
<div class="toolForm">
<div class="toolFormTitle">
<slot name="title">{{ title }}</slot>
</div>
<div class="toolFormBody">
<table class="manage-table colored" border="0" cellspacing="0" cellpadding="0" width="100%">
<slot name="columns">
<th v-for="column in columns" bgcolor="#D8D8D8">{{ column.text }}</th>
</slot>
<slot name="rows">
<template v-for="(row, index) in rows">
<tr :class="[index % 2 === 0 ? 'tr' : 'odd_row']">
<td v-for="column in columns">{{ row[column.dataIndex] }}</td>
</tr>
</template>
</slot>
</table>
<div v-if="isLoaded !== undefined && !isLoaded" :style="{ textAlign: 'center', padding: '7px 0' }">
Loading...
</div>
</div>
</div>
</template>
<script>
export default {
props: {
isLoaded: {
type: Boolean
},
title: {
type: String
},
columns: {
type: Array
},
rows: {
type: Array
}
}
};
</script>
@@ -0,0 +1,47 @@
<template>
<table class="tabletip">
<thead>
<tr>
<th :colspan="dataManagerColumns.length" style="font-size:120%">
Data Manager: {{ dataManagerTableName }}
<a class="icon-btn" href="javascript:void(0)"
@click="handleReloadButtonClick"
:title="`Reload ${dataManagerTableName} tool data table`"
><span class="fa fa-refresh"></span></a>
</th>
</tr>
<tr>
<th v-for="column in dataManagerColumns">{{ column }}</th>
</tr>
</thead>
<tbody v-for="item in dataManagerItems">
<tr>
<td v-for="column in dataManagerColumns">{{ item[column] }}</td>
</tr>
</tbody>
</table>
</template>
<script>
export default {
props: {
dataManagerTableName: {
type: String,
required: true
},
dataManagerColumns: {
type: Array,
required: true
},
dataManagerItems: {
type: Array,
required: true
}
},
methods: {
handleReloadButtonClick(event) {
this.$emit("reloaddatamanager", this.dataManagerTableName);
}
}
};
</script>
@@ -0,0 +1,120 @@
<template>
<div>
<message :message="message" :status="status"></message>
<component
:is="currentView"
v-if="status !== 'error'"
v-bind="currentProps"
@changeview="showDataManager"
@reloaddatamanager="reloadDataManager">
</component>
</div>
</template>
<script>
import axios from "axios";
import Message from "../Message.vue";
import DataTablesGrid from "./DataTablesGrid.vue";
import DataManagerGrid from "./DataManagerGrid.vue";
export default {
data() {
return {
currentView: "data-tables-grid",
isLoaded: false,
dataTables: [],
dataManagerTableName: "",
dataManagerColumns: [],
dataManagerItems: [],
message: "",
status: ""
};
},
components: {
message: Message,
"data-tables-grid": DataTablesGrid,
"data-manager-grid": DataManagerGrid
},
computed: {
currentProps() {
let props;
if (this.currentView === "data-tables-grid") {
props = {
isLoaded: this.isLoaded,
rows: this.dataTables
};
} else {
props = {
dataManagerTableName: this.dataManagerTableName,
dataManagerColumns: this.dataManagerColumns,
dataManagerItems: this.dataManagerItems
};
}
return props;
}
},
methods: {
showDataManager(dataManagerTableName) {
axios
.get(`${Galaxy.root}data_manager/tool_data_table_items`, {
params: {
table_name: dataManagerTableName
}
})
.then(response => {
this.message = response.data.message;
this.status = response.data.status;
if (response.data.status !== "error" && response.data.status !== "warning") {
this.dataManagerTableName = dataManagerTableName;
this.dataManagerColumns = response.data.data.columns;
this.dataManagerItems = response.data.data.items;
this.currentView = "data-manager-grid";
}
})
.catch(error => {
console.error(error);
});
},
reloadDataManager(dataManagerTableName) {
axios
.get(`${Galaxy.root}data_manager/reload_tool_data_table`, {
params: {
table_name: dataManagerTableName
}
})
.then(response => {
this.message = response.data.message;
this.status = response.data.status;
if (response.data.status !== "error" && response.data.status !== "warning") {
this.dataManagerItems = response.data.data.items;
}
})
.catch(error => {
console.error(error);
});
}
},
created() {
axios
.get(`${Galaxy.root}admin/data_tables_list`)
.then(response => {
this.isLoaded = true;
this.dataTables = response.data.data;
this.message = response.data.message;
this.status = response.data.status;
})
.catch(error => {
console.error(error);
});
}
};
</script>
@@ -0,0 +1,53 @@
<template>
<base-grid :is-loaded="isLoaded" :columns="columns">
<template slot="title">
Current data table registry contains {{ rows.length }} data tables
</template>
<template slot="rows" v-for="(row, index) in rows">
<tr :class="[index % 2 === 0 ? 'tr' : 'odd_row']">
<td><a href="javascript:void(0)" @click="handleTableNameClick">{{ row.name }}</a></td>
<td>{{ row.filename }}</td>
<td>{{ row.tool_data_path }}</td>
<td>{{ row.errors }}</td>
</tr>
</template>
</base-grid>
</template>
<script>
import BaseGrid from "./BaseGrid.vue";
export default {
props: {
isLoaded: {
type: Boolean,
required: true
},
rows: {
type: Array,
required: true
}
},
data() {
return {
columns: [
{ text: "Name", dataIndex: "name" },
{ text: "Filename", dataIndex: "filename" },
{ text: "Tool data path", dataIndex: "tool_data_path" },
{ text: "Errors", dataIndex: "errors" }
]
};
},
components: {
"base-grid": BaseGrid
},
methods: {
handleTableNameClick(event) {
this.$emit("changeview", event.target.text);
}
}
};
</script>
@@ -6,9 +6,7 @@
export function append_notebook(url) {
clear_main_area();
$("#main").append(
`<iframe frameBorder="0" seamless="seamless" style="width: 100%; height: 100%; overflow:hidden;" scrolling="no" src="${
url
}"></iframe>`
`<iframe frameBorder="0" seamless="seamless" style="width: 100%; height: 100%; overflow:hidden;" scrolling="no" src="${url}"></iframe>`
);
}
@@ -21,9 +19,7 @@ export function clear_main_area() {
export function display_spinner() {
$("#main").append(
`<img id="spinner" src="${
galaxy_root
}static/style/largespinner.gif" style="position:absolute;margin:auto;top:0;left:0;right:0;bottom:0;">`
`<img id="spinner" src="${galaxy_root}static/style/largespinner.gif" style="position:absolute;margin:auto;top:0;left:0;right:0;bottom:0;">`
);
}
+3
View File
@@ -256,6 +256,9 @@ GalaxyApp.prototype.toString = function toString() {
return `GalaxyApp(${userEmail})`;
};
// This is not great. Rework to be a singleton-style Galaxy app accessible/created by import
window.Galaxy = window.Galaxy || new GalaxyApp();
// ============================================================================
export default {
GalaxyApp: GalaxyApp
+1 -1
View File
@@ -176,7 +176,7 @@ var GalaxyLibrary = Backbone.View.extend({
mod_toastr.info("You must select at least one dataset to download");
Galaxy.libraries.library_router.navigate(`folders/${folder_id}`, { trigger: true, replace: true });
} else {
Galaxy.libraries.folderToolbarView.download(folder_id, format);
Galaxy.libraries.folderToolbarView.download(format);
Galaxy.libraries.library_router.navigate(`folders/${folder_id}`, { trigger: false, replace: true });
}
});
+4 -8
View File
@@ -5,6 +5,8 @@ import "libs/jquery/jquery.wymeditor";
import "libs/jquery/jquery.autocomplete";
import GridView from "mvc/grid/grid-view";
var WYMeditor = window.WYMeditor;
var CONTROLS = {
// Item types.
ITEM_HISTORY: "item_history",
@@ -154,11 +156,7 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
}
show_modal(
"Create Link",
`<div><div><label id='link_attribute_label'>URL <span style='float: right; font-size: 90%'><a href='#' id='set_link_id'>Create in-page anchor</a></span></label><br><input type='text' class='wym_href' value='${
curURL
}' size='40' /></div><div><label>Title</label><br><input type='text' class='wym_title' value='${
curTitle
}' size='40' /></div><div>`,
`<div><div><label id='link_attribute_label'>URL <span style='float: right; font-size: 90%'><a href='#' id='set_link_id'>Create in-page anchor</a></span></label><br><input type='text' class='wym_href' value='${curURL}' size='40' /></div><div><label>Title</label><br><input type='text' class='wym_title' value='${curTitle}' size='40' /></div><div>`,
{
"Make link": function() {
// Get URL, name/title.
@@ -332,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({
@@ -426,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({
@@ -493,7 +489,7 @@ WYMeditor.editor.prototype.dialog = function(dialogType, dialogFeatures, bodyHtm
}
};
export default function editor_onload() {
export default function pagesEditorOnload() {
// Generic error handling
$(document).ajaxError((e, x) => {
// console.log( e, x );
+3 -3
View File
@@ -154,9 +154,9 @@ function show_in_overlay(options) {
closeButton: true,
title: "&nbsp;",
body: $(
`<div style='margin: -5px;'><iframe style='margin: 0; padding: 0;' src='${options.url}' width='${
width
}' height='${height}' scrolling='${scroll}' frameborder='0'></iframe></div>`
`<div style='margin: -5px;'><iframe style='margin: 0; padding: 0;' src='${
options.url
}' width='${width}' height='${height}' scrolling='${scroll}' frameborder='0'></iframe></div>`
)
});
modal.show({ backdrop: true });
+2 -4
View File
@@ -17,10 +17,8 @@ var Router = Backbone.Router.extend({
data.__identifer = Math.random()
.toString(36)
.substr(2);
if (!$.isEmptyObject(data)) {
url += url.indexOf("?") == -1 ? "?" : "&";
url += $.param(data, true);
}
url += url.indexOf("?") == -1 ? "?" : "&";
url += $.param(data, true);
Galaxy.params = data;
this.navigate(url, { trigger: true });
},
@@ -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)}`;
}
});
@@ -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 = `<div class="loading-elt-overlay"></div><table><tr><td width="75%">${this.grid_header(
options
)}</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(
options
)}`;
}
// add info text
if (options.info_text) {
tmpl += `<br><div class="toolParamHelp" style="clear: both;">${options.info_text}</div>`;
}
// return
return tmpl;
},
// template
grid_table: function() {
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>`;
},
// template
grid_header: function(options) {
var tmpl = '<div class="grid-header">';
if (!options.embedded) {
tmpl += `<h2>${options.title}</h2>`;
}
if (options.global_actions) {
tmpl += '<ul class="manage-table-actions">';
var show_popup = options.global_actions.length >= 3;
if (show_popup) {
tmpl +=
'<li><a class="action-button" id="popup-global-actions" class="menubutton">Actions</a></li>' +
'<div popupmenu="popup-global-actions">';
}
for (let action of options.global_actions) {
tmpl += `<li><a class="action-button use-target" target="${action.target}" href="${
action.url_args
}" onclick="return false;" >${action.label}</a></li>`;
}
if (show_popup) {
tmpl += "</div>";
}
tmpl += "</ul>";
}
if (options.insert) {
tmpl += options.insert;
}
// add grid filters
tmpl += this.grid_filters(options);
tmpl += "</div>";
// return template
return tmpl;
},
// template
header: function(options) {
// start
var tmpl = "<tr>";
// add checkbox
if (options.show_item_checkboxes) {
tmpl += "<th>";
if (options.items.length > 0) {
tmpl +=
'<input type="checkbox" id="check_all" name=select_all_checkbox value="true">' +
'<input type="hidden" name=select_all_checkbox value="true">';
}
tmpl += "</th>";
}
// create header elements
for (let column of options.columns) {
if (column.visible) {
tmpl += `<th id="${column.key}-header">`;
if (column.href) {
tmpl += `<a href="${column.href}" class="sort-link" sort_key="${column.key}">${column.label}</a>`;
} else {
tmpl += column.label;
}
tmpl += `<span class="sort-arrow">${column.extra}</span></th>`;
}
}
// finalize
tmpl += "</tr>";
// 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 += '<tr><td colspan="100"><em>No Items</em></td></tr>';
}
// create rows
for (let item of options.items) {
// Tag current
tmpl += "<tr ";
if (options.current_item_id == item.id) {
tmpl += 'class="current"';
}
tmpl += ">";
// Item selection column
if (options.show_item_checkboxes) {
tmpl += `<td style="width: 1.5em;"><input type="checkbox" name="id" value="${item.encode_id}" id="${
item.encode_id
}" class="grid-row-select-checkbox" /></td>`;
}
// 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 += `<td ${nowrap}>`;
// Link
if (link) {
if (options.operations.length !== 0) {
tmpl += `<div id="${id}" class="${cls}" style="float: left;">`;
}
tmpl += `<a class="menubutton-label use-target" target="${target}" href="${link}" onclick="return false;">${value}</a>`;
if (options.operations.length !== 0) {
tmpl += "</div>";
}
} else {
tmpl += `<div id="${id}" class="${cls}"><label id="${column.label_id_prefix}${
item.encode_id
}" for="${item.encode_id}">${value || ""}</label></div>`;
}
tmpl += "</td>";
}
}
tmpl += "</tr>";
}
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 += '<tr id="page-links-row">';
if (options.show_item_checkboxes) {
tmpl += "<td></td>";
}
tmpl += '<td colspan="100">' + '<span id="page-link-container">' + "Page:";
if (min_page > 1) {
tmpl +=
'<span class="page-link" id="page-link-1"><a href="javascript:void(0);" page_num="1" onclick="return false;">1</a></span> ...';
}
// create page urls
for (var page_index = min_page; page_index < max_page + 1; page_index++) {
if (page_index == options.cur_page_num) {
tmpl += `<span class="page-link inactive-link" id="page-link-${page_index}">${page_index}</span>`;
} else {
tmpl += `<span class="page-link" id="page-link-${page_index}"><a href="javascript:void(0);" onclick="return false;" page_num="${page_index}">${page_index}</a></span>`;
}
}
// show last page
if (max_page < num_pages) {
tmpl += `...<span class="page-link" id="page-link-${num_pages}"><a href="javascript:void(0);" onclick="return false;" page_num="${num_pages}">${num_pages}</a></span>`;
}
tmpl += "</span>";
// Show all link
tmpl += `
<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>`;
}
// Grid operations for multiple items.
if (options.show_item_checkboxes) {
// start template
tmpl += `
<tr>
<input type="hidden" id="operation" name="operation" value="">
<td></td>
<td colspan="100">
For <span class="grid-selected-count"></span> selected items:
`;
// configure buttons for operations
for (let operation of options.operations) {
if (operation.allow_multiple) {
tmpl += `<input type="button" value="${
operation.label
}" class="operation-button action-button">&nbsp;`;
}
}
// finalize template
tmpl += "</td>" + "</tr>";
}
// 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 += "<tr>" + '<td colspan="100">';
for (let operation of options.operations) {
if (operation.global_operation) {
tmpl += `<a class="action-button" href="${operation.global_operation}">${operation.label}</a>`;
}
}
tmpl += "</td>" + "</tr>";
}
// add legend
if (options.legend) {
tmpl += `<tr><td colspan="100">${options.legend}</td></tr>`;
}
// return
return tmpl;
},
// template
message: function(options) {
var status = options.status;
if (["success", "ok"].indexOf(status) != -1) {
status = "done";
}
return `<p><div class="${status}message transient-message">${_.escape(
options.message
)}</div><div style="clear: both"></div></p>`;
},
// 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 = `<div id="standard-search" style="display: ${standard_search_display};"><table><tr><td style="padding: 0;"><table>`;
// add standard filters
for (let column of options.columns) {
if (column.filterable == "standard") {
tmpl += this.grid_column_filter(options, column);
}
}
// finalize standard search
tmpl += "</table>" + "</td>" + "</tr>" + "<tr>" + "<td>";
// show advanced search link in standard display
if (show_advanced_search_link) {
tmpl += '<a href="" class="advanced-search-toggle">Advanced Search</a>';
}
// finalize standard search display
tmpl += "</td>" + "</tr>" + "</table>" + "</div>";
//
// advanced search
//
tmpl += `<div id="advanced-search" style="display: ${advanced_search_display}; 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>`;
// add advanced filters
for (let column of options.columns) {
if (column.filterable == "advanced") {
tmpl += this.grid_column_filter(options, column);
}
}
// finalize advanced search template
tmpl += "</table>" + "</div>";
// 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 = "<tr>";
if (column.filterable == "advanced") {
tmpl += `<td align="left" style="padding-left: 10px">${column_label}:</td>`;
}
tmpl += '<td style="padding-bottom: 1px;">';
if (column.is_text) {
tmpl += `<form class="text-filter-form" column_key="${column_key}" action="${options.url}" method="get" >`;
// 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 += `<input type="hidden" id="${column.key}" name="f-${
column.key
}" value="${filter_value}"/>`;
}
}
}
// Print current filtering criteria and links to delete.
tmpl += `<span id="${column_key}-filtering-criteria">`;
// 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 += "</span>";
// 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 += `
<span class="search-box">
<input class="search-box-input" id="input-${column_key}-filter" name="f-${column_key}" type="text" placeholder="${value}" size="${size}"/>
<button type="submit" style="background: transparent; border: none; padding: 4px; margin: 0px;">
<i class="fa fa-search"></i>
</button>
</span>
</form>`;
} else {
// filter criteria
tmpl += `<span id="${column_key}-filtering-criteria">`;
// 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 += `<span class="categorical-filter ${column_key}-filter current-filter">${cf_label}</span>`;
} else {
tmpl += `<span class="categorical-filter ${column_key}-filter"><a href="javascript:void(0);" filter_key="${cf_key}" filter_val="${cf_arg}">${cf_label}</a></span>`;
}
}
tmpl += "</span>";
}
tmpl += "</td>" + "</tr>";
// return template
return tmpl;
},
// 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;"/></a></span>`;
}
};
@@ -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("<div/>");
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) == "-" ? "&uarr;" : "&darr;";
var t = $(`<span>${sort_arrow}</span>`).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 = $(`<a href="#">${text}</a>`);
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>
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 = $(`<a href="#">${text}</a>`);
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
});
}
});
}
});
@@ -4754,3 +4754,5 @@ WYMeditor.WymClassSafari.prototype.getTagForStyle = function(style) {
if(/super/.test(style)) return 'sup';
return false;
};
window.WYMeditor = WYMeditor;
@@ -278,8 +278,8 @@ var ListCollectionCreator = Backbone.View.extend(BASE_MVC.LoggableMixin)
/** describe what is wrong with a particular element if anything */
_isElementInvalid: function(element) {
if (element.history_content_type !== "dataset") {
return _l("is not a dataset");
if (element.history_content_type === "dataset_collection") {
return _l("is a collection, this is not allowed");
}
var validState = element.state === STATES.OK || _.contains(STATES.NOT_READY_STATES, element.state);
if (!validState) {
@@ -1796,7 +1796,8 @@ var pairedCollectionCreatorModal = function _pairedCollectionCreatorModal(datase
oncreate: function(creator, response) {
Galaxy.modal.hide();
deferred.resolve(response);
}
},
title: _l("Create a collection of paired datasets")
});
if (!window.Galaxy || !Galaxy.modal) {
@@ -1805,7 +1806,7 @@ var pairedCollectionCreatorModal = function _pairedCollectionCreatorModal(datase
creator = new PairedCollectionCreator(options);
Galaxy.modal.show({
title: _l("Create a collection of paired datasets"),
title: options.title,
body: creator.$el,
width: "80%",
height: "800px",
+1 -3
View File
@@ -1,8 +1,6 @@
import _l from "utils/localization";
// Additional dependencies: jQuery, underscore.
import Modal from "mvc/ui/ui-modal";
import Frames from "mvc/ui/ui-frames";
import mod_icon_btn from "mvc/ui/icon-button";
// Additional dependencies: jQuery, underscore.
/**
* Dataset metedata.
*/
@@ -16,53 +16,54 @@ var View = Backbone.View.extend({
render: function() {
var data_url = `${Galaxy.root}api/datasets/${this.model.get("dataset_id")}`;
var self = this;
Utils.get({
url: data_url,
success: function(dataset) {
success: dataset => {
var job_url = `${Galaxy.root}api/jobs/${dataset.creating_job}?full=True`;
Utils.get({
url: job_url,
success: function(job) {
var job_url = `${Galaxy.root}api/jobs/${dataset.creating_job}?full=True`;
self.render_error_page(self, dataset, job);
success: job => {
this.render_error_page(dataset, job);
},
error: function(response) {
error: response => {
var error_response = {
status: "error",
message: "Error occured while loading the job.",
persistent: true,
cls: "errormessage"
};
self.display_message(error_response, self.$(".response-message"));
this.display_message(error_response, this.$(".response-message"));
}
});
},
error: function(response) {
error: response => {
var error_response = {
status: "error",
message: "Error occured while loading the dataset.",
persistent: true,
cls: "errormessage"
};
self.display_message(error_response, self.$(".response-message"));
this.display_message(error_response, this.$(".response-message"));
}
});
},
/** Render the view */
render_error_page: function(self, dataset, job) {
self.$el.empty().append(self._templateHeader());
self.$el.append("<h2>Dataset Error</h2>");
self.$el.append(`<p>An error occured while running the tool <b>${job.tool_id}</b>.</p>`);
self.$el.append("<p>Tool execution generated the following messages:</p>");
self.$el.append(`<pre class="code">${job.stderr}</pre>`);
self.$el.append("<h2>Report This Error</pre>");
self.$el.append(
"<p>Usually the local Galaxy administrators regularly review errors that occur on the server. However, if you would like to provide additional information (such as what you were trying to do when the error occurred) and a contact e-mail address, we will be better able to investigate your problem and get back to you.</p>"
);
self.$el.append(self._getBugFormTemplate(dataset, job));
render_error_page: function(dataset, job) {
this.$el.empty().append(`
${this._templateHeader()}
<h2>Dataset Error</h2>
<p>An error occured while running the tool <b>${job.tool_id}</b>.</p>
<p>Tool execution generated the following messages:</p>
<pre class="code">${_.escape(job.stderr)}</pre>
<h2>Report This Error</h2>
<p>
Usually the local Galaxy administrators regularly review errors that occur on the server
However, if you would like to provide additional information (such as what you were trying
to do when the error occurred) and a contact e-mail address, we will be better able to
investigate your problem and get back to you.
</p>`);
this.$el.append(this._getBugFormTemplate(dataset, job));
},
/** Display actions messages */
@@ -84,12 +85,11 @@ var View = Backbone.View.extend({
/** Main template */
_templateHeader: function() {
return '<div class="page-container edit-attr">' + '<div class="response-message"></div>' + "</div>";
return `<div class="page-container edit-attr"><div class="response-message"></div></div>`;
},
/** Convert tab template */
_getBugFormTemplate: function(dataset, job) {
var self = this;
var inputs = [
{
help: _l("Your email address"),
@@ -135,11 +135,11 @@ var View = Backbone.View.extend({
title: _l("Report"),
cls: "ui-button btn btn-primary",
floating: "clear",
onclick: function() {
onclick: () => {
var form_data = form.data.create();
var url = `${Galaxy.root}api/jobs/${job.id}/error`;
form_data.dataset_id = dataset.id;
self.submit(form_data, url);
this.submit(form_data, url);
}
})
}
@@ -149,37 +149,36 @@ var View = Backbone.View.extend({
/** Make ajax request */
submit: function(form_data, url) {
var self = this;
// Some required metadata
$.ajax({
type: "POST",
url: url,
data: form_data,
success: function(response) {
success: response => {
// Clear out the div
self.$el.empty().append(self._templateHeader());
this.$el.empty().append(this._templateHeader());
// And display the messages.
response.messages.forEach(message => {
self.display_message(
this.display_message(
{
status: message[1],
message: message[0],
persistent: true
},
self.$(".response-message"),
this.$(".response-message"),
true,
true
);
});
},
error: function(response) {
error: response => {
var error_response = {
status: "error",
message: "Error occured while saving. Please fill all the required fields and try again.",
persistent: true,
cls: "errormessage"
};
self.display_message(error_response, self.$(".response-message"));
this.display_message(error_response, this.$(".response-message"));
}
});
}
+29 -37
View File
@@ -286,47 +286,39 @@ var DatasetListItemView = _super.extend(
return this._renderMetaFileDownloadButton();
}
return $(
[
'<a class="download-btn icon-btn" ',
'href="',
this.model.urls.download,
`" title="${_l("Download")}" download>`,
'<span class="fa fa-floppy-o"></span>',
"</a>"
].join("")
);
return $(`
<a class="download-btn icon-btn" href="${this.model.urls.download}" title="${_l("Download")}">
<span class="fa fa-floppy-o"></span>
</a>`);
},
/** Render the download button which opens a dropdown with links to download assoc. meta files (indeces, etc.) */
/** Render the download button which opens a dropdown with links to download assoc. meta files (indices, etc.) */
_renderMetaFileDownloadButton: function() {
var urls = this.model.urls;
return $(
[
'<div class="metafile-dropdown dropdown">',
'<a class="download-btn icon-btn" href="javascript:void(0)" data-toggle="dropdown"',
` title="${_l("Download")}">`,
'<span class="fa fa-floppy-o"></span>',
"</a>",
'<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">',
`<li><a href="${urls.download}" download>`,
_l("Download dataset"),
"</a></li>",
_.map(this.model.get("meta_files"), meta_file =>
[
'<li><a href="',
urls.meta_download + meta_file.file_type,
'">',
_l("Download"),
" ",
meta_file.file_type,
"</a></li>"
].join("")
).join("\n"),
"</ul>",
"</div>"
].join("\n")
);
return $(`
<div class="metafile-dropdown dropdown">
<a class="download-btn icon-btn" href="${urls.download}" data-toggle="dropdown" title="${_l(
"Download"
)}">
<span class="fa fa-floppy-o"></span>
</a>
<ul class="dropdown-menu" role="menu" aria-labelledby="dLabel">
<li>
<a href="${urls.download}">
${_l("Download dataset")}
</a>
</li>
${_.map(
this.model.get("meta_files"),
meta_file =>
`<li>
<a href="${urls.meta_download + meta_file.file_type}">
${_l("Download")} ${meta_file.file_type}
</a>
</li>`
)}
</ul>
</div>`);
},
_renderNametags: function() {
@@ -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.
*/
@@ -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();
}
+11 -38
View File
@@ -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 += '<tr><td colspan="100"><em>No Items</em></td></tr>';
num_rows_rendered = 1;
}
// create rows
@@ -180,9 +178,7 @@ export default {
if (options.operations.length !== 0) {
tmpl += `<div id="${id}" class="${cls}" style="float: left;">`;
}
tmpl += `<a class="menubutton-label use-target" target="${target}" href="${
link
}" onclick="return false;">${value}</a>`;
tmpl += `<a class="menubutton-label use-target" target="${target}" href="${link}" onclick="return false;">${value}</a>`;
if (options.operations.length !== 0) {
tmpl += "</div>";
}
@@ -195,7 +191,6 @@ export default {
}
}
tmpl += "</tr>";
num_rows_rendered++;
}
return tmpl;
},
@@ -262,21 +257,13 @@ export default {
if (page_index == options.cur_page_num) {
tmpl += `<span class="page-link inactive-link" id="page-link-${page_index}">${page_index}</span>`;
} else {
tmpl += `<span class="page-link" id="page-link-${
page_index
}"><a href="javascript:void(0);" onclick="return false;" page_num="${page_index}">${
page_index
}</a></span>`;
tmpl += `<span class="page-link" id="page-link-${page_index}"><a href="javascript:void(0);" onclick="return false;" page_num="${page_index}">${page_index}</a></span>`;
}
}
// show last page
if (max_page < num_pages) {
tmpl += `...<span class="page-link" id="page-link-${
num_pages
}"><a href="javascript:void(0);" onclick="return false;" page_num="${num_pages}">${
num_pages
}</a></span>`;
tmpl += `...<span class="page-link" id="page-link-${num_pages}"><a href="javascript:void(0);" onclick="return false;" page_num="${num_pages}">${num_pages}</a></span>`;
}
tmpl += "</span>";
@@ -386,9 +373,7 @@ export default {
//
// standard search
//
var tmpl = `<div id="standard-search" style="display: ${
standard_search_display
};"><table><tr><td style="padding: 0;"><table>`;
var tmpl = `<div id="standard-search" style="display: ${standard_search_display};"><table><tr><td style="padding: 0;"><table>`;
// add standard filters
for (let column of options.columns) {
@@ -411,9 +396,7 @@ export default {
//
// advanced search
//
tmpl += `<div id="advanced-search" style="display: ${
advanced_search_display
}; 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>`;
tmpl += `<div id="advanced-search" style="display: ${advanced_search_display}; 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>`;
// add advanced filters
for (let column of options.columns) {
@@ -456,9 +439,9 @@ export default {
if (column.is_text) {
filter_value = JSON.stringify(filter_value);
}
tmpl += `<input type="hidden" id="${column.key}" name="f-${column.key}" value="${
filter_value
}"/>`;
tmpl += `<input type="hidden" id="${column.key}" name="f-${
column.key
}" value="${filter_value}"/>`;
}
}
}
@@ -510,9 +493,7 @@ export default {
// print input field for column
tmpl += `
<span class="search-box">
<input class="search-box-input" id="input-${column_key}-filter" name="f-${
column_key
}" type="text" placeholder="${value}" size="${size}"/>
<input class="search-box-input" id="input-${column_key}-filter" name="f-${column_key}" type="text" placeholder="${value}" size="${size}"/>
<button type="submit" style="background: transparent; border: none; padding: 4px; margin: 0px;">
<i class="fa fa-search"></i>
</button>
@@ -547,11 +528,7 @@ export default {
if (filter && cf[column_key] && filter == cf_arg) {
tmpl += `<span class="categorical-filter ${column_key}-filter current-filter">${cf_label}</span>`;
} else {
tmpl += `<span class="categorical-filter ${
column_key
}-filter"><a href="javascript:void(0);" filter_key="${cf_key}" filter_val="${cf_arg}">${
cf_label
}</a></span>`;
tmpl += `<span class="categorical-filter ${column_key}-filter"><a href="javascript:void(0);" filter_key="${cf_key}" filter_val="${cf_arg}">${cf_label}</a></span>`;
}
}
tmpl += "</span>";
@@ -565,10 +542,6 @@ export default {
// 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;"/></a></span>`;
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;"/></a></span>`;
}
};
+18 -35
View File
@@ -15,7 +15,6 @@ export default Backbone.View.extend({
// 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) => {
@@ -30,26 +29,21 @@ export default Backbone.View.extend({
};
// set element
if (this.dict_format) {
this.setElement("<div/>");
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);
}
this.setElement("<div/>");
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.setElement("#grid-container");
this.init_grid(grid_config);
}
@@ -570,10 +564,8 @@ export default Backbone.View.extend({
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);
this.update_grid();
}
// done
@@ -587,11 +579,7 @@ export default Backbone.View.extend({
}
// refresh grid
if (this.grid.get("async") || this.dict_format) {
this.update_grid();
} else {
this.go_to(target, href);
}
this.update_grid();
// done
return false;
@@ -599,10 +587,6 @@ export default Backbone.View.extend({
// 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);
@@ -615,8 +599,7 @@ export default Backbone.View.extend({
// clear grid of transient request attributes.
this.grid.set({
operation: undefined,
item_ids: undefined,
async: async
item_ids: undefined
});
switch (target) {
case "center":
@@ -652,7 +635,7 @@ export default Backbone.View.extend({
var advanced_search = self.$el.find("#advanced-search").is(":visible");
// request new configuration
var json = self.dict_format ? response_text : $.parseJSON(response_text);
var json = response_text;
// update
json.embedded = embedded;
+1 -3
View File
@@ -75,9 +75,7 @@ var HDCAListItemView = _super.extend(
} else if (!jobStatesSummary || !jobStatesSummary.hasDetails()) {
return `
<div class="progress state-progress">
<span class="note">Loading job data for ${
collectionTypeDescription
}.<span class="blinking">..</span></span>
<span class="note">Loading job data for ${collectionTypeDescription}.<span class="blinking">..</span></span>
<div class="progress-bar info" style="width:100%">
</div>`;
} else {
@@ -1,15 +1,14 @@
function _labelIfName(tag) {
if (tag.indexOf("name:") == 0) {
return `<span class="label label-info">${_.escape(tag.slice(5))}</span>`;
} else {
return "";
}
function _templateNametag(tag) {
return `<span class="label label-info">${_.escape(tag.slice(5))}</span>`;
}
function nametagTemplate(historyItem) {
return `<span class="nametags">${_.sortBy(_.uniq(historyItem.tags))
.map(_labelIfName)
.join("")}</span>`;
let uniqueNametags = _.filter(_.uniq(historyItem.tags), t => t.indexOf("name:") === 0);
let nametagsDisplay = _.sortBy(uniqueNametags).map(_templateNametag);
return `
<div class="nametags" title="${uniqueNametags.length} nametags">
${nametagsDisplay.join("")}
</div>`;
}
export default {
@@ -60,7 +60,6 @@ var View = Backbone.View.extend({
Utils.get({
url: `${Galaxy.root}history/${options.action_id}?${$.param(Galaxy.params)}`,
success: function(response) {
response.dict_format = true;
self.model.set(response);
self.render();
}
File diff suppressed because it is too large Load Diff
@@ -124,9 +124,9 @@ var LibraryView = Backbone.View.extend({
placeholder: "Click to select a role",
container: self.$el.find(`#${id}`),
ajax: {
url: `${Galaxy.root}api/libraries/${self.id}/permissions?scope=available&is_library_access=${
is_library_access
}`,
url: `${Galaxy.root}api/libraries/${
self.id
}/permissions?scope=available&is_library_access=${is_library_access}`,
dataType: "json",
quietMillis: 100,
data: function(term, page) {
@@ -7,7 +7,7 @@ import Deferred from "utils/deferred";
import Ui from "mvc/ui/ui-misc";
import FormBase from "mvc/form/form-view";
import Citations from "components/Citations.vue";
import Vue from "libs/vue";
import Vue from "vue";
export default FormBase.extend({
initialize: function(options) {
var self = this;
@@ -119,7 +119,7 @@ var View = Backbone.View.extend({
input.help = input.step_linked ? `${input.help}, ` : "";
input.help += `Output dataset '${connection.output_name}' from step ${parseInt(i) + 1}`;
input.step_linked = input.step_linked || [];
input.step_linked.push(step);
input.step_linked.push({ index: step.index, step_type: step.step_type });
}
});
});
@@ -200,6 +200,7 @@ var View = Backbone.View.extend({
this._renderMessage();
this._renderParameters();
this._renderHistory();
this._renderUseCachedJob();
_.each(this.steps, step => {
self._renderStep(step);
});
@@ -311,6 +312,39 @@ var View = Backbone.View.extend({
this._append(this.$steps, this.history_form.$el);
},
/** Render job caching option */
_renderUseCachedJob: function() {
var extra_user_preferences = {};
if (Galaxy.user.attributes.preferences && "extra_user_preferences" in Galaxy.user.attributes.preferences) {
extra_user_preferences = JSON.parse(Galaxy.user.attributes.preferences.extra_user_preferences);
}
var display_use_cached_job_checkbox =
"use_cached_job|use_cached_job_checkbox" in extra_user_preferences
? extra_user_preferences["use_cached_job|use_cached_job_checkbox"]
: false;
this.display_use_cached_job_checkbox = display_use_cached_job_checkbox === "true";
if (this.display_use_cached_job_checkbox) {
this.job_options_form = new Form({
cls: "ui-portlet-narrow",
title: "<b>Job re-use Options</b>",
inputs: [
{
type: "conditional",
name: "use_cached_job",
test_param: {
name: "check",
label: "BETA: Attempt to reuse jobs with identical parameters?",
type: "boolean",
value: "false",
help: "This may skip executing jobs that you have already run."
}
}
]
});
this._append(this.$steps, this.job_options_form.$el);
}
},
/** Render step */
_renderStep: function(step) {
var self = this;
@@ -510,6 +544,9 @@ var View = Backbone.View.extend({
// so that inputs can be batched.
batch: true
};
if (this.display_use_cached_job_checkbox) {
job_def["use_cached_job"] = this.job_options_form.data.create()["use_cached_job|check"] === "true";
}
var validated = true;
for (var i in this.forms) {
var form = this.forms[i];
+23 -3
View File
@@ -148,6 +148,28 @@ var View = Backbone.View.extend({
"The previous run of this tool failed and other tools were waiting for it to finish successfully. Use this option to resume those tools using the new output(s) of this tool run."
});
}
// Job Re-use Options
var extra_user_preferences = {};
if (Galaxy.user.attributes.preferences && "extra_user_preferences" in Galaxy.user.attributes.preferences) {
extra_user_preferences = JSON.parse(Galaxy.user.attributes.preferences.extra_user_preferences);
}
var use_cached_job =
"use_cached_job|use_cached_job_checkbox" in extra_user_preferences
? extra_user_preferences["use_cached_job|use_cached_job_checkbox"]
: false;
if (use_cached_job === "true") {
options.inputs.push({
label: "BETA: Attempt to re-use jobs with identical parameters ?",
help: "This may skip executing jobs that you have already run",
name: "use_cached_job",
type: "select",
display: "radio",
ignore: "__ignore__",
value: "__ignore__",
options: [["No", false], ["Yes", true]]
});
}
},
/** Submit a regular job.
@@ -269,9 +291,7 @@ var View = Backbone.View.extend({
} else if (batch_n !== n) {
this.form.highlight(
input_id,
`Please make sure that you select the same number of inputs for all batch mode fields. This field contains <b>${
n
}</b> selection(s) while a previous field contains <b>${batch_n}</b>.`
`Please make sure that you select the same number of inputs for all batch mode fields. This field contains <b>${n}</b> selection(s) while a previous field contains <b>${batch_n}</b>.`
);
return false;
}
@@ -48,7 +48,7 @@ var ToolShedRepositoryView = Backbone.View.extend({
this.options.tps_select_options = this.templatePanelSelectOptions;
this.options.tool_dependencies = models.get("tool_dependencies");
this.options.shed_tool_conf = this.templateShedToolConf({
stc_html: models.get("shed_conf")
shed_tool_confs: models.get("shed_conf")
});
this.options.panel_section_dict = models.get("panel_section_dict");
this.options.api_url = `${Galaxy.root}api/tool_shed_repositories/install?async=True`;
@@ -474,7 +474,11 @@ var ToolShedRepositoryView = Backbone.View.extend({
'<div class="toolFormTitle">Shed tool configuration file:</div>',
'<div class="toolFormBody">',
'<div class="form-row">',
"<%= stc_html %>",
'<select name="shed_tool_conf">',
"<% _.each(shed_tool_confs.options, function(conf) { %>",
'<option value="<%= conf.value %>"><%= conf.label %></option>',
"<% }); %>",
"</select>",
'<div class="toolParamHelp" style="clear: both;">Select the file whose <b>tool_path</b> setting you want used for installing repositories.</div>',
"</div>",
"</div>"
+10 -11
View File
@@ -39,17 +39,16 @@ export var View = Backbone.View.extend({
if (!this.visible) {
this.visible = true;
this.$el.fadeIn("fast");
if (this.options.closing_events) {
var self = this;
$(document).on("keyup.ui-modal", e => {
if (e.keyCode == 27) {
self.hide(true);
}
});
this.$backdrop.on("click", () => {
self.hide(true);
});
}
}
if (this.options.closing_events) {
$(document).on("keyup.ui-modal", e => {
if (e.keyCode == 27) {
this.hide(true);
}
});
this.$backdrop.on("click", () => {
this.hide(true);
});
}
},
@@ -5,7 +5,6 @@ import Utils from "utils/utils";
import Buttons from "mvc/ui/ui-buttons";
var View = Backbone.View.extend({
initialize: function(options) {
var self = this;
this.data = [];
this.data2 = [];
this.model =
@@ -28,7 +27,9 @@ var View = Backbone.View.extend({
pagesize: 20
}).set(options);
this.on("change", () => {
self.model.get("onchange") && self.model.get("onchange")(self.value());
if (this.model.get("onchange")) {
this.model.get("onchange")(this.value());
}
});
this.listenTo(this.model, "change:data", this._changeData, this);
this.listenTo(this.model, "change:disabled", this._changeDisabled, this);
@@ -40,8 +41,11 @@ var View = Backbone.View.extend({
},
render: function() {
var self = this;
this.model.get("searchable") ? this._renderSearchable() : this._renderClassic();
if (this.model.get("searchable")) {
this._renderSearchable();
} else {
this._renderClassic();
}
this.$el.addClass(this.model.get("cls")).attr("id", this.model.get("id"));
this.$select
.empty()
@@ -49,8 +53,8 @@ var View = Backbone.View.extend({
.attr("id", `${this.model.get("id")}_select`)
.prop("multiple", this.model.get("multiple"))
.on("change", () => {
self.value(self._getValue());
self.trigger("change");
this.value(this._getValue());
this.trigger("change");
});
this._changeData();
this._changeWait();
@@ -60,7 +64,6 @@ var View = Backbone.View.extend({
/** Renders the classic selection field */
_renderClassic: function() {
var self = this;
this.$el
.addClass(this.model.get("multiple") ? "ui-select-multiple" : "ui-select")
.append((this.$select = $("<select/>")))
@@ -76,12 +79,12 @@ var View = Backbone.View.extend({
.off("mousedown")
.on("mousedown", event => {
var currentY = event.pageY;
var currentHeight = self.$select.height();
self.minHeight = self.minHeight || currentHeight;
var currentHeight = this.$select.height();
this.minHeight = this.minHeight || currentHeight;
$("#dd-helper")
.show()
.on("mousemove", event => {
self.$select.height(Math.max(currentHeight + (event.pageY - currentY), self.minHeight));
this.$select.height(Math.max(currentHeight + (event.pageY - currentY), this.minHeight));
})
.on("mouseup mouseleave", () => {
$("#dd-helper")
@@ -98,25 +101,27 @@ var View = Backbone.View.extend({
/** Renders the default select2 field */
_renderSearchable: function() {
var self = this;
this.$el.append((this.$select = $("<div/>"))).append((this.$dropdown = $("<div/>")));
this.$dropdown.hide();
if (!this.model.get("multiple")) {
this.$dropdown.show().on("click", () => {
self.$select.select2 && self.$select.select2("open");
if (this.$select.select2) {
this.$select.select2("open");
}
});
}
this.all_button = null;
if (this.model.get("multiple") && !this.model.get("individual") && !this.model.get("readonly")) {
this.all_button = new Buttons.ButtonCheck({
onclick: function() {
onclick: () => {
var new_value = [];
self.all_button.value() !== 0 &&
_.each(self.model.get("data"), option => {
if (this.all_button.value() !== 0) {
_.each(this.model.get("data"), option => {
new_value.push(option.value);
});
self.value(new_value);
self.trigger("change");
}
this.value(new_value);
this.trigger("change");
}
});
this.$el.prepend(this.all_button.$el);
@@ -127,7 +132,7 @@ var View = Backbone.View.extend({
_match: function(term, text) {
return (
!term ||
term == "" ||
term === "" ||
String(text)
.toUpperCase()
.indexOf(term.toUpperCase()) >= 0
@@ -136,18 +141,17 @@ var View = Backbone.View.extend({
/** Updates the selection options */
_changeData: function() {
var self = this;
this.data = [];
if (!this.model.get("multiple") && this.model.get("optional")) {
this.data.push({
value: "__null__",
label: self.model.get("empty_text")
label: this.model.get("empty_text")
});
}
_.each(this.model.get("data"), option => {
self.data.push(option);
this.data.push(option);
});
if (this.length() == 0) {
if (this.length() === 0) {
this.data.push({
value: "__null__",
label: this.model.get("error_text")
@@ -156,61 +160,71 @@ var View = Backbone.View.extend({
if (this.model.get("searchable")) {
this.data2 = [];
_.each(this.data, (option, index) => {
self.data2.push({
this.data2.push({
order: index,
id: option.value,
text: option.label,
tags: option.tags
});
});
this.$select.data("select2") && this.$select.select2("destroy");
if (this.$select.data("select2")) {
this.$select.select2("destroy");
}
this.matched_tags = {};
this.$select.select2({
data: self.data2,
data: this.data2,
closeOnSelect: !this.model.get("multiple"),
multiple: this.model.get("multiple"),
query: function(q) {
self.matched_tags = {};
var pagesize = self.model.get("pagesize");
var results = _.filter(self.data2, e => {
query: q => {
this.matched_tags = {};
var pagesize = this.model.get("pagesize");
var results = _.filter(this.data2, e => {
var found = false;
_.each(e.tags, tag => {
if (self._match(q.term, tag)) {
found = self.matched_tags[tag] = true;
if (this._match(q.term, tag)) {
found = this.matched_tags[tag] = true;
}
});
return found || self._match(q.term, e.text);
return found || this._match(q.term, e.text);
});
q.callback({
results: results.slice((q.page - 1) * pagesize, q.page * pagesize),
more: results.length >= q.page * pagesize
});
},
formatResult: function(result) {
return `${_.escape(result.text)}<div class="ui-tags">${_.reduce(
result.tags,
(memo, tag) => {
if (self.matched_tags[tag]) {
formatResult: result => {
let extraTagWarning = "";
let filteredTags = _.filter(result.tags, t => this.matched_tags.hasOwnProperty(t));
if (filteredTags.length > 5) {
extraTagWarning = `&nbsp;<div class="label label-warning">${filteredTags.length -
5} more tags</div>`;
}
return `
${_.escape(result.text)}
<div class="ui-tags">
${_.reduce(
filteredTags.slice(0, 5),
(memo, tag) => {
return `${memo}&nbsp;<div class="label label-info">${_.escape(tag)}</div>`;
}
return memo;
},
""
)}</div>`;
},
""
)}
${extraTagWarning}
</div>`;
}
});
this.$(".select2-container .select2-search input").off("blur");
} else {
this.$select.find("option").remove();
_.each(this.data, option => {
self.$select.append(
this.$select.append(
$("<option/>")
.attr("value", option.value)
.html(_.escape(option.label))
);
});
}
this.model.set("disabled", this.model.get("readonly") || this.length() == 0);
this.model.set("disabled", this.model.get("readonly") || this.length() === 0);
this._changeValue();
},
@@ -252,7 +266,9 @@ var View = Backbone.View.extend({
/** Return/Set current selection */
value: function(new_value) {
new_value !== undefined && this.model.set("value", new_value);
if (new_value !== undefined) {
this.model.set("value", new_value);
}
return this._getValue();
},
@@ -311,9 +327,13 @@ var View = Backbone.View.extend({
/** Update all available options at once */
add: function(options, sorter) {
_.each(this.model.get("data"), v => {
v.keep && !_.findWhere(options, { value: v.value }) && options.push(v);
if (v.keep && !_.findWhere(options, { value: v.value })) {
options.push(v);
}
});
sorter && options && options.sort(sorter);
if (sorter && options) {
options.sort(sorter);
}
this.model.set("data", options);
},
@@ -334,7 +354,6 @@ var View = Backbone.View.extend({
/** Set value to dom */
_setValue: function(new_value) {
var self = this;
if (new_value === null || new_value === undefined) {
new_value = "__null__";
}
@@ -351,8 +370,10 @@ var View = Backbone.View.extend({
if ($.isArray(new_value)) {
var val = [];
_.each(new_value, v => {
var d = _.findWhere(self.data2, { id: v });
d && val.push(d);
var d = _.findWhere(this.data2, { id: v });
if (d) {
val.push(d);
}
});
new_value = val;
} else {
@@ -1,8 +1,7 @@
// dependencies
import Utils from "utils/utils";
import Ui from "mvc/ui/ui-misc";
import Table from "mvc/ui/ui-table";
import List from "mvc/ui/ui-list";
// collection of libraries
var Libraries = Backbone.Collection.extend({
url: `${Galaxy.root}api/libraries?deleted=false`
@@ -85,7 +84,9 @@ var View = Backbone.View.extend({
// add change event. fires on trigger
this.on("change", () => {
options.onchange && options.onchange(self.value());
if (options.onchange) {
options.onchange(self.value());
}
});
// create elements
@@ -62,7 +62,7 @@ var Model = Backbone.Model.extend({
description: _l("Associate OpenIDs with your account."),
icon: "fa-openid",
onclick: function() {
window.location.href = `${Galaxy.root}user/openid_manage?cntrller=user&use_panels=True`;
window.location.href = `${Galaxy.root}openids/list`;
}
},
custom_builds: {
@@ -106,7 +106,7 @@ $.extend(CanvasManager.prototype, {
// If it appears that the user is trying to copy/paste text, we
// pass that through.
if (window.getSelection().toString() === "") {
if (this.app.workflow.active_node) {
if (this.app.workflow.active_node && this.app.workflow.active_node.type !== "subworkflow") {
e.clipboardData.setData(
"application/json",
JSON.stringify({
@@ -35,9 +35,7 @@ $.extend(Workflow.prototype, {
}
if (!this.canLabelOutputWith(toLabel)) {
Toastr.warning(
`Workflow contains duplicate workflow output labels ${
toLabel
}. This must be fixed before it can be saved.`
`Workflow contains duplicate workflow output labels ${toLabel}. This must be fixed before it can be saved.`
);
}
if (toLabel) {
@@ -179,13 +179,19 @@ var Node = Backbone.Model.extend({
// Remove active class
$(element).removeClass("toolForm-active");
},
set_tool_version: function() {
if (this.config_form) {
this.tool_version = this.config_form.version;
this.content_id = this.config_form.id;
}
},
init_field_data: function(data) {
if (data.type) {
this.type = data.type;
}
this.name = data.name;
this.config_form = data.config_form;
this.tool_version = this.config_form && this.config_form.version;
this.set_tool_version();
this.tool_state = data.tool_state;
this.errors = data.errors;
this.tooltip = data.tooltip ? data.tooltip : "";
@@ -263,7 +269,7 @@ var Node = Backbone.Model.extend({
});
this.tool_state = data.tool_state;
this.config_form = data.config_form;
this.tool_version = this.config_form && this.config_form.version;
this.set_tool_version();
this.errors = data.errors;
this.annotation = data.annotation;
this.label = data.label;
@@ -277,9 +277,7 @@ export default Backbone.View.extend({
if (upgrade_message) {
window.show_modal(
"Issues loading this workflow",
`Please review the following issues, possibly resulting from tool upgrades or changes.<p><ul>${
upgrade_message
}</ul></p>`,
`Please review the following issues, possibly resulting from tool upgrades or changes.<p><ul>${upgrade_message}</ul></p>`,
{ Continue: hide_modal }
);
} else {
@@ -560,9 +558,7 @@ export default Backbone.View.extend({
if (upgrade_message) {
window.show_modal(
"Subworkflow embedded with changes",
`Problems were encountered loading this workflow (possibly a result of tool upgrades). Please review the following parameters and then save.<ul>${
upgrade_message
}</ul>`,
`Problems were encountered loading this workflow (possibly a result of tool upgrades). Please review the following parameters and then save.<ul>${upgrade_message}</ul>`,
{ Continue: hide_modal }
);
} else {
@@ -764,13 +760,15 @@ export default Backbone.View.extend({
// Fix width to computed width
// Now add floats
var buttons = $("<div class='buttons' style='float: right;'></div>");
buttons.append(
$("<div/>")
.addClass("fa-icon-button fa fa-files-o")
.click(e => {
node.clone();
})
);
if (type !== "subworkflow") {
buttons.append(
$("<div/>")
.addClass("fa-icon-button fa fa-files-o")
.click(e => {
node.clone();
})
);
}
buttons.append(
$("<div/>")
.addClass("fa-icon-button fa fa-times")
+45 -49
View File
@@ -5,7 +5,7 @@ import WORKFLOWS from "mvc/workflow/workflow-model";
import QueryStringParsing from "utils/query-string-parsing";
import _l from "utils/localization";
/** View of the individual workflows */
var WorkflowItemView = Backbone.View.extend({
const WorkflowItemView = Backbone.View.extend({
tagName: "tr", // name of (orphan) root tag in this.el
initialize: function() {
_.bindAll(
@@ -33,15 +33,20 @@ var WorkflowItemView = Backbone.View.extend({
},
showInToolPanel: function() {
this.model.set("show_in_tool_panel", !this.model.get("show_in_tool_panel"));
this.model.save();
// This reloads the whole page, so that the workflow appears in the tool panel.
// Ideally we would notify only the tool panel of a change
window.location = `${Galaxy.root}workflows/list`;
this.model.save(
{ show_in_tool_panel: !this.model.get("show_in_tool_panel") },
{
success: function() {
window.location = `${Galaxy.root}workflows/list`;
}
}
);
},
removeWorkflow: function() {
var wfName = this.model.get("name");
const wfName = this.model.get("name");
if (window.confirm(`Are you sure you want to delete workflow '${wfName}'?`)) {
this.model.destroy({
success: function() {
@@ -53,8 +58,8 @@ var WorkflowItemView = Backbone.View.extend({
},
renameWorkflow: function() {
var oldName = this.model.get("name");
var newName = window.prompt(`Enter a new Name for workflow '${oldName}'`, oldName);
const oldName = this.model.get("name");
const newName = window.prompt(`Enter a new Name for workflow '${oldName}'`, oldName);
if (newName) {
this.model.save(
{ name: newName },
@@ -69,16 +74,15 @@ var WorkflowItemView = Backbone.View.extend({
},
copyWorkflow: function() {
var self = this;
var oldName = this.model.get("name");
const oldName = this.model.get("name");
$.getJSON(`${this.model.urlRoot}/${this.model.id}/download`, wfJson => {
var newName = `Copy of ${oldName}`;
var currentOwner = self.model.get("owner");
let newName = `Copy of ${oldName}`;
const currentOwner = this.model.get("owner");
if (currentOwner != Galaxy.user.attributes.username) {
newName += ` shared by user ${currentOwner}`;
}
wfJson.name = newName;
self.collection.create(wfJson, {
this.collection.create(wfJson, {
at: 0,
wait: true,
success: function() {
@@ -96,16 +100,14 @@ var WorkflowItemView = Backbone.View.extend({
},
_rowTemplate: function() {
var show = this.model.get("show_in_tool_panel");
var wfId = this.model.id;
var checkboxHtml = `<input id="show-in-tool-panel" type="checkbox" class="show-in-tool-panel" ${
let show = this.model.get("show_in_tool_panel");
let wfId = this.model.id;
const checkboxHtml = `<input id="show-in-tool-panel" type="checkbox" class="show-in-tool-panel" ${
show ? `checked="${show}"` : ""
} value="${wfId}">`;
var trHtml = `<td><div class="dropdown"><button class="menubutton" type="button" data-toggle="dropdown">${_.escape(
const trHtml = `<td><div class="dropdown"><button class="menubutton" type="button" data-toggle="dropdown">${_.escape(
this.model.get("name")
)}<span class="caret"></span></button>${this._templateActions()}</div></td><td><span><div class="${
wfId
} tags-display"></div></td><td>${
)}<span class="caret"></span></button>${this._templateActions()}</div></td><td><span><div class="${wfId} tags-display"></div></td><td>${
this.model.get("owner") === Galaxy.user.attributes.username ? "You" : this.model.get("owner")
}</span></td><td>${this.model.get("number_of_steps")}</td><td>${
this.model.get("published") ? "Yes" : "No"
@@ -114,7 +116,7 @@ var WorkflowItemView = Backbone.View.extend({
},
renderTagEditor: function() {
var TagEditor = new TAGS.TagsEditor({
const TagEditor = new TAGS.TagsEditor({
model: this.model,
el: $.find(`.${this.model.id}.tags-display`),
workflow_mode: true
@@ -152,7 +154,7 @@ var WorkflowItemView = Backbone.View.extend({
});
/** View of the main workflow list page */
var WorkflowListView = Backbone.View.extend({
const WorkflowListView = Backbone.View.extend({
title: _l("Workflows"),
initialize: function() {
this.setElement("<div/>");
@@ -182,18 +184,16 @@ var WorkflowListView = Backbone.View.extend({
// TODO: check that file is valid galaxy workflow
this.unhighlightDropZone();
e.preventDefault();
var files = e.dataTransfer.files;
var self = this;
for (var i = 0, f; (f = files[i]); i++) {
self.readWorkflowFiles(f);
const files = e.dataTransfer.files;
for (let i = 0, f; (f = files[i]); i++) {
this.readWorkflowFiles(f);
}
},
readWorkflowFiles: function(f) {
var self = this;
var reader = new FileReader();
const reader = new FileReader();
reader.onload = theFile => {
var wf_json;
let wf_json;
try {
wf_json = JSON.parse(reader.result);
} catch (e) {
@@ -201,7 +201,7 @@ var WorkflowListView = Backbone.View.extend({
wf_json = null;
}
if (wf_json) {
self.collection.create(wf_json, {
this.collection.create(wf_json, {
at: 0,
wait: true,
success: function() {
@@ -218,8 +218,8 @@ var WorkflowListView = Backbone.View.extend({
_showArgErrors: _.once(() => {
// Parse args out of params, display if there's a message.
var msg_text = QueryStringParsing.get("message");
var msg_status = QueryStringParsing.get("status");
const msg_text = QueryStringParsing.get("message");
const msg_status = QueryStringParsing.get("status");
if (msg_status === "error") {
mod_toastr.error(_.escape(msg_text || "Unknown Error, please report this to an administrator."));
} else if (msg_text) {
@@ -229,18 +229,17 @@ var WorkflowListView = Backbone.View.extend({
render: function() {
// Add workflow header
var header = this._templateHeader();
const header = this._templateHeader();
// Add the actions buttons
var templateActions = this._templateActionButtons();
var tableTemplate = this._templateWorkflowTable();
const templateActions = this._templateActionButtons();
const tableTemplate = this._templateWorkflowTable();
this.$el.html(header + templateActions + tableTemplate);
var self = this;
_(this.collection.models).each(item => {
// in case collection is not empty
self.appendItem(item);
self.confirmDelete(item);
this.appendItem(item);
this.confirmDelete(item);
}, this);
var minQueryLength = 3;
const minQueryLength = 3;
this.searchWorkflow(this.$(".search-wf"), this.$(".workflow-search tr"), minQueryLength);
this.adjustActiondropdown();
this._showArgErrors();
@@ -248,7 +247,7 @@ var WorkflowListView = Backbone.View.extend({
},
appendItem: function(item) {
var workflowItemView = new WorkflowItemView({
const workflowItemView = new WorkflowItemView({
model: item,
collection: this.collection
});
@@ -258,7 +257,7 @@ var WorkflowListView = Backbone.View.extend({
/** Add confirm box before removing/unsharing workflow */
confirmDelete: function(workflow) {
var $el_shared_wf_link = this.$(`.link-confirm-shared-${workflow.id}`);
const $el_shared_wf_link = this.$(`.link-confirm-shared-${workflow.id}`);
$el_shared_wf_link.click(() =>
window.confirm(`Are you sure you want to remove the shared workflow '${workflow.attributes.name}'?`)
);
@@ -267,12 +266,12 @@ var WorkflowListView = Backbone.View.extend({
/** Implement client side workflow search/filtering */
searchWorkflow: function($el_searchinput, $el_tabletr, min_querylen) {
$el_searchinput.on("keyup", function() {
var query = $(this).val();
const query = $(this).val();
// Filter when query is at least 3 characters
// otherwise show all rows
if (query.length >= min_querylen) {
// Ignore the query's case using 'i'
var regular_expression = new RegExp(query, "i");
const regular_expression = new RegExp(query, "i");
$el_tabletr.hide();
$el_tabletr
.filter(function() {
@@ -313,7 +312,7 @@ var WorkflowListView = Backbone.View.extend({
/** Template for workflow table */
_templateWorkflowTable: function() {
var tableHtml =
const tableHtml =
'<table class="table colored"><thead>' +
'<tr class="header">' +
"<th>Name</th>" +
@@ -323,9 +322,7 @@ var WorkflowListView = Backbone.View.extend({
"<th>Published</th>" +
"<th>Show in tools panel</th>" +
"</tr></thead>";
return `${
tableHtml
}<tbody class="workflow-search "><div class="hidden_description_layer"><p>Drop workflow files here to import</p></tbody></table></div>`;
return `${tableHtml}<tbody class="workflow-search "><div class="hidden_description_layer"><p>Drop workflow files here to import</p></tbody></table></div>`;
},
/** Main template */
@@ -343,7 +340,7 @@ var WorkflowListView = Backbone.View.extend({
}
});
var ImportWorkflowView = Backbone.View.extend({
const ImportWorkflowView = Backbone.View.extend({
initialize: function() {
this.setElement("<div/>");
this.render();
@@ -351,9 +348,8 @@ var ImportWorkflowView = Backbone.View.extend({
/** Open page to import workflow */
render: function() {
var self = this;
$.getJSON(`${Galaxy.root}workflow/upload_import_workflow`, options => {
self.$el.empty().append(self._mainTemplate(options));
this.$el.empty().append(this._mainTemplate(options));
});
},
+43 -42
View File
@@ -208,33 +208,33 @@ define({
"Edit dataset tags": "Editer les mots-clés du jeu de données",
"Edit dataset annotation": "Editer les annotations du jeu de données",
"Tool Help": false,
"Tool Help": "Aide outil",
// ---------------------------------------------------------------------------- admin
"Reset passwords": false,
"Search Tool Shed": false,
"Monitor installing repositories": false,
"Manage installed tools": false,
"Reset metadata": false,
"Download local tool": false,
"Tool lineage": false,
"Reload a tool's configuration": false,
"Review tool migration stages": false,
"View Tool Error Logs": false,
"Manage Display Whitelist": false,
"Manage Tool Dependencies": false,
"Reset passwords": "Réinitialiser les mots de passe",
"Search Tool Shed": "Chercher via un Tool Shed",
"Monitor installing repositories": "Contrôler les dépôts installés",
"Manage installed tools": "Gérer les outils installés",
"Reset metadata": "Réinitialiser les métadonnées",
"Download local tool": "Télécharger un outil en local",
"Tool lineage": "Affiliation des outils",
"Reload a tool's configuration": "Recharger une configuration d'outils",
"Review tool migration stages": "Vérifier les étapes de migration d'outils",
"View Tool Error Logs": "Voir les logs d'erreur d'outils",
"Manage Display Whitelist": "Gérer la liste blanche d'outils graphiques",
"Manage Tool Dependencies": "Gérer les dépendances d'outils",
Users: false,
Groups: false,
"API keys": false,
"Impersonate a user": false,
"API keys": "Clé d'API",
"Impersonate a user": "Prendre la place d'un utilisateur",
Data: false,
Quotas: false,
Roles: false,
"Local data": false,
"Form Definitions": false,
"Local data": "Données locales",
"Form Definitions": "Définitions de formulaires",
// ---------------------------------------------------------------------------- Scratchbook
"Enable/Disable Scratchbook": false,
"Show/Hide Scratchbook": false,
"Enable/Disable Scratchbook": "Activer/Désactiver la visu Scratchbook",
"Show/Hide Scratchbook": "Montrer/cacher la visu Scratchbook",
// ---------------------------------------------------------------------------- misc. MVC
Tags: "Mots-clés",
@@ -246,24 +246,25 @@ define({
Trackster: false,
Visualize: false,
// ---------------------------------------------------------------------------- dataset-error
"Any additional comments you can provide regarding what you were doing at the time of the bug.": false,
"Your email address": false,
"Any additional comments you can provide regarding what you were doing at the time of the bug.":
"N'importe quel commentaire que vous pouvez fournir concernant ce que vous faisiez lorsque le bug est apparu",
"Your email address": "Votre adresse e-mail",
Report: false,
"Error Report": false,
"Error Report": "Rapport d'erreur",
// ---------------------------------------------------------------------------- dataset-li
"Dataset details": false,
"Dataset details": "Détails du jeu de données",
// ---------------------------------------------------------------------------- dataset-edit-attributes
"Save permissions.": false,
"Change the datatype to a new type.": false,
"Convert the datatype to a new format.": false,
"Save attributes of the dataset.": false,
"Change data type": false,
"Edit dataset attributes": false,
"Save permissions": false,
"Manage dataset permissions": false,
"Change datatype": false,
"Convert datatype": false,
"Convert to new format": false,
"Save permissions.": "Sauvegarder les permissions",
"Change the datatype to a new type.": "Changer en un nouveau format de données",
"Convert the datatype to a new format.": "Convertir le format de données",
"Save attributes of the dataset.": "Sauvegarder les attributs du jeu de données",
"Change data type": "Changer le format de données",
"Edit dataset attributes": "Modifier les attributs du jeu de données",
"Save permissions": "Sauvegarder les permissions",
"Manage dataset permissions": "Gérer les permissions sur les jeux de données",
"Change datatype": "Changer le format de données",
"Convert datatype": "Convertir le format de données",
"Convert to new format": "Convertir en un nouveau format de données",
Save: false,
Permissions: false,
Datatypes: false,
@@ -272,16 +273,16 @@ define({
// ---------------------------------------------------------------------------- dataset-li-edit
Visualization: false,
// ---------------------------------------------------------------------------- library-dataset-view
"Import into History": false,
"Import into History": "Importer dans l'historique",
// ---------------------------------------------------------------------------- library-foldertoolbar-view
"Location Details": false,
"Deleting selected items": false,
"Please select folders or files": false,
"Please enter paths to import": false,
"Adding datasets from your history": false,
"Create New Folder": false,
"Location Details": "Détails de localisation",
"Deleting selected items": "Suppression des objets sélectionnés",
"Please select folders or files": "S'il vous plaît sélectionnez des dossiers ou fichiers",
"Please enter paths to import": "S'il vous plaît entrer des chemins/URLs à importer",
"Adding datasets from your history": "Ajouts de jeux de données à partir de votre historique",
"Create New Folder": "Créer un nouveau dossier",
// ---------------------------------------------------------------------------- library-librarytoolbar-view
"Create New Library": false,
"Create New Library": "Créer une nouvelle bibliothèque",
// ---------------------------------------------------------------------------- tours
Tours: false,
// ---------------------------------------------------------------------------- user-preferences
+172 -96
View File
@@ -1,169 +1,245 @@
/** zh localization */
/* by steven shen */
define({
// ----------------------------------------------------------------------------- masthead
"Analyze Data": "数据分析",
Workflow: "流程",
"Shared Data": "共享的数据",
"Data Libraries": "数据库",
Histories: "已发布的历史",
Workflows: "已发布的流程",
Visualizations: "已发布的可视化",
Pages: "已发布的页面",
Visualization: "可视化",
"New Track Browser": "创建 Track Browser",
"Saved Visualizations": "已保存的保存",
"Interactive Environments": "交互式环境",
Admin: "管理员",
Help: "帮助",
Support: "支持",
Search: "搜索",
"Mailing Lists": "邮件列表",
Videos: "视频",
Wiki: false,
"How to Cite Galaxy": "引用",
"Interactive Tours": "使用引导",
User: "用户",
Login: "登陆",
Register: "注册",
"Login or Register": "登陆或注册",
"Logged in as": "您已登陆为",
Preferences: "用户偏好性",
"Custom Builds": "自定义构建集",
Logout: "退出",
"Saved Histories": "保存历史",
"Saved Datasets": "保存数据集",
"Saved Pages": "保存页面",
Tools: "工具",
//Tooltip
"Account and saved data": "账号及数据保存",
"Account registration or login": "注册与登录",
"Support, contact, and community": "支持,联系,社区",
"Administer this Galaxy": "管理您的Galaxy",
"Visualize datasets": "数据集可视化",
"Access published resources": "访问发布的资源",
"Chain tools into workflows": "将工具链接到工作流程中",
"Analysis home view": "数据分析主页",
// ---------------------------------------------------------------------------- histories
History: "历史",
// ---- history/options-menu
"History Lists": "历史列表",
// Saved histories is defined above.
"Saved Histories": "保存历史",
"Histories Shared with Me": "分享给您历史",
"Current History": "当前历史",
"Create New": "创建新的历史",
"Copy History": "复制历史",
"Share or Publish": "分享或发布历史",
"Show Structure": "展示结构",
"Extract Workflow": "提取为工作流",
// Delete is defined elsewhere, but is also in this menu.
Delete: "删除",
"Delete Permanently": "永久删除",
"Dataset Actions": "数据操作",
"Copy Datasets": "复制数据集",
"Dataset Security": "数据安全",
"Resume Paused Jobs": "恢复已暂停的任务",
"Collapse Expanded Datasets": false,
"Unhide Hidden Datasets": "取消隐藏的数据集",
"Delete Hidden Datasets": "删除隐藏的数据集",
"Purge Deleted Datasets": "清空已删除的数据集",
Downloads: "下载",
"Export Tool Citations": "导出工具文献引用",
"Export History to File": "导出历史到文件",
"Other Actions": "其他操作",
"Import from File": "从文件导入",
Webhooks: false,
// ---- history-model
// ---- history-view
"This history is empty": "历史已空",
"No matching datasets found": "未找到匹配的数据集",
//"An error occurred while getting updates from the server" :
//false,
//"Please contact a Galaxy administrator if the problem persists" :
//false,
"An error occurred while getting updates from the server": "从服务器更新出现错误",
"Please contact a Galaxy administrator if the problem persists": "如果问题仍然存在,请联系Galaxy管理员",
//TODO:
//"An error was encountered while <% where %>" :
//false,
"Search datasets": "搜索数据集",
"You are currently viewing a deleted history!": "正在查看已删除的历史",
"You are over your disk quota": "您已超过磁盘配额",
//"Tool execution is on hold until your disk usage drops below your allocated quota" :
//false,
All: "皆",
"An error was encountered while <% where %>": "出现错误,当执行以下操作时: <% where %>",
"search datasets": "搜索数据集",
"You are currently viewing a deleted history!": "您正在查看已删除的历史记录",
"You are over your disk quota": "您超出了您的磁盘配额",
"Tool execution is on hold until your disk usage drops below your allocated quota":
"工具执行处于暂停状态,直到您的磁盘使用量低于您分配的配额",
All: "所有的",
None: "一个也没有",
"For all selected": "为每个选定",
// ---- history-view-edit
//"Edit history tags" :
//false,
//"Edit history Annotation" :
//false,
"Edit history tags": "编辑历史标签",
"Edit history Annotation": "编辑历史备注",
"Click to rename history": "单击要重命名的历史",
// multi operations
"Operations on multiple datasets": "编辑多个数据集",
//"Hide datasets" :
//false,
//"Unhide datasets" :
//false,
//"Delete datasets" :
//false,
//"Undelete datasets" :
//false,
"Hide datasets": "隐藏数据集",
"Unhide datasets": "显示数据集",
"Delete datasets": "删除数据集",
"Undelete datasets": "取消删除数据集",
"Permanently delete datasets": "永久删除数据集",
"This will permanently remove the data in your datasets. Are you sure?": "这将永久在你的数据集删除数据。你确定?",
"This will permanently remove the data in your datasets. Are you sure?": "这将永久在您的数据集删除数据。你确定?",
// ---- history-view-annotated
Dataset: "数据集",
//"Annotation" :
//false,
Dataset: "数据",
Annotation: "备注",
// ---- history-view-edit-current
"This history is empty. Click 'Get Data' on the left tool menu to start": "历史已空,请单击左边窗格中‘获取数据’",
"You must be logged in to create histories": "你必须登录后才能创建历史",
//TODO:
//"You can <% loadYourOwn %> or <% externalSource %>" :
"You can ": "您可以 ",
" or ": " 或者 ",
"load your own data": "上传您的个人数据",
"get data from an external source": "从外部来源获取数据",
// these aren't in zh/ginga.po and the template doesn't localize
//"Include Deleted Datasets" :
//false,
//"load your own data" :
//false,
//"get data from an external source" :
//"Include Hidden Datasets" :
//false,
// ---------------------------------------------------------------------------- datasets
// ---- hda-model
//"Unable to purge dataset" :
//false,
"Unable to purge dataset": "无法清除数据集",
// ---- hda-base
// display button
//"Cannot display datasets removed from disk" :
//false,
//"This dataset must finish uploading before it can be viewed" :
//false,
//"This dataset is not yet viewable" :
//false,
"View data": "数据",
"Cannot display datasets removed from disk": "无法显示已从磁盘中删除的数据集",
"This dataset must finish uploading before it can be viewed": "此数据集必须先完成上传, 然后才能查看",
"This dataset is not yet viewable": "此数据集是不可见的",
"View data": "查看数据",
// download button
Download: "下载",
"Download dataset": "下载数据集",
//"Additional files" :
//false,
"Additional files": "其他文件",
// info/show_params
"View details": "查看详情",
// dataset states
// state: new
//"This is a new dataset and not all of its data are available yet" :
//false,
"This is a new dataset and not all of its data are available yet": "这是一个新的数据集,并不是所有的数据都可用",
// state: noPermission
//"You do not have permission to view this dataset" :
//false,
"You do not have permission to view this dataset": "您无权查看此数据集",
// state: discarded
//"The job creating this dataset was cancelled before completion" :
//false,
"The job creating this dataset was cancelled before completion": "创建此数据集的任务在完成之前已被取消",
// state: queued
"This job is waiting to run": "等待运行的进程",
"This job is waiting to run": "任务正在等待运行",
// state: upload
//"This dataset is currently uploading" :
//false,
"This dataset is currently uploading": "此数据集正在上传中",
// state: setting_metadata
//"Metadata is being auto-detected" :
//false,
"Metadata is being auto-detected": "元数据正在被自动检测中",
// state: running
"This job is currently running": "正在运行的进程",
"This job is currently running": "任务正在运行中",
// state: paused
//"This job is paused. Use the \"Resume Paused Jobs\" in the history menu to resume" :
//false,
'This job is paused. Use the "Resume Paused Jobs" in the history menu to resume':
'此任务已暂停。使用历史菜单中的 "恢复已暂停的工作" 来恢复',
// state: error
"An error occurred with this dataset": "进程运行时出错",
"An error occurred with this dataset": "此数据集发生错误",
// state: empty
"No data": "没有数据",
// state: failed_metadata
//"An error occurred setting the metadata for this dataset" :
//false,
"An error occurred setting the metadata for this dataset": "设置此数据集的元数据时发生错误",
// ajax error prefix
//"There was an error getting the data for this dataset" :
//false,
"There was an error getting the data for this dataset": "获取此数据集的数据时出错",
// purged'd/del'd msg
//"This dataset has been deleted and removed from disk" :
//false,
"This dataset has been deleted and removed from disk": "此数据集已被删除并从磁盘中删除",
"This dataset has been deleted": "此数据集已被删除",
"This dataset has been hidden": "此数据集已隐藏",
"This dataset has been hidden": "此数据集已被隐藏",
format: "格式",
database: "数据库",
// ---- hda-edit
"Edit attributes": "编辑属性",
//"Cannot edit attributes of datasets removed from disk" :
//false,
//"Undelete dataset to edit attributes" :
//false,
//"This dataset must finish uploading before it can be edited" :
//false,
//"This dataset is not yet editable" :
//false,
"Cannot edit attributes of datasets removed from disk": "无法编辑已从磁盘中删除的数据集的属性",
"Undelete dataset to edit attributes": "取消删除数据集以编辑属性",
"This dataset must finish uploading before it can be edited": "该数据集必须先上传完成, 然后才能编辑",
"This dataset is not yet editable": "该数据集不可编辑",
Delete: "删除",
//"Dataset is already deleted" :
//false,
"Dataset is already deleted": "数据集已被删除",
"View or report this error": "报告错误",
"View or report this error": "查看或报告此错误",
"Run this job again": "重新运行",
"Run this job again": "重新运行此任务",
Visualize: "图形",
//"Visualize in" :
//false,
Visualize: "可视化",
"Visualize in": "的可视化",
"Undelete it": "反删除",
"Undelete it": "取消删除",
"Permanently remove it from disk": "从磁盘中永久删除",
"Unhide it": "取消隐藏"
"Unhide it": "取消隐藏",
//"You may be able to" :
//false,
//"set it manually or retry auto-detection" :
//false,
"You may be able to": "您可能可以",
"set it manually or retry auto-detection": "手动设置或重试自动检测",
//"Edit dataset tags" :
//false,
//"Edit dataset annotation" :
//false,
"Edit dataset tags": "编辑数据集标签",
"Edit dataset annotation": "编辑数据集备注",
"Tool Help": "工具帮助",
// ---------------------------------------------------------------------------- admin
"Search Tool Shed": "搜索 Tool Shed",
"Monitor installing repositories": "",
"Manage installed tools": "管理已安装的工具",
"Reset metadata": "重置元数据",
"Download local tool": "下载本地工具",
"Tool lineage": false,
"Reload a tool's configuration": "重新加载工具的配置",
"Review tool migration stages": "查看工具迁移状态",
"View Tool Error Logs": "查看工具错误日志",
"Manage Display Whitelist": "管理显示白名单",
"Manage Tool Dependencies": "管理工具依赖",
Users: "用户",
Groups: "组别",
"API keys": false,
"Impersonate a user": "模拟用户",
Data: "数据",
Quotas: "配额",
Roles: "角色",
"Local data": "本地数据",
"Form Definitions": "定义表单",
// ---------------------------------------------------------------------------- Scratchbook
"Enable/Disable Scratchbook": "启用/禁用 Scratchbook",
"Show/Hide Scratchbook": "显示/隐藏 Scratchbook",
// ---------------------------------------------------------------------------- misc. MVC
//"Tags" :
//false,
//"Annotation" :
//false,
//"Edit annotation" :
//false,
Tags: "标签",
"Edit annotation": "编辑备注",
// ----------------------------------------------------------------------------
ja: true,
fr: true,
zh: true
});
@@ -0,0 +1,138 @@
import * as mod_toastr from "libs/toastr";
import mod_group_model from "toolshed/groups/group-model";
// toolshed group detail view
const GroupDetailView = Backbone.View.extend({
el: "#groups_element",
options: {},
app: null,
initialize: function(options) {
this.options = _.extend(this.options, options);
this.app = window.globalTS.groups;
if (this.app.collection !== null) {
this.model = this.app.collection.get(this.options.group_id);
this.render();
} else {
this.fetchGroup();
}
},
fetchGroup: function(options) {
const that = this;
this.options = _.extend(this.options, options);
this.model = new mod_group_model.Group({ id: this.options.group_id });
this.model.fetch({
success: function(model) {
console.log("received data: ");
console.log(model);
that.render();
},
error: function(model, response) {
if (typeof response.responseJSON !== "undefined") {
mod_toastr.error(response.responseJSON.err_msg);
} else {
mod_toastr.error("An error occurred.");
}
}
});
},
render: function() {
const template = this.templateRow();
this.$el.html(template({ group: this.model }));
$("#center [data-toggle]").tooltip();
$("#center").css("overflow", "auto");
},
templateRow: function() {
return _.template(
[
"<div>",
'<h3><%= _.escape(group.get("name")) %></h3>',
'<p class="" style="color:gray;">',
'A group of <%= group.get("members").length %> members with <%= group.get("repositories").length %> repositories and a total of <%= group.get("total_downloads") %> combined repository clones.</p>',
"<h3>Members</h3>",
'<table class="grid table table-condensed">',
"<thead>",
"<th>Name</th>",
"<th>Repositories</th>",
"<th>Registered</th>",
"</thead>",
"<tbody>",
'<% _.each(group.get("members"), function(member) { %>',
"<tr>",
"<td>",
"<%= _.escape(member.username) %>",
"</td>",
"<td>",
'<a data-toggle="tooltip" data-placement="top" title="Repositories of <%= _.escape(member.username) %>" href="/repository/browse_repositories_by_user?user_id=<%= member.id %>&use_panels=true" id="<%= member.id %>"><%= member.user_repos_count %></a>',
"</td>",
"<td>",
"<%= member.time_created %>",
"</td>",
"</tr>",
"<% }); %>",
"</tbody>",
"</table>",
"<h3>Repositories</h3>",
'<table class="grid table table-condensed">',
"<thead>",
"<th>Name</th>",
"<th>Description</th>",
"<th>Clones</th>",
"<th>Owner</th>",
"<th>Categories</th>",
"<th>Created</th>",
"<th>Updated</th>",
"<th>Rating</th>",
"<th>Verified</th>",
"</thead>",
"<tbody>",
'<% _.each(group.get("repositories"), function(repo) { %>',
"<tr>",
"<td>",
'<a data-toggle="tooltip" data-placement="top" title="Details of <%= _.escape(repo.name) %>" href="/view/<%= _.escape(repo.owner) %>/<%= _escape(repo.name) %>" id="<%= repo.id %>"><%= _.escape(repo.name) %></a>',
"</td>",
"<td>",
"<%= _.escape(repo.description) %>",
"</td>",
"<td>",
"<%= repo.times_downloaded %>",
"</td>",
"<td>",
"<%= _.escape(repo.owner) %>",
"</td>",
"<td>",
"<% _.each((repo.categories), function(cat) { %>",
'<a data-toggle="tooltip" data-placement="top" title="Repositories in <%= cat.name %>" href="/repository/browse_repositories_in_category?id=<%= cat.id %>&use_panels=true"><%= cat.name %></a><br/>',
// '<%= repo.categories %>',
"<% }); %>",
"</td>",
'<td data-toggle="tooltip" data-placement="top" title="<%= repo.time_created_full %>">',
"<%= repo.time_created %>",
"</td>",
'<td data-toggle="tooltip" data-placement="top" title="<%= repo.time_updated_full %>">',
"<%= repo.time_updated %>",
"</td>",
"<td>",
"<%= repo.ratings_mean %>",
"</td>",
"<td>",
"<%= repo.approved %>",
"</td>",
"</tr>",
"<% }); %>",
"</tbody>",
"</table>",
"</div>"
].join("")
);
}
});
export default {
GroupDetailView: GroupDetailView
};
@@ -0,0 +1,128 @@
import * as mod_toastr from "libs/toastr";
import mod_group_model from "toolshed/groups/group-model";
import mod_group_row from "toolshed/groups/group-listrow-view";
const GroupListView = Backbone.View.extend({
el: "#groups_element",
defaults: {},
/**
* Initialize and fetch the groups from server.
* Async render afterwards.
* @param {object} options an object with options
*/
initialize: function(options) {
this.options = _.defaults(this.options || {}, this.defaults, options);
const that = this;
window.globalTS.groups.collection = new mod_group_model.Groups();
window.globalTS.groups.collection.fetch({
success: function(model) {
console.log("received data: ");
console.log(model);
that.render();
},
error: function(model, response) {
if (typeof response.responseJSON !== "undefined") {
mod_toastr.error(response.responseJSON.err_msg);
} else {
mod_toastr.error("An error occurred.");
}
}
});
},
fetch: function() {},
/**
* Render the groups table from the object's own collection.
*/
render: function(options) {
this.options = _.extend(this.options, options);
$(".tooltip").hide();
const template = this.templateGroupsList();
this.$el.html(template({ length: window.globalTS.groups.collection.models.length }));
this.renderRows(window.globalTS.groups.collection.models);
$("#center [data-toggle]").tooltip();
$("#center").css("overflow", "auto");
},
/**
* Render all given models as rows in the groups list
* @param {array} groups_to_render array of group models to render
*/
renderRows: function(groups_to_render) {
for (let i = 0; i < groups_to_render.length; i++) {
const group = groups_to_render[i];
this.renderOne({ group: group });
}
},
/**
* Create a view for the given model and add it to the groups view.
* @param {Group} model of the view that will be rendered
*/
renderOne: function(options) {
const rowView = new mod_group_row.GroupListRowView(options);
this.$el.find("#group_list_body").append(rowView.el);
},
/**
* Table heading was clicked, update sorting preferences and re-render.
* @return {[type]} [description]
*/
// sort_clicked : function(){
// if (Galaxy.libraries.preferences.get('sort_order') === 'asc'){
// Galaxy.libraries.preferences.set({'sort_order': 'desc'});
// } else {
// Galaxy.libraries.preferences.set({'sort_order': 'asc'});
// }
// this.render();
// },
/**
* Sort the underlying collection according to the parameters received.
* Currently supports only sorting by name.
*/
// sortLibraries: function(){
// if (Galaxy.libraries.preferences.get('sort_by') === 'name'){
// if (Galaxy.libraries.preferences.get('sort_order') === 'asc'){
// this.collection.sortByNameAsc();
// } else if (Galaxy.libraries.preferences.get('sort_order') === 'desc'){
// this.collection.sortByNameDesc();
// }
// }
// },
// MMMMMMMMMMMMMMMMMM
// === TEMPLATES ====
// MMMMMMMMMMMMMMMMMM
templateGroupsList: function() {
const tmpl_array = [];
tmpl_array.push('<div id="groups">');
tmpl_array.push("</div>");
tmpl_array.push('<div class="groups_container table-responsive">');
tmpl_array.push("<% if(length === 0) { %>");
tmpl_array.push("<div>There are no groups yet.</div>");
tmpl_array.push("<% } else{ %>");
tmpl_array.push('<table class="grid table table-condensed">');
tmpl_array.push(" <thead>");
tmpl_array.push(" <th>Name</th>");
// tmpl_array.push(' <th style="width:22%;">description</th>');
tmpl_array.push(" <th>Members</th> ");
tmpl_array.push(" <th>Repositories</th>");
tmpl_array.push(" </thead>");
tmpl_array.push(' <tbody id="group_list_body">');
// group item views will attach here
tmpl_array.push(" </tbody>");
tmpl_array.push("</table>");
tmpl_array.push("<% }%>");
tmpl_array.push("</div>");
return _.template(tmpl_array.join(""));
}
});
export default {
GroupListView: GroupListView
};
@@ -1,36 +1,32 @@
define([],
function() {
// toolshed group row view
var GroupListRowView = Backbone.View.extend({
const GroupListRowView = Backbone.View.extend({
events: {},
initialize : function( options ){
this.render( options.group );
initialize: function(options) {
this.render(options.group);
},
render: function( group ){
var tmpl = this.templateRow();
this.setElement(tmpl( { group:group } ));
render: function(group) {
const tmpl = this.templateRow();
this.setElement(tmpl({ group: group }));
this.$el.show();
return this;
},
templateRow: function() {
return _.template([
return _.template(
[
'<tr class="" data-id="<%- group.get("id") %>">',
'<td><a href="groups#/<%= group.get("id") %>"><%= _.escape(group.get("name")) %></a></td>',
// '<td>description</td>',
'<td><%= group.get("total_members") %></td>',
'<td><%= group.get("total_repos") %></td>',
'</tr>'
].join(''));
}
"</tr>"
].join("")
);
}
});
return {
export default {
GroupListRowView: GroupListRowView
};
});
@@ -0,0 +1,17 @@
// ============================================================================
// TS GROUP RELATED MODELS
const Group = Backbone.Model.extend({
urlRoot: `${Galaxy.root}api/groups`
});
const Groups = Backbone.Collection.extend({
url: `${Galaxy.root}api/groups`,
model: Group
});
export default {
Group: Group,
Groups: Groups
};
@@ -0,0 +1,42 @@
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// === MAIN TOOLSHED GROUP MODULE ====
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
import mod_group_list from "toolshed/groups/group-list-view";
import mod_group_detail from "toolshed/groups/group-detail-view";
// ============================================================================
// ROUTER
const ToolshedRouter = Backbone.Router.extend({
routes: {
"": "groups",
":group_id": "group_page"
}
});
const ToolshedGroups = Backbone.View.extend({
groupListView: null,
groupDetailView: null,
collection: null,
initialize: function() {
window.globalTS.groups = this;
this.ts_router = new ToolshedRouter();
this.ts_router.on("route:groups", () => {
window.globalTS.groups.groupListView = new mod_group_list.GroupListView();
});
this.ts_router.on("route:group_page", group_id => {
window.globalTS.groups.groupDetailView = new mod_group_detail.GroupDetailView({
group_id: group_id
});
});
Backbone.history.start({ pushState: false });
}
});
export default {
ToolshedGroups: ToolshedGroups
};
+7 -9
View File
@@ -1,6 +1,7 @@
import _l from "utils/localization";
import * as _ from "libs/underscore";
import * as d3 from "libs/d3";
import * as d3 from "d3";
import { event as currentEvent } from "d3";
import visualization from "viz/visualization";
import mod_utils from "utils/utils";
import config from "utils/config";
@@ -193,7 +194,6 @@ var CircsterView = Backbone.View.extend({
tracks_start_radii = d3.range(radius_start, min_dimension / 2, dataset_arc_height + track_gap);
// Map from track start to bounds.
var self = this;
return _.map(tracks_start_radii, radius => [radius, radius + dataset_arc_height]);
},
@@ -221,8 +221,8 @@ var CircsterView = Backbone.View.extend({
.call(
d3.behavior.zoom().on("zoom", () => {
// Do zoom, drag.
var scale = d3.event.scale;
svg.attr("transform", `translate(${d3.event.translate}) scale(${scale})`);
var scale = currentEvent.scale;
svg.attr("transform", `translate(${currentEvent.translate}) scale(${scale})`);
// Propagate scale changes to views.
if (self.scale !== scale) {
@@ -628,7 +628,6 @@ var CircsterTrackView = Backbone.View.extend({
* default data bounds.
*/
_update_data_bounds: function(new_bounds) {
var old_bounds = this.data_bounds;
this.data_bounds =
new_bounds || this.get_data_bounds(this.track.get("data_manager").get_genome_wide_data(this.genome));
this._transition_chrom_data();
@@ -962,7 +961,6 @@ var CircsterQuantitativeTrackView = CircsterTrackView.extend({
_data_bounds_ticks_fn: function() {
// Closure vars.
var self = this;
visibleChroms = 0;
// Return function for locating ticks based on chrom arc data.
return (
@@ -1101,9 +1099,9 @@ var Circster = Backbone.View.extend({
// load css
mod_utils.cssLoadFile("static/style/circster.css");
// -- Configure visualization --
var genome = new visualization.Genome(galaxy_config.app.genome);
var genome = new visualization.Genome(window.galaxy_config.app.genome);
var vis = new visualization.GenomeVisualization(galaxy_config.app.viz_config);
var vis = new visualization.GenomeVisualization(window.galaxy_config.app.viz_config);
// Add Circster-specific config options.
vis.get("config").add([
@@ -1143,7 +1141,7 @@ var Circster = Backbone.View.extend({
// setup title
$("#center .unified-panel-header-inner").append(
`${galaxy_config.app.viz_config.title} ${galaxy_config.app.viz_config.dbkey}`
`${window.galaxy_config.app.viz_config.title} ${window.galaxy_config.app.viz_config.dbkey}`
);
// setup menu
+20 -18
View File
@@ -2,13 +2,7 @@ import _l from "utils/localization";
/**
* Top-level trackster code, used for creating/loading visualizations and user interface elements.
*/
// global variables
var ui = null;
var view = null;
var browser_router = null;
// trackster viewer
//import * as $ from 'jquery';
import * as _ from "libs/underscore";
import tracks from "viz/trackster/tracks";
import visualization from "viz/visualization";
@@ -25,6 +19,12 @@ import "libs/farbtastic";
import "libs/jquery/jquery.form";
import "libs/jquery/jquery.rating";
import "ui/editable-text";
// trackster global variables
var ui = null;
var view = null;
var browser_router = null;
/**
* Base Object/Model for inhertiance.
*/
@@ -152,7 +152,7 @@ var TracksterUI = Base.extend({
title: _l("Bookmarks"),
on_click: function() {
// HACK -- use style to determine if panel is hidden and hide/show accordingly.
force_right_panel($("div#right").css("right") == "0px" ? "hide" : "show");
window.force_right_panel($("div#right").css("right") == "0px" ? "hide" : "show");
}
},
{
@@ -250,7 +250,7 @@ var TracksterUI = Base.extend({
// Create view.
var self = this;
var view = new tracks.TracksterView(_.extend(view_config, { header: false }));
view = new tracks.TracksterView(_.extend(view_config, { header: false }));
view.editor = true;
$.when(view.load_chroms_deferred).then(chrom_info => {
@@ -408,10 +408,10 @@ var TracksterView = Backbone.View.extend({
});
// hide right panel
force_right_panel("hide");
window.force_right_panel("hide");
// check if id is available
if (galaxy_config.app.id) {
if (window.galaxy_config.app.id) {
this.view_existing();
} else if (query_string.get("dataset_id")) {
this.choose_existing_or_new();
@@ -459,7 +459,6 @@ var TracksterView = Backbone.View.extend({
view_in_saved: function(dataset_params) {
var tracks_grid = new GridView({
url_base: `${Galaxy.root}visualization/list_tracks`,
dict_format: true,
embedded: true
});
Galaxy.modal.show({
@@ -484,7 +483,7 @@ var TracksterView = Backbone.View.extend({
// view
view_existing: function() {
// get config
var viz_config = galaxy_config.app.viz_config;
var viz_config = window.galaxy_config.app.viz_config;
// view
view = ui.create_visualization(
@@ -534,8 +533,11 @@ var TracksterView = Backbone.View.extend({
// select default
var dbkeys_in_genomes = response.map(r => r[1]);
if (galaxy_config.app.default_dbkey && _.contains(dbkeys_in_genomes, galaxy_config.app.default_dbkey)) {
$("#new-dbkey").val(galaxy_config.app.default_dbkey);
if (
window.galaxy_config.app.default_dbkey &&
_.contains(dbkeys_in_genomes, window.galaxy_config.app.default_dbkey)
) {
$("#new-dbkey").val(window.galaxy_config.app.default_dbkey);
}
// change focus
@@ -589,7 +591,7 @@ var TracksterView = Backbone.View.extend({
name: name,
dbkey: dbkey
},
galaxy_config.app.gene_region
window.galaxy_config.app.gene_region
);
// initialize editor
@@ -605,9 +607,9 @@ var TracksterView = Backbone.View.extend({
$("#center .unified-panel-title").text(`${view.config.get_value("name")} (${view.dbkey})`);
// add dataset
if (galaxy_config.app.add_dataset)
if (window.galaxy_config.app.add_dataset)
$.ajax({
url: `${Galaxy.root}api/datasets/${galaxy_config.app.add_dataset}`,
url: `${Galaxy.root}api/datasets/${window.galaxy_config.app.add_dataset}`,
data: { hda_ldda: "hda", data_type: "track_config" },
dataType: "json",
success: function(track_data) {
@@ -46,14 +46,12 @@ var select_datasets = (filters, success_fn) => {
var history_grid = new GridView({
url_base: `${Galaxy.root}visualization/list_history_datasets`,
filters: filters,
dict_format: true,
embedded: true
});
// library dataset selection tab
var library_grid = new GridView({
url_base: `${Galaxy.root}visualization/list_library_datasets`,
dict_format: true,
embedded: true
});
+4 -2
View File
@@ -221,7 +221,9 @@
}
}
}
span.nametags {
div.nametags {
max-height:26px;
overflow-y: auto;
span.label {
display: inline-block;
margin-right: 2px;
@@ -698,4 +700,4 @@
margin-right: 8px;
}
}
}
}
+19 -58
View File
@@ -104,7 +104,7 @@ th.button_heading{
margin-right: 0.2em;
}
.toolbar-item{
margin-left: 1em;
margin-left: 0.5em;
}
}
#libraries_element, #folder_items_element{
@@ -112,7 +112,7 @@ th.button_heading{
margin-left: 0.5em;
}
}
.help-button{
.right-float{
float: right;
}
.pagination-sm{
@@ -128,11 +128,6 @@ th.button_heading{
.libimport-select-all{
margin-left: 0.5em;
}
.library-modal-item{
width:90%;
margin-left: 1em;
margin-right: 1em;
}
.paginator-bottom{
width: 27em;
margin-left: auto;
@@ -143,60 +138,26 @@ th.button_heading{
}
// Extra style for Galaxy modal
.modal-content #selected_history_content{
ul{
list-style-type: none;
.modal-content{
.library_selected_history_content{
ul{
list-style-type: none;
}
}
.library-collection-type-select, .library-collection-history-select{
margin-left:auto;
margin-right:auto;
width:200px;
min-width:200px;
}
.library-modal-item{
width:90%;
margin-left: auto;
margin-right: auto;
margin-top: 1.5em;
}
}
// Follows the style for the deprecated admin libraries interface
#library-grid{
ul{
list-style: none;
}
span.expandLink{
padding-left: 12px;
display: inline-block;
vertical-align: middle;
background: url(../images/silk/resultset_next.png) no-repeat;
}
.folderRow.expanded span.expandLink{
background: url(../images/silk/resultset_bottom.png) no-repeat;
}
.folderRow span.rowIcon{
float: left;
margin-right: 5px;
width: 16px;
height: 16px;
display: inline-block;
vertical-align: middle;
background: url(../images/silk/folder.png);
}
.libraryItem-error{
margin-right: 2px;
padding: 0 2px 0 2px;
border: 1px solid @state-danger-border;
background: @state-danger-bg;
}
.libraryItem-queued{
margin-right: 2px;
padding: 0 2px 0 2px;
border: 1px solid @state-default-border;
background: @state-default-bg;
}
.libraryItem-running{
margin-right: 2px;
padding: 0 2px 0 2px;
border: 1px solid @state-running-border;
background: @state-running-bg;
}
.libraryItem-upload{
margin-right: 2px;
padding: 0 2px 0 2px;
border: 1px solid @state-upload-border;
background: @state-upload-bg;
}
}
.libraryTitle{
th{
text-align: left;
-80
View File
@@ -1,80 +0,0 @@
/**
* Grunt task to fetch third-party libraries using bower and copy them to the proper location in client/.
* @param {Object} grunt main grunt file
* @return {Function} callback to build this task
*/
module.exports = function( grunt ){
"use strict";
var dev_path = './galaxy/scripts',
// where to move fetched bower components into the build structure (libName: [ bower-location, libs-location ])
libraryLocations = {
'farbtastic': [ 'src/farbtastic.js', 'farbtastic.js' ],
'bootstrap-tour': [ 'build/js/bootstrap-tour.js', 'bootstrap-tour.js' ],
// these need to be updated and tested
//'jquery-form': [ 'jquery.form.js', 'jquery/jquery.form.js' ],
//'jquery-autocomplete': [ 'src/jquery.autocomplete.js', 'jquery/jquery.autocomplete.js' ],
//'select2': [ 'select2.js', 'jquery/select2.js' ],
//'jStorage': [ 'jstorage.js', 'jquery/jstorage.js' ],
//'dynatree': [ 'dist/jquery.dynatree.js', 'jquery/jquery.dynatree.js' ],
//'jquery.event.drag-drop': [
// [ 'event.drag/jquery.event.drag.js', 'jquery/jquery.event.drag.js' ],
// [ 'event.drag/jquery.event.drop.js', 'jquery/jquery.event.drop.js' ]
//],
// these are complicated by additional css/less
//'toastr': [ 'toastr.js', 'toastr.js' ],
//'wymeditor': [ 'dist/wymeditor/jquery.wymeditor.js', 'jquery/jquery.wymeditor.js' ],
//'jstree': [ 'jstree.js', 'jquery/jstree.js' ],
// these have been customized by Galaxy
//'bootstrap': [ 'dist/js/bootstrap.js', 'bootstrap.js' ],
//'jquery-ui': [
// // multiple components now
// [ '', 'jquery/jquery-ui.js' ]
//],
};
// call bower to install libraries and other external resources
grunt.config( 'bower-install-simple', {
options: {
color: true
},
"prod": {
options: {
production: true
}
},
"dev": {
options: {
production: false
}
}
});
/** copy external libraries from bower components to scripts/libs */
function copyLibs(){
var lib_path = dev_path + '/libs/';
for( var libName in libraryLocations ){
if( libraryLocations.hasOwnProperty( libName ) ){
var bower_dir = 'bower_components',
location = libraryLocations[ libName ],
source = [ bower_dir, libName, location[0] ].join( '/' ),
destination = lib_path + location[1];
grunt.log.writeln( source + ' -> ' + destination );
grunt.file.copy( source, destination );
}
}
}
grunt.loadNpmTasks( 'grunt-bower-install-simple' );
grunt.registerTask( 'copy-libs', 'copy external libraries to src', copyLibs );
grunt.registerTask( 'install-libs', 'fetch external libraries and copy to src',
[ 'bower-install-simple:prod', 'copy-libs' ] );
};
-123
View File
@@ -1,123 +0,0 @@
/**
* Grunt task to compress/bundle static/scripts.
* @param {Object} grunt main grunt file
* @return {Function} callback to build this task
*/
module.exports = function( grunt ){
"use strict";
var app = grunt.config.get( 'app' ),
paths = grunt.config.get( 'paths' ),
// uglify settings used when scripts are decompressed/not-minified
decompressedSettings = {
mangle : false,
beautify : true,
compress : {
drop_debugger : false
}
},
// uglify settings used when scripts are compressed/minified
compressedSettings = {
mangle : {
screw_ie8 : true
},
compress : {
// high compression options
screw_ie8 : true,
sequences: true,
dead_code : true,
drop_debugger : true,
comparisons : true,
conditionals : true,
evaluate : true,
booleans : true,
loops : true,
unused : true,
hoist_funs : true,
if_return : true,
join_vars : true,
cascade : true,
}
};
grunt.config( 'uglify', {
target : {
files: [{
expand : true,
cwd : paths.srcSymlink,
// NOTE: do not use uglify in the apps dir (webpack will do that section)
src : [ '**/*.js', '!apps/**/*.js' ],
// src : '**/*.js',
dest : paths.dist
}],
}
});
if (grunt.option( 'develop' )){
grunt.config( 'uglify.options', decompressedSettings );
} else {
grunt.config( 'uglify.target.options', {
sourceMap : true,
sourceMapName : function( path ){
// rewrite name to have all source maps in 'static/maps'
return path.replace( paths.dist, paths.maps ) + '.map';
}
});
grunt.config( 'uglify.options', compressedSettings );
}
// -------------------------------------------------------------------------- decompress for easier debugging
grunt.registerTask( 'decompress', function(){
grunt.log.writeln( "decompressing... (don't forget to call 'grunt' again before committing)" );
grunt.config( 'uglify.options', decompressedSettings );
grunt.config( 'uglify.target.options', {});
grunt.task.run( 'uglify' );
});
// alias for symmetry
grunt.registerTask( 'compress', [ 'uglify' ] );
// -------------------------------------------------------------------------- watch & copy,pack only those changed
// use 'grunt watch' (from a new tab in your terminal) to have grunt re-copy changed files automatically
grunt.config( 'watch', {
watch: {
// watch for changes in the src dir
// NOTE: but not in the apps dir (which is only used by webpack)
files: [ paths.srcSymlink + '/**', '!' + paths.srcSymlink + '/apps/**' ],
tasks: [ 'uglify' ],
options: {
spawn: false
}
}
});
// outer scope variable for the event handler and onChange fn - begin with empty hash
var changedFiles = Object.create(null);
// when files are changed, set the copy src and packScripts target to the filenames of the updated files
var onChange = grunt.util._.debounce(function() {
grunt.log.writeln( 'onChange, changedFiles:', Object.keys( changedFiles ) );
grunt.config( 'uglify.target.files', [{
expand : true,
cwd : paths.srcSymlink,
src : Object.keys( changedFiles ),
dest : paths.dist
}]);
changedFiles = Object.create(null);
}, 200);
var addChangedFile = function( action, filepath ) {
// store each filepath in a Files obj, the debounced fn above will use it as an aggregate list for copying
// we need to take galaxy/scripts out of the filepath or it will be copied to the wrong loc
filepath = filepath.replace( paths.srcSymlink + '/', '' );
// grunt.log.writeln( 'on.watch, filepath:', filepath );
changedFiles[ filepath ] = action;
onChange();
};
grunt.event.on( 'watch', addChangedFile );
// --------------------------------------------------------------------------
grunt.loadNpmTasks( 'grunt-contrib-watch' );
grunt.loadNpmTasks( 'grunt-contrib-uglify' );
};
+1 -2
View File
@@ -34,8 +34,7 @@ var paths = {
'jquery-mousewheel': [ 'jquery.mousewheel.js', 'jquery/jquery.mousewheel.js' ],
'raven-js': ['dist/raven.js', 'raven.js'],
'requirejs': [ 'require.js', 'require.js' ],
'underscore': [ 'underscore.js', 'underscore.js' ],
'vue': [ 'dist/vue.js', 'vue.js' ]
'underscore': [ 'underscore.js', 'underscore.js' ]
},
libs: ['galaxy/scripts/libs/**/*.js']
};
+12 -11
View File
@@ -28,14 +28,15 @@
"raven-js": "^3.17.0",
"requirejs": "2",
"underscore": "^1.8.3",
"vue": "^2.5.3"
"vue": "^2.5.9"
},
"scripts": {
"watch": "gulp stage-libs && concurrently \"yarn run webpack-watch\" \"yarn run gulp watch\" \"yarn run style-watch\"",
"build": "NODE_ENV=development gulp stage-libs && concurrently \"yarn run style\" \"yarn run webpack\" \"yarn run gulp clean && yarn run gulp\"",
"build-production": "NODE_ENV=production gulp stage-libs && concurrently \"yarn run style\" \"yarn run webpack-production\" \"yarn run gulp clean && yarn run gulp-production\"",
"build-production-maps": "NODE_ENV=production gulp stage-libs && concurrently \"yarn run style\" \"yarn run webpack-production-maps\" \"yarn run gulp clean && yarn run gulp-production-maps\"",
"build": "NODE_ENV=development gulp stage-libs && concurrently \"yarn run style\" \"yarn run webpack\" \"yarn run gulp clean && yarn run gulp\" && yarn run save-build-hash",
"build-production": "NODE_ENV=production gulp stage-libs && concurrently \"yarn run style\" \"yarn run webpack-production\" \"yarn run gulp clean && yarn run gulp-production\" && yarn run save-build-hash",
"build-production-maps": "NODE_ENV=production gulp stage-libs && concurrently \"yarn run style\" \"yarn run webpack-production-maps\" \"yarn run gulp clean && yarn run gulp-production-maps\" && yarn run save-build-hash",
"webpack": "webpack -d",
"save-build-hash": "git rev-parse HEAD > ../static/client_build_hash.txt",
"webpack-watch": "webpack -d --watch",
"webpack-production": "webpack -p",
"webpack-production-maps": "GXY_BUILD_SOURCEMAPS=1 webpack -p",
@@ -50,16 +51,16 @@
"jshint": "jshint --exclude='galaxy/scripts/libs/**' galaxy/scripts/**/*.js",
"build-charts": "webpack -p --config ../config/plugins/visualizations/charts/webpack.config.js",
"build-scatterplot": "NODE_PATH=./node_modules webpack -p --config ../config/plugins/visualizations/scatterplot/webpack.config.js",
"prettier": "prettier --write --tab-width 4 --print-width 120 \"galaxy/scripts/{,!(libs)/**/}/*.js\""
"prettier": "prettier --write --tab-width 4 --print-width 120 \"galaxy/scripts/{,!(libs)/**/}/{*.js,*.vue}\""
},
"devDependencies": {
"babel-core": "^6.24.0",
"babel-loader": "^7.1.1",
"babel-preset-env": "^1.3.2",
"concurrently": "^3.4.0",
"babel-preset-env": "^1.6.1",
"concurrently": "^3.5.1",
"css-loader": "^0.28.7",
"del": "^3.0.0",
"expose-loader": "^0.7.3",
"expose-loader": "^0.7.4",
"grunt": "^0.4.5",
"grunt-bower-install-simple": "^1.1.0",
"grunt-cli": "^0.1.13",
@@ -87,11 +88,11 @@
"karma-webpack": "^2.0.6",
"phantomjs-polyfill": "0.0.2",
"phantomjs-prebuilt": "^2.1.7",
"prettier": "^1.7.4",
"prettier": "^1.10.1",
"qunitjs": "^2.4.1",
"sinon": "^4.1.2",
"vue-loader": "^13.5.0",
"vue-template-compiler": "^2.5.3",
"webpack": "^3.6.0"
"vue-template-compiler": "^2.5.9",
"webpack": "^3.10.0"
}
}
@@ -1,146 +0,0 @@
define([
"libs/toastr",
"toolshed/scripts/mvc/groups/group-model"
],
function(
mod_toastr,
mod_group_model
) {
// toolshed group detail view
var GroupDetailView = Backbone.View.extend({
el: "#groups_element",
options: {},
app: null,
initialize : function( options ){
this.options = _.extend( this.options, options );
this.app = window.globalTS.groups;
if ( this.app.collection !== null ){
this.model = this.app.collection.get( this.options.group_id );
this.render();
} else {
this.fetchGroup();
}
},
fetchGroup: function( options ){
var that = this;
this.options = _.extend( this.options, options );
this.model = new mod_group_model.Group( { id: this.options.group_id } );
this.model.fetch({
success: function( model ){
console.log( 'received data: ' );
console.log( model );
that.render();
},
error: function( model, response ){
if ( typeof response.responseJSON !== "undefined" ){
mod_toastr.error( response.responseJSON.err_msg );
} else {
mod_toastr.error( 'An error occurred.' );
}
}
});
},
render: function(){
var template = this.templateRow();
this.$el.html( template( { group: this.model } ) );
$( "#center [data-toggle]" ).tooltip();
$( "#center" ).css( 'overflow','auto' );
},
templateRow: function() {
return _.template([
'<div>',
'<h3><%= _.escape(group.get("name")) %></h3>',
'<p class="" style="color:gray;">',
'A group of <%= group.get("members").length %> members with <%= group.get("repositories").length %> repositories and a total of <%= group.get("total_downloads") %> combined repository clones.</p>',
'<h3>Members</h3>',
'<table class="grid table table-condensed">',
'<thead>',
'<th>Name</th>',
'<th>Repositories</th>',
'<th>Registered</th>',
'</thead>',
'<tbody>',
'<% _.each(group.get("members"), function(member) { %>',
'<tr>',
'<td>',
'<%= _.escape(member.username) %>',
'</td>',
'<td>',
'<a data-toggle="tooltip" data-placement="top" title="Repositories of <%= _.escape(member.username) %>" href="/repository/browse_repositories_by_user?user_id=<%= member.id %>&use_panels=true" id="<%= member.id %>"><%= member.user_repos_count %></a>',
'</td>',
'<td>',
'<%= member.time_created %>',
'</td>',
'</tr>',
'<% }); %>',
'</tbody>',
'</table>',
'<h3>Repositories</h3>',
'<table class="grid table table-condensed">',
'<thead>',
'<th>Name</th>',
'<th>Description</th>',
'<th>Clones</th>',
'<th>Owner</th>',
'<th>Categories</th>',
'<th>Created</th>',
'<th>Updated</th>',
'<th>Rating</th>',
'<th>Verified</th>',
'</thead>',
'<tbody>',
'<% _.each(group.get("repositories"), function(repo) { %>',
'<tr>',
'<td>',
'<a data-toggle="tooltip" data-placement="top" title="Details of <%= _.escape(repo.name) %>" href="/view/<%= _.escape(repo.owner) %>/<%= _escape(repo.name) %>" id="<%= repo.id %>"><%= _.escape(repo.name) %></a>',
'</td>',
'<td>',
'<%= _.escape(repo.description) %>',
'</td>',
'<td>',
'<%= repo.times_downloaded %>',
'</td>',
'<td>',
'<%= _.escape(repo.owner) %>',
'</td>',
'<td>',
'<% _.each((repo.categories), function(cat) { %>',
'<a data-toggle="tooltip" data-placement="top" title="Repositories in <%= cat.name %>" href="/repository/browse_repositories_in_category?id=<%= cat.id %>&use_panels=true"><%= cat.name %></a><br/>',
// '<%= repo.categories %>',
'<% }); %>',
'</td>',
'<td data-toggle="tooltip" data-placement="top" title="<%= repo.time_created_full %>">',
'<%= repo.time_created %>',
'</td>',
'<td data-toggle="tooltip" data-placement="top" title="<%= repo.time_updated_full %>">',
'<%= repo.time_updated %>',
'</td>',
'<td>',
'<%= repo.ratings_mean %>',
'</td>',
'<td>',
'<%= repo.approved %>',
'</td>',
'</tr>',
'<% }); %>',
'</tbody>',
'</table>',
'</div>'
].join(''));
}
});
return {
GroupDetailView: GroupDetailView
};
});
@@ -1,141 +0,0 @@
define([
"libs/toastr",
"toolshed/scripts/mvc/groups/group-model",
"toolshed/scripts/mvc/groups/group-listrow-view"
], function(
mod_toastr,
mod_group_model,
mod_group_row
){
var GroupListView = Backbone.View.extend({
el: '#groups_element',
defaults: {},
/**
* Initialize and fetch the groups from server.
* Async render afterwards.
* @param {object} options an object with options
*/
initialize : function( options ){
this.options = _.defaults( this.options || {}, this.defaults, options );
var that = this;
window.globalTS.groups.collection = new mod_group_model.Groups();
window.globalTS.groups.collection.fetch({
success: function( model ){
console.log( 'received data: ' );
console.log( model );
that.render();
},
error: function( model, response ){
if ( typeof response.responseJSON !== "undefined" ){
mod_toastr.error( response.responseJSON.err_msg );
} else {
mod_toastr.error( 'An error occurred.' );
}
}
});
},
fetch: function(){
},
/**
* Render the groups table from the object's own collection.
*/
render: function ( options ) {
this.options = _.extend( this.options, options );
$( ".tooltip" ).hide();
var template = this.templateGroupsList();
this.$el.html( template( { length: window.globalTS.groups.collection.models.length } ) );
this.renderRows( window.globalTS.groups.collection.models );
$( "#center [data-toggle]" ).tooltip();
$( "#center" ).css( 'overflow','auto' );
},
/**
* Render all given models as rows in the groups list
* @param {array} groups_to_render array of group models to render
*/
renderRows: function( groups_to_render ){
for ( var i = 0; i < groups_to_render.length; i++ ) {
var group = groups_to_render[i];
this.renderOne( { group: group } )
}
},
/**
* Create a view for the given model and add it to the groups view.
* @param {Group} model of the view that will be rendered
*/
renderOne: function( options ){
var rowView = new mod_group_row.GroupListRowView( options );
this.$el.find( '#group_list_body' ).append( rowView.el );
},
/**
* Table heading was clicked, update sorting preferences and re-render.
* @return {[type]} [description]
*/
// sort_clicked : function(){
// if (Galaxy.libraries.preferences.get('sort_order') === 'asc'){
// Galaxy.libraries.preferences.set({'sort_order': 'desc'});
// } else {
// Galaxy.libraries.preferences.set({'sort_order': 'asc'});
// }
// this.render();
// },
/**
* Sort the underlying collection according to the parameters received.
* Currently supports only sorting by name.
*/
// sortLibraries: function(){
// if (Galaxy.libraries.preferences.get('sort_by') === 'name'){
// if (Galaxy.libraries.preferences.get('sort_order') === 'asc'){
// this.collection.sortByNameAsc();
// } else if (Galaxy.libraries.preferences.get('sort_order') === 'desc'){
// this.collection.sortByNameDesc();
// }
// }
// },
// MMMMMMMMMMMMMMMMMM
// === TEMPLATES ====
// MMMMMMMMMMMMMMMMMM
templateGroupsList: function(){
var tmpl_array = [];
tmpl_array.push('<div id="groups">');
tmpl_array.push('</div>');
tmpl_array.push('<div class="groups_container table-responsive">');
tmpl_array.push('<% if(length === 0) { %>');
tmpl_array.push('<div>There are no groups yet.</div>');
tmpl_array.push('<% } else{ %>');
tmpl_array.push('<table class="grid table table-condensed">');
tmpl_array.push(' <thead>');
tmpl_array.push(' <th>Name</th>');
// tmpl_array.push(' <th style="width:22%;">description</th>');
tmpl_array.push(' <th>Members</th> ');
tmpl_array.push(' <th>Repositories</th>');
tmpl_array.push(' </thead>');
tmpl_array.push(' <tbody id="group_list_body">');
// group item views will attach here
tmpl_array.push(' </tbody>');
tmpl_array.push('</table>');
tmpl_array.push('<% }%>');
tmpl_array.push('</div>');
return _.template(tmpl_array.join(''));
},
});
return {
GroupListView: GroupListView
};
});
@@ -1,22 +0,0 @@
define([], function() {
// ============================================================================
// TS GROUP RELATED MODELS
var Group = Backbone.Model.extend({
urlRoot: Galaxy.root + 'api/groups',
});
var Groups = Backbone.Collection.extend({
url: Galaxy.root + 'api/groups',
model: Group,
});
return {
Group: Group,
Groups: Groups
};
});
@@ -1,51 +0,0 @@
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
// === MAIN TOOLSHED GROUP MODULE ====
// MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
define([
'toolshed/scripts/mvc/groups/group-list-view',
'toolshed/scripts/mvc/groups/group-detail-view',
'toolshed/scripts/mvc/groups/group-model'
],
function(
mod_group_list,
mod_group_detail,
mod_group_model
) {
// ============================================================================
// ROUTER
var ToolshedRouter = Backbone.Router.extend({
routes: {
"" : "groups",
":group_id" : "group_page"
}
});
var ToolshedGroups = Backbone.View.extend({
groupListView: null,
groupDetailView: null,
collection: null,
initialize : function(){
window.globalTS.groups = this;
this.ts_router = new ToolshedRouter();
this.ts_router.on( 'route:groups', function() {
window.globalTS.groups.groupListView = new mod_group_list.GroupListView();
});
this.ts_router.on( 'route:group_page', function( group_id ) {
window.globalTS.groups.groupDetailView = new mod_group_detail.GroupDetailView( { group_id: group_id } );
});
Backbone.history.start({pushState: false});
}
});
return {
ToolshedGroups: ToolshedGroups
};
});
+6 -9
View File
@@ -25,7 +25,7 @@ var commonLibs = [
"libs/farbtastic",
"libs/bootstrap",
"libs/bootstrap-tour",
"libs/vue",
"vue",
// mvc
"libs/underscore",
"libs/backbone",
@@ -78,10 +78,10 @@ let buildconfig = {
}
]
},
{
{
test: /\.vue$/,
loader: 'vue-loader',
}
loader: "vue-loader"
}
]
},
node: {
@@ -99,9 +99,6 @@ let buildconfig = {
name: "libs",
filename: "libs.bundled.js"
}),
new webpack.SourceMapDevToolPlugin({
filename: '[name].js.map',
}),
// this plugin allows using the following keys/globals in scripts (w/o req'ing them first)
// and webpack will automagically require them in the bundle for you
new webpack.ProvidePlugin({
@@ -115,8 +112,8 @@ let buildconfig = {
]
};
if (process.env.GXY_BUILD_SOURCEMAPS){
buildconfig.devtool = 'source-map';
if (process.env.GXY_BUILD_SOURCEMAPS || process.env.NODE_ENV == "development") {
buildconfig.devtool = "source-map";
}
module.exports = buildconfig;
+23 -19
View File
@@ -887,9 +887,9 @@ babel-plugin-transform-strict-mode@^6.24.1:
babel-runtime "^6.22.0"
babel-types "^6.24.1"
babel-preset-env@^1.3.2:
version "1.6.0"
resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.6.0.tgz#2de1c782a780a0a5d605d199c957596da43c44e4"
babel-preset-env@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.6.1.tgz#a18b564cc9b9afdf4aae57ae3c1b0d99188e6f48"
dependencies:
babel-plugin-check-es2015-constants "^6.22.0"
babel-plugin-syntax-trailing-function-commas "^6.22.0"
@@ -1659,9 +1659,9 @@ concat-stream@~1.4.6:
readable-stream "~1.1.9"
typedarray "~0.0.5"
concurrently@^3.4.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-3.5.0.tgz#8cf1b7707a6916a78a4ff5b77bb04dec54b379b2"
concurrently@^3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/concurrently/-/concurrently-3.5.1.tgz#ee8b60018bbe86b02df13e5249453c6ececd2521"
dependencies:
chalk "0.5.1"
commander "2.6.0"
@@ -2616,9 +2616,9 @@ expand-tilde@^2.0.2:
dependencies:
homedir-polyfill "^1.0.1"
expose-loader@^0.7.3:
version "0.7.3"
resolved "https://registry.yarnpkg.com/expose-loader/-/expose-loader-0.7.3.tgz#35fbd3659789e4faa81f59de8b7e9fc39e466d51"
expose-loader@^0.7.4:
version "0.7.4"
resolved "https://registry.yarnpkg.com/expose-loader/-/expose-loader-0.7.4.tgz#9bcdd3878b5da9107930b55a03f65afe90b3314a"
extend@^3.0.0, extend@~3.0.0, extend@~3.0.1:
version "3.0.1"
@@ -5894,7 +5894,11 @@ preserve@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
prettier@^1.7.0, prettier@^1.7.4:
prettier@^1.10.1:
version "1.10.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.10.1.tgz#01423fea6957ea23618d37d339ef0e7f7c967fc6"
prettier@^1.7.0:
version "1.8.2"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.8.2.tgz#bff83e7fd573933c607875e5ba3abbdffb96aeb8"
@@ -7495,9 +7499,9 @@ vue-style-loader@^3.0.0:
hash-sum "^1.0.2"
loader-utils "^1.0.2"
vue-template-compiler@^2.5.3:
version "2.5.3"
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.3.tgz#ab631b0694e211a6aaf0d800102b37836aae36a4"
vue-template-compiler@^2.5.9:
version "2.5.9"
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.9.tgz#7fabc73c8d3d12d32340cd86c5fc33e00e86d686"
dependencies:
de-indent "^1.0.2"
he "^1.1.0"
@@ -7506,9 +7510,9 @@ vue-template-es2015-compiler@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18"
vue@^2.5.3:
version "2.5.3"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.3.tgz#e1a3b1f49b6e93e574ce040b95cbc873912fecc1"
vue@^2.5.9:
version "2.5.9"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.9.tgz#b2380cd040915dca69881dafd121d760952e65f7"
walk-sync@0.3.1:
version "0.3.1"
@@ -7577,9 +7581,9 @@ webpack@^1.14.0:
watchpack "^0.2.1"
webpack-core "~0.6.9"
webpack@^3.6.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.7.0.tgz#9081d0b1ba4950e34ddc54195721eba4bf205e83"
webpack@^3.10.0:
version "3.10.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.10.0.tgz#5291b875078cf2abf42bdd23afe3f8f96c17d725"
dependencies:
acorn "^5.0.0"
acorn-dynamic-import "^2.0.0"
+15
View File
@@ -505,6 +505,7 @@
<datatype extension="augustus" type="galaxy.datatypes.annotation:Augustus" display_in_upload="true" />
<datatype extension="icm" type="galaxy.datatypes.binary:ICM" display_in_upload="true"/>
<!-- MSA Datatypes -->
<datatype extension="cm" type="galaxy.datatypes.msa:InfernalCM" display_in_upload="False" />
<datatype extension="hmm2" type="galaxy.datatypes.msa:Hmmer2" display_in_upload="true" />
<datatype extension="hmm3" type="galaxy.datatypes.msa:Hmmer3" display_in_upload="true" />
<datatype extension="stockholm" type="galaxy.datatypes.msa:Stockholm_1_0" display_in_upload="true" />
@@ -625,6 +626,9 @@
<datatype extension="mothur.sff.flow" type="galaxy.datatypes.mothur:SffFlow" display_in_upload="true"/>
<datatype extension="mothur.count_table" type="galaxy.datatypes.mothur:CountTable" display_in_upload="true"/>
<datatype extension="neostore" type="galaxy.datatypes.neo4j:Neo4jDB" mimetype="text/html" display_in_upload="false"/>
<datatype extension="neostore.zip" type="galaxy.datatypes.neo4j:Neo4jDBzip" display_in_upload="true">
<converter file="neostorezip_to_neostore_converter.xml" target_datatype="neostore"/>
</datatype>
<datatype extension="trackhub" type="galaxy.datatypes.tracks:UCSCTrackHub" display_in_upload="true">
<display file="ucsc/trackhub.xml" />
</datatype>
@@ -636,18 +640,27 @@
<datatype extension="maskinfo-asn1-binary" type="galaxy.datatypes.binary:GenericAsn1Binary" mimetype="application/octet-stream" subclass="true" display_in_upload="true" />
<datatype extension="pssm-asn1" type="galaxy.datatypes.data:GenericAsn1" mimetype="text/plain" subclass="true" display_in_upload="true" />
<!-- PlantTribes datatypes -->
<!--
The commented entries in this section are required by versions 1.0.0, 1.0.1 and 1.0.2 of the
PlantTribes tools in the MTS Phylogenetics category, and are not required by version 1.0.3 of
later. These datatypes will be removed in a future Galaxy release.
-->
<!--
<datatype extension="ptalign" type="galaxy.datatypes.plant_tribes:PlantTribesMultipleSequenceAlignment" />
<datatype extension="ptalignca" type="galaxy.datatypes.plant_tribes:PlantTribesMultipleSequenceAlignmentCodonAlignment" />
<datatype extension="ptaligntrimmed" type="galaxy.datatypes.plant_tribes:PlantTribesMultipleSequenceAlignmentTrimmed" />
<datatype extension="ptaligntrimmedca" type="galaxy.datatypes.plant_tribes:PlantTribesMultipleSequenceAlignmentTrimmedCodonAlignment" />
<datatype extension="ptalignfiltered" type="galaxy.datatypes.plant_tribes:PlantTribesMultipleSequenceAlignmentFiltered" />
<datatype extension="ptalignfilteredca" type="galaxy.datatypes.plant_tribes:PlantTribesMultipleSequenceAlignmentFilteredCodonAlignment" />
-->
<datatype extension="ptkscmp" type="galaxy.datatypes.plant_tribes:PlantTribesKsComponents" display_in_upload="true"/>
<!--
<datatype extension="ptortho" type="galaxy.datatypes.plant_tribes:PlantTribesOrtho" />
<datatype extension="ptorthocs" type="galaxy.datatypes.plant_tribes:PlantTribesOrthoCodingSequence" />
<datatype extension="ptphylip" type="galaxy.datatypes.plant_tribes:PlantTribesPhylip" />
<datatype extension="pttgf" type="galaxy.datatypes.plant_tribes:PlantTribesTargetedGeneFamilies" />
<datatype extension="pttree" type="galaxy.datatypes.plant_tribes:PlantTribesPhylogeneticTree" />
-->
<datatype extension="smat" type="galaxy.datatypes.plant_tribes:Smat" display_in_upload="true" />
<!-- Start Haplotype / LOD Datatypes -->
<datatype extension="alohomora_gts" type="galaxy.datatypes.genetics:GenotypeMatrix" />
@@ -789,6 +802,7 @@
<sniffer type="galaxy.datatypes.msa:Hmmer3" />
<sniffer type="galaxy.datatypes.msa:Stockholm_1_0" />
<sniffer type="galaxy.datatypes.msa:MauveXmfa" />
<sniffer type="galaxy.datatypes.msa:InfernalCM"/>
<sniffer type="galaxy.datatypes.annotation:SnapHmm" />
<sniffer type="galaxy.datatypes.binary:Cel" />
<sniffer type="galaxy.datatypes.binary:RData" />
@@ -820,5 +834,6 @@
<sniffer type="galaxy.datatypes.binary:OxliStopTags"/>
<sniffer type="galaxy.datatypes.binary:OxliSubset"/>
<sniffer type="galaxy.datatypes.binary:OxliGraphLabels"/>
<sniffer type="galaxy.datatypes.neo4j:Neo4jDBzip"/>
</sniffers>
</datatypes>
+12
View File
@@ -615,6 +615,13 @@ nglims_config_file = tool-data/nglims.yaml
# The URL linked by the "Support" link in the "Help" menu.
#support_url = https://galaxyproject.org/support
# The following options enable integration with a custom Biostar instance.
#biostar_url = None
#biostar_key_name = None
#biostar_key = None
#biostar_enable_bug_reports = True
#biostar_never_authenticate = False
# The URL linked by the "How to Cite Galaxy" link in the "Help" menu.
#citation_url = https://galaxyproject.org/citing-galaxy
@@ -920,6 +927,11 @@ use_interactive = True
# create symlinks to them).
#user_library_import_symlink_whitelist = None
# In conjunction or alternatively, Galaxy can restrict user library imports to
# those files that the user can read (by checking basic unix permissions).
# For this to work, the username has to match the username on the filesystem.
#user_library_import_check_permissions = False
# Allow admins to paste filesystem paths during upload. For libraries this
# adds an option to the admin library upload tool allowing admins to paste
# filesystem paths to files and directories in a box, and these paths will be
+25
View File
@@ -248,6 +248,31 @@
a requirement of the other.
-->
<!-- <param id="requests_cpu">500m</param> -->
<!-- <param id="requests_memory">500Mi</param> -->
<!-- <param id="limits_cpu">2</param> -->
<!-- <param id="limits_memory">2Gi</param> -->
<!-- Kubernetes resource Requests and Limits
Parameters above (requests_* and limits_*) set default minimal (requests) and
maximal (limits) CPU and memory resources to be allocated to all containers in
Kubernetes jobs by default.
Limits and requests for CPU resources are measured in cpu units. Fractional requests are allowed. A
Container with requests_cpu of 0.5 is guaranteed half as much CPU as one that asks for 1 CPU. The
expression 0.1 is equivalent to the expression 100m, which can be read as “one hundred millicpu”. Some
people say “one hundred millicores”, and this is understood to mean the same thing. A request with a
decimal point, like 0.1, is converted to 100m by the API, and precision finer than 1m is not allowed.
For this reason, the form 100m might be preferred.
You can express memory as a plain integer or as a fixed-point integer using one of these suffixes: E,
P, T, G, M, K. You can also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki.
For instance, "1Gi" stands for 1 Gigabyte, and "500Mi" stands for 500 megabytes. Using other formats
will make the jobs fail as Kubernetes won't recognize the assignment.
For more details on the Kubernetes resources management, see:
https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/
-->
</plugin>
<plugin id="godocker" type="runner" load="galaxy.jobs.runners.godocker:GodockerJobRunner">
<!-- Go-Docker is a batch computing/cluster management tool using Docker
+10
View File
@@ -52,6 +52,16 @@
</object_store>
-->
<!-- Sample Cloud Object Store
<object_store type="cloud">
<auth access_key="..." secret_key="..." />
<bucket name="..." use_reduced_redundancy="False" />
<cache path="database/object_store_cache" size="1000" />
<extra_dir type="job_work" path="database/job_working_directory_s3"/>
<extra_dir type="temp" path="database/tmp_s3"/>
</object_store>
-->
</backends>
</object_store>
-1
View File
@@ -1,7 +1,6 @@
<?xml version="1.0"?>
<openid>
<provider file="genomespace.xml" />
<provider file="yahoo.xml" />
<provider file="aol.xml" />
<provider file="launchpad.xml" />
</openid>
@@ -6,6 +6,6 @@
# appropriate files.
---
-
image: bgruening/docker-hicbrowser
image: quay.io/bgruening/hicbrowser
description: |
A simple web browser to visualize Hi-C and other genomic tracks
@@ -38,6 +38,8 @@
error : function(){},
assert : function(){}
};
window.jQuery = window.jquery = window.$;
define( 'jquery', [], function(){ return window.$; })
require.config({
baseUrl: Galaxy.root + "static/scripts/",
paths: {
@@ -64,4 +66,4 @@
});
</script>
</body>
</html>
</html>
@@ -174,7 +174,7 @@ class TourGenerator(object):
elif input.type == 'select':
params = []
if name in test_inputs:
for option in self._tool.inputs[name].static_options:
for option in input.static_options:
for test_option in self._test.inputs[name]:
if test_option == option[1]:
params.append(option[0])
@@ -39,3 +39,13 @@ preferences:
label: Maximum number of search results
type: text
required: False
use_cached_job:
description: Do you want to be able to re-use previously run jobs ?
inputs:
- name: use_cached_job_checkbox
label: Do you want to be able to re-use equivalent jobs ?
type: boolean
checked: false
value: false
help: If you select yes, you will be able to select for each tool and workflow run if you would like to use this feature.
+1 -1
View File
@@ -59,7 +59,7 @@ galaxy_status() {
if [[ $(grep '\[server:' $RUN_IN/config/galaxy.ini|awk -F'(:)|(])' '{ print $2 }') == 'main' ]]
then
echo -n "$SERVICE_NAME status: "
status -p $RUN_IN/paster.pid galaxy
status -p $RUN_IN/galaxy.pid galaxy
else
for proc in $(grep '\[server:' $RUN_IN/config/galaxy.ini|awk -F'(:)|(])' '{ print $2 }')
do
+9 -4
View File
@@ -13,10 +13,15 @@ import os
import socket
import sys
import time
import urllib2
import warnings
from user import home
from six.moves.urllib.request import (
build_opener,
HTTPCookieProcessor,
Request
)
with warnings.catch_warnings():
warnings.simplefilter('ignore')
import twill.commands as tc
@@ -85,7 +90,7 @@ class Browser:
dprint("no cookie jar at above path, creating")
tc.save_cookies(self.cookie_jar)
tc.load_cookies(self.cookie_jar)
self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(tc.get_browser().cj))
self.opener = build_opener(HTTPCookieProcessor(tc.get_browser().cj))
def get(self, path):
tc.go("%s%s" % (self.server, path))
@@ -94,9 +99,9 @@ class Browser:
def req(self, path, data=None, method=None):
url = self.server + path
if data:
req = urllib2.Request(url, headers={'Content-Type': 'application/json'}, data=json.dumps(data))
req = Request(url, headers={'Content-Type': 'application/json'}, data=json.dumps(data))
else:
req = urllib2.Request(url, headers={'Content-Type': 'application/json'})
req = Request(url, headers={'Content-Type': 'application/json'})
if method:
req.get_method = lambda: method
res = self.opener.open(req)
+2 -2
View File
@@ -21,7 +21,7 @@ Many of the following instructions are best practices for any production applica
* Create a **NON-ROOT** user called galaxy. Running as an existing user will cause problems down the line when you want to grant or restrict access to data.
* Start with a fresh checkout of Galaxy, don't try to convert one previously used for development. Download and install it in the galaxy user home directory.
* Galaxy should be a managed system service (like Apache, mail servers, database servers, *etc.*) run by the galaxy user. Init scripts, OS X launchd definitions and Solaris SMF manifests are provided in the `contrib/` directory of the distribution. You can also use the `--daemon` and `--stop-daemon` arguments to `run.sh` to start and stop by hand, but still run detached. When running as a daemon the server's output log will be written to `paster.log` instead of the terminal, unless instructed otherwise with the `--log-file` argument.
* Galaxy should be a managed system service (like Apache, mail servers, database servers, *etc.*) run by the galaxy user. Init scripts, OS X launchd definitions and Solaris SMF manifests are provided in the `contrib/` directory of the distribution. You can also use the `--daemon` and `--stop-daemon` arguments to `run.sh` to start and stop by hand, but still run detached. When running as a daemon the server's output log will be written to `galaxy.log` instead of the terminal, unless instructed otherwise with the `--log-file` argument.
* Give Galaxy its own database user and database to prevent Galaxy's schema from conflicting with other tables in your database. Also, restrict Galaxy's database user so it only has access to its own database.
* Make sure Galaxy is using a clean Python interpreter. Conflicts in $PYTHONPATH or the interpreter's `site-packages/` directory could cause problems. Galaxy manages its own dependencies for the framework, so you do not need to worry about these. The easiest way to do this is with a [virtualenv](http://pypi.python.org/pypi/virtualenv):
@@ -37,7 +37,7 @@ nate@weyerbacher% sh run.sh
## Basic configuration
The steps to install Galaxy mostly follow those of the regular instructions at [Admin/GetGalaxy](https://getgalaxy.org). The difference is that after performing the groundwork above, you should initialize the configuration file (`cp config/galaxy.ini.sample config/galaxy.ini`) and modify it as outlined below before starting the server. If you make any changes to this configuration file while the server is running, you will have to restart the server for the changes to take effect.
The steps to install Galaxy mostly follow those of the [regular instructions](http://getgalaxy.org). The difference is that after performing the groundwork above, you should initialize the configuration file (`cp config/galaxy.ini.sample config/galaxy.ini`) and modify it as outlined below before starting the server. If you make any changes to this configuration file while the server is running, you will have to restart the server for the changes to take effect.
### Disable the developer settings
+1 -1
View File
@@ -180,7 +180,7 @@ For this to work, the user under which your nginx server runs will need read acc
### External user authentication
- [Apache for External Authentication](https://galaxyproject.org/admin/config/apache-external-user-auth/)
- [Built-in Galaxy External Authentication](../authentication.md)
- [Built-in Galaxy External Authentication](../authentication.html)
### Display Sites
@@ -1,4 +1,6 @@
AGalaxy contains a plugin infrastructure for collecting and displaying metrics for job execution (see the original [pull request](https://bitbucket.org/galaxy/galaxy-central/pull-request/352) for implementation details).
# Job Metrics
Galaxy contains a plugin infrastructure for collecting and displaying metrics for job execution (see the original [pull request](https://bitbucket.org/galaxy/galaxy-central/pull-request/352) for implementation details).
## Configuration
+2 -2
View File
@@ -47,7 +47,7 @@ http {
**Notes:**
Make sure that you either comment out or modify line containing default configuration for enabled sites.
Make sure that you either comment out or modify line containing default configuration for enabled sites (if present, e.g. on Debian/Ubuntu systems).
```
include /etc/nginx/sites-enabled/*;
@@ -304,4 +304,4 @@ server { # This you should already have.
### External User Authentication
- [Nginx for External Authentication](https://galaxyproject.org/admin/config/nginx-external-user-auth/)
- [Built-in Galaxy External Authentication](../authentication.md)
- [Built-in Galaxy External Authentication](../authentication.html)
+15 -14
View File
@@ -4,9 +4,10 @@ Quickstart
Log in as your user, navigate to the API Keys page in the User menu, and
generate a new API key. Make a note of the API key, and then pull up a
terminal. Now we'll use the display.py script in your galaxy/scripts/api
directory for a short example::
directory for a short example, which assumes your local galaxy server is
running on the default port 8080::
% ./display.py my_key http://localhost:4096/api/histories
% ./display.py my_key http://localhost:8080/api/histories
Collection Members
------------------
#1: /api/histories/8c49be448cfe29bc
@@ -20,7 +21,7 @@ The result is a Collection of the histories of the user specified by the API
key (you). To look at the details of a particular history, say #1 above, do
the following::
% ./display.py my_key http://localhost:4096/api/histories/8c49be448cfe29bc
% ./display.py my_key http://localhost:8080/api/histories/8c49be448cfe29bc
Member Information
------------------
state_details: {'ok': 1, 'failed_metadata': 0, 'upload': 0, 'discarded': 0, 'running': 0, 'setting_metadata': 0, 'error': 0, 'new': 0, 'queued': 0, 'empty': 0}
@@ -33,7 +34,7 @@ This gives detailed information about the specific member in question, in this
case the History. To view history contents, do the following::
% ./display.py my_key http://localhost:4096/api/histories/8c49be448cfe29bc/contents
% ./display.py my_key http://localhost:8080/api/histories/8c49be448cfe29bc/contents
Collection Members
------------------
#1: /api/histories/8c49be448cfe29bc/contents/6f91353f3eb0fa4a
@@ -45,7 +46,7 @@ What we have here is another Collection of items containing all of the datasets
in this particular history. Finally, to view details of a particular dataset
in this collection, execute the following::
% ./display.py my_key http://localhost:4096/api/histories/8c49be448cfe29bc/contents/6f91353f3eb0fa4a
% ./display.py my_key http://localhost:8080/api/histories/8c49be448cfe29bc/contents/6f91353f3eb0fa4a
Member Information
------------------
misc_blurb: 1 line
@@ -82,27 +83,27 @@ In Galaxy, create an account that matches the address you put in 'admin_users',
then browse to that user's preferences and generate a new API Key. Copy the
key to your clipboard and then use these scripts::
% ./display.py my_key http://localhost:4096/api/libraries
% ./display.py my_key http://localhost:8080/api/libraries
Collection Members
------------------
0 elements in collection
% ./library_create_library.py my_key http://localhost:4096/api/libraries api_test 'API Test Library'
% ./library_create_library.py my_key http://localhost:8080/api/libraries api_test 'API Test Library'
Response
--------
/api/libraries/f3f73e481f432006
name: api_test
id: f3f73e481f432006
% ./display.py my_key http://localhost:4096/api/libraries
% ./display.py my_key http://localhost:8080/api/libraries
Collection Members
------------------
/api/libraries/f3f73e481f432006
name: api_test
id: f3f73e481f432006
% ./display.py my_key http://localhost:4096/api/libraries/f3f73e481f432006
% ./display.py my_key http://localhost:8080/api/libraries/f3f73e481f432006
Member Information
------------------
synopsis: None
@@ -110,7 +111,7 @@ key to your clipboard and then use these scripts::
description: API Test Library
name: api_test
% ./display.py my_key http://localhost:4096/api/libraries/f3f73e481f432006/contents
% ./display.py my_key http://localhost:8080/api/libraries/f3f73e481f432006/contents
Collection Members
------------------
/api/libraries/f3f73e481f432006/contents/28202595c0d2591f61ddda595d2c3670
@@ -118,14 +119,14 @@ key to your clipboard and then use these scripts::
type: folder
id: 28202595c0d2591f61ddda595d2c3670
% ./library_create_folder.py my_key http://localhost:4096/api/libraries/f3f73e481f432006/contents 28202595c0d2591f61ddda595d2c3670 api_test_folder1 'API Test Folder 1'
% ./library_create_folder.py my_key http://localhost:8080/api/libraries/f3f73e481f432006/contents 28202595c0d2591f61ddda595d2c3670 api_test_folder1 'API Test Folder 1'
Response
--------
/api/libraries/f3f73e481f432006/contents/28202595c0d2591fa4f9089d2303fd89
name: api_test_folder1
id: 28202595c0d2591fa4f9089d2303fd89
% ./library_upload_from_import_dir.py my_key http://localhost:4096/api/libraries/f3f73e481f432006/contents 28202595c0d2591fa4f9089d2303fd89 bed bed hg19
% ./library_upload_from_import_dir.py my_key http://localhost:8080/api/libraries/f3f73e481f432006/contents 28202595c0d2591fa4f9089d2303fd89 bed bed hg19
Response
--------
/api/libraries/f3f73e481f432006/contents/e9ef7fdb2db87d7b
@@ -135,7 +136,7 @@ key to your clipboard and then use these scripts::
name: 3.bed
id: 3b7f6a31f80a5018
% ./display.py my_key http://localhost:4096/api/libraries/f3f73e481f432006/contents
% ./display.py my_key http://localhost:8080/api/libraries/f3f73e481f432006/contents
Collection Members
------------------
/api/libraries/f3f73e481f432006/contents/28202595c0d2591f61ddda595d2c3670
@@ -155,7 +156,7 @@ key to your clipboard and then use these scripts::
type: file
id: 3b7f6a31f80a5018
% ./display.py my_key http://localhost:4096/api/libraries/f3f73e481f432006/contents/e9ef7fdb2db87d7b
% ./display.py my_key http://localhost:8080/api/libraries/f3f73e481f432006/contents/e9ef7fdb2db87d7b
Member Information
------------------
misc_blurb: 68 regions
+87 -30
View File
@@ -8,6 +8,11 @@ import os.path
from markupsafe import escape
from galaxy import util
from galaxy.exceptions import (
AdminRequiredException,
ConfigDoesNotAllowException,
RequestParameterInvalidException,
)
from galaxy.tools.actions import upload_common
from galaxy.tools.parameters import populate_state
from galaxy.util.path import (
@@ -19,6 +24,47 @@ from galaxy.util.path import (
log = logging.getLogger(__name__)
def validate_server_directory_upload(trans, server_dir):
if server_dir in [None, 'None', '']:
raise RequestParameterInvalidException("Invalid or unspecified server_dir parameter")
if trans.user_is_admin():
import_dir = trans.app.config.library_import_dir
import_dir_desc = 'library_import_dir'
if not import_dir:
raise ConfigDoesNotAllowException('"library_import_dir" is not set in the Galaxy configuration')
else:
import_dir = trans.app.config.user_library_import_dir
if not import_dir:
raise ConfigDoesNotAllowException('"user_library_import_dir" is not set in the Galaxy configuration')
if server_dir != trans.user.email:
import_dir = os.path.join(import_dir, trans.user.email)
import_dir_desc = 'user_library_import_dir'
full_dir = os.path.join(import_dir, server_dir)
unsafe = None
if safe_relpath(server_dir):
username = trans.user.username if trans.app.config.user_library_import_check_permissions else None
if import_dir_desc == 'user_library_import_dir' and safe_contains(import_dir, full_dir, whitelist=trans.app.config.user_library_import_symlink_whitelist, username=username):
for unsafe in unsafe_walk(full_dir, whitelist=[import_dir] + trans.app.config.user_library_import_symlink_whitelist):
log.error('User attempted to import a path that resolves to a path outside of their import dir: %s -> %s', unsafe, os.path.realpath(unsafe))
else:
log.error('User attempted to import a directory path that resolves to a path outside of their import dir: %s -> %s', server_dir, os.path.realpath(full_dir))
unsafe = True
if unsafe:
raise RequestParameterInvalidException("Invalid server_dir specified")
return full_dir, import_dir_desc
def validate_path_upload(trans):
if not trans.app.config.allow_library_path_paste:
raise ConfigDoesNotAllowException('"allow_path_paste" is not set to True in the Galaxy configuration file')
if not trans.user_is_admin():
raise AdminRequiredException('Uploading files via filesystem paths can only be performed by administrators')
class LibraryActions(object):
"""
Mixin for controllers that provide library functionality.
@@ -42,37 +88,11 @@ class LibraryActions(object):
upload_option = kwd.get('upload_option', 'upload_file')
response_code = 200
if upload_option == 'upload_directory':
if server_dir in [None, 'None', '']:
response_code = 400
if trans.user_is_admin():
import_dir = trans.app.config.library_import_dir
import_dir_desc = 'library_import_dir'
else:
import_dir = trans.app.config.user_library_import_dir
if server_dir != trans.user.email:
import_dir = os.path.join(import_dir, trans.user.email)
import_dir_desc = 'user_library_import_dir'
full_dir = os.path.join(import_dir, server_dir)
unsafe = None
if safe_relpath(server_dir):
if import_dir_desc == 'user_library_import_dir' and safe_contains(import_dir, full_dir, whitelist=trans.app.config.user_library_import_symlink_whitelist):
for unsafe in unsafe_walk(full_dir, whitelist=[import_dir] + trans.app.config.user_library_import_symlink_whitelist):
log.error('User attempted to import a path that resolves to a path outside of their import dir: %s -> %s', unsafe, os.path.realpath(unsafe))
else:
log.error('User attempted to import a directory path that resolves to a path outside of their import dir: %s -> %s', server_dir, os.path.realpath(full_dir))
unsafe = True
if unsafe:
response_code = 403
message = 'Invalid server_dir'
if import_dir:
message = 'Select a directory'
else:
response_code = 403
message = '"%s" is not defined in the Galaxy configuration file' % import_dir_desc
full_dir, import_dir_desc = validate_server_directory_upload(trans, server_dir)
message = 'Select a directory'
elif upload_option == 'upload_paths':
if not trans.app.config.allow_library_path_paste:
response_code = 403
message = '"allow_library_path_paste" is not defined in the Galaxy configuration file'
# Library API already checked this - following check isn't actually needed.
validate_path_upload(trans)
# Some error handling should be added to this method.
try:
# FIXME: instead of passing params here ( which have been processed by util.Params(), the original kwd
@@ -118,6 +138,43 @@ class LibraryActions(object):
uploaded_datasets.append(self._make_library_uploaded_dataset(trans, params, name, file, 'server_dir', library_bunch))
return uploaded_datasets, 200, None
def _get_server_dir_files(self, params, full_dir, import_dir_desc):
files = []
try:
for entry in os.listdir(full_dir):
# Only import regular files
path = os.path.join(full_dir, entry)
link_data_only = params.get('link_data_only', 'copy_files')
if os.path.islink(full_dir) and link_data_only == 'link_to_files':
# If we're linking instead of copying and the
# sub-"directory" in the import dir is actually a symlink,
# dereference the symlink, but not any of its contents.
link_path = os.readlink(full_dir)
if os.path.isabs(link_path):
path = os.path.join(link_path, entry)
else:
path = os.path.abspath(os.path.join(link_path, entry))
elif os.path.islink(path) and os.path.isfile(path) and link_data_only == 'link_to_files':
# If we're linking instead of copying and the "file" in the
# sub-directory of the import dir is actually a symlink,
# dereference the symlink (one dereference only, Vasili).
link_path = os.readlink(path)
if os.path.isabs(link_path):
path = link_path
else:
path = os.path.abspath(os.path.join(os.path.dirname(path), link_path))
if os.path.isfile(path):
files.append(path)
except Exception as e:
message = "Unable to get file list for configured %s, error: %s" % (import_dir_desc, str(e))
response_code = 500
return None, response_code, message
if not files:
message = "The directory '%s' contains no valid files" % full_dir
response_code = 400
return None, response_code, message
return files, None, None
def _get_path_paste_uploaded_datasets(self, trans, params, library_bunch, response_code, message):
preserve_dirs = util.string_as_bool(params.get('preserve_dirs', False))
uploaded_datasets = []
+1 -3
View File
@@ -371,9 +371,6 @@ class Configuration(object):
# Beta containers interface used by GIEs
self.enable_beta_containers_interface = string_as_bool(kwargs.get('enable_beta_containers_interface', 'False'))
# Deprecated API for sample tracking
self.enable_legacy_sample_tracking_api = string_as_bool(kwargs.get('enable_legacy_sample_tracking_api', 'False'))
# Certain modules such as the pause module will automatically cause
# workflows to be scheduled in job handlers the way all workflows will
# be someday - the following two properties can also be used to force this
@@ -429,6 +426,7 @@ class Configuration(object):
self.library_import_dir = kwargs.get('library_import_dir', None)
self.user_library_import_dir = kwargs.get('user_library_import_dir', None)
self.user_library_import_symlink_whitelist = listify(kwargs.get('user_library_import_symlink_whitelist', []), do_strip=True)
self.user_library_import_check_permissions = string_as_bool(kwargs.get('user_library_import_check_permissions', False))
# Searching data libraries
self.ftp_upload_dir = kwargs.get('ftp_upload_dir', None)
self.ftp_upload_dir_identifier = kwargs.get('ftp_upload_dir_identifier', 'email') # attribute on user - email, username, id, etc...
@@ -46,7 +46,7 @@ SWARM_MANAGER_CONF_DEFAULTS = {
'service_wait_count_limit': 0,
'service_wait_time_limit': 5,
'slots_min_limit': 0,
'slots_max_limit': sys.maxint,
'slots_max_limit': sys.maxsize,
'slots_min_spare': 0,
'node_idle_limit': 120,
'limits': [],
@@ -137,7 +137,7 @@ class SwarmManager(object):
state = node_state['state']
if not nodes:
nodes = self._docker_interface.nodes()
node = (filter(lambda x: x.name == name, nodes) + [None])[0]
node = ([x for x in nodes if x.name == name] + [None])[0]
if not node:
if elapsed > self._conf.spawn_wait_time:
log.warning("spawning node '%s' not found in `docker node ls` and spawn_wait_time exceeded! %d seconds have elapsed", name, elapsed)
+29 -21
View File
@@ -267,27 +267,35 @@ class BamNative(Binary):
return zip(file_paths, rel_paths)
def get_chunk(self, trans, dataset, offset=0, ck_size=None):
with pysam.AlignmentFile(dataset.file_name, "rb") as bamfile:
ck_size = 300 # 300 lines
ck_data = ""
header_line_count = 0
if offset == 0:
ck_data = bamfile.text.replace('\t', ' ')
header_line_count = bamfile.text.count('\n')
else:
bamfile.seek(offset)
for line_number, alignment in enumerate(bamfile) :
# return only Header lines if 'header_line_count' exceeds 'ck_size'
# FIXME: Can be problematic if bam has million lines of header
offset = bamfile.tell()
if (line_number + header_line_count) > ck_size:
break
else:
bamline = alignment.tostring(bamfile)
# Galaxy display each tag as separate column because 'tostring()' funcition put tabs in between each tag of tags column.
# Below code will remove spaces between each tag.
bamline_modified = ('\t').join(bamline.split()[:11] + [(' ').join(bamline.split()[11:])])
ck_data = "%s\n%s" % (ck_data, bamline_modified)
if not offset == -1:
try:
with pysam.AlignmentFile(dataset.file_name, "rb") as bamfile:
ck_size = 300 # 300 lines
ck_data = ""
header_line_count = 0
if offset == 0:
ck_data = bamfile.text.replace('\t', ' ')
header_line_count = bamfile.text.count('\n')
else:
bamfile.seek(offset)
for line_number, alignment in enumerate(bamfile) :
# return only Header lines if 'header_line_count' exceeds 'ck_size'
# FIXME: Can be problematic if bam has million lines of header
offset = bamfile.tell()
if (line_number + header_line_count) > ck_size:
break
else:
bamline = alignment.tostring(bamfile)
# Galaxy display each tag as separate column because 'tostring()' funcition put tabs in between each tag of tags column.
# Below code will remove spaces between each tag.
bamline_modified = ('\t').join(bamline.split()[:11] + [(' ').join(bamline.split()[11:])])
ck_data = "%s\n%s" % (ck_data, bamline_modified)
except Exception as e:
offset = -1
ck_data = "Could not display BAM file, error was:\n%s" % e.message
else:
ck_data = ''
offset = -1
return dumps({'ck_data': util.unicodify(ck_data),
'offset': offset})

Some files were not shown because too many files have changed in this diff Show More